From fb55624f5f74108a629d62ceacfcf58b6f7dbaec Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 18 Apr 2024 14:25:10 +0100 Subject: [PATCH] add more null handling --- src/SharpCompress/Archives/AbstractArchive.cs | 5 +- .../Archives/GZip/GZipArchiveEntry.cs | 2 +- .../Archives/GZip/GZipWritableArchiveEntry.cs | 4 +- src/SharpCompress/Archives/Rar/RarArchive.cs | 11 +++-- .../Archives/SevenZip/SevenZipArchive.cs | 46 ++++++++----------- src/SharpCompress/Archives/Tar/TarArchive.cs | 7 ++- .../Archives/Tar/TarArchiveEntry.cs | 2 +- .../Archives/Tar/TarWritableArchiveEntry.cs | 2 - src/SharpCompress/Archives/Zip/ZipArchive.cs | 24 +++++----- src/SharpCompress/Common/GZip/GZipEntry.cs | 14 +++--- src/SharpCompress/Common/GZip/GZipVolume.cs | 2 +- src/SharpCompress/Common/Rar/RarEntry.cs | 2 +- .../Common/SevenZip/SevenZipEntry.cs | 2 +- .../Common/Tar/Headers/TarHeader.cs | 18 ++++---- src/SharpCompress/Common/Tar/TarEntry.cs | 26 +++++------ src/SharpCompress/Common/Tar/TarFilePart.cs | 8 ++-- src/SharpCompress/Common/Volume.cs | 6 +-- .../Common/Zip/Headers/ZipFileEntry.cs | 16 +++---- .../Common/Zip/StreamingZipFilePart.cs | 2 +- .../Common/Zip/StreamingZipHeaderFactory.cs | 4 ++ src/SharpCompress/Common/Zip/ZipEntry.cs | 29 ++++++------ src/SharpCompress/Common/Zip/ZipFilePart.cs | 2 +- src/SharpCompress/Utility.cs | 17 +++++++ src/SharpCompress/Writers/AbstractWriter.cs | 20 ++++---- src/SharpCompress/Writers/GZip/GZipWriter.cs | 16 +++---- src/SharpCompress/Writers/Tar/TarWriter.cs | 2 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 6 +-- .../SevenZip/SevenZipArchiveTests.cs | 2 +- .../SharpCompress.Test/Tar/TarArchiveTests.cs | 2 +- .../SharpCompress.Test/Tar/TarReaderTests.cs | 8 ++-- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 10 ++-- 31 files changed, 157 insertions(+), 160 deletions(-) diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index cfac55f2..3975a0d4 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -24,7 +24,7 @@ public abstract class AbstractArchive : IArchive, IArchiveExtra protected ReaderOptions ReaderOptions { get; } private bool disposed; - protected SourceStream SrcStream; + protected SourceStream? SrcStream; internal AbstractArchive(ArchiveType type, SourceStream srcStream) { @@ -35,15 +35,14 @@ public abstract class AbstractArchive : IArchive, IArchiveExtra lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); } -#nullable disable internal AbstractArchive(ArchiveType type) { Type = type; + ReaderOptions = new(); lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); } -#nullable enable public ArchiveType Type { get; } diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs index be872e80..459d042d 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Archives.GZip; public class GZipArchiveEntry : GZipEntry, IArchiveEntry { - internal GZipArchiveEntry(GZipArchive archive, GZipFilePart part) + internal GZipArchiveEntry(GZipArchive archive, GZipFilePart? part) : base(part) => Archive = archive; public virtual Stream OpenEntryStream() diff --git a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs index 27dfc2bf..90f6f16f 100644 --- a/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipWritableArchiveEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -32,7 +30,7 @@ internal sealed class GZipWritableArchiveEntry : GZipArchiveEntry, IWritableArch public override long Crc => 0; - public override string Key { get; } + public override string? Key { get; } public override long CompressedSize => 0; diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index b6472407..b6a7c02b 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -31,14 +31,15 @@ public class RarArchive : AbstractArchive protected override IEnumerable LoadVolumes(SourceStream srcStream) { - SrcStream.LoadAllParts(); //request all streams - var streams = SrcStream.Streams.ToArray(); + var sourceStream = SrcStream.NotNull("SourceStream is null"); + sourceStream .LoadAllParts(); //request all streams + var streams = sourceStream.Streams.ToArray(); var idx = 0; if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split { - SrcStream.IsVolumes = true; + sourceStream.IsVolumes = true; streams[1].Position = 0; - SrcStream.Position = 0; + sourceStream.Position = 0; return srcStream.Streams.Select(a => new StreamRarArchiveVolume( a, @@ -48,7 +49,7 @@ public class RarArchive : AbstractArchive } else //split mode or single file { - return new StreamRarArchiveVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + return new StreamRarArchiveVolume(sourceStream, ReaderOptions, idx++).AsEnumerable(); } } diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 7577b43d..b41f4cf6 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -14,14 +12,14 @@ namespace SharpCompress.Archives.SevenZip; public class SevenZipArchive : AbstractArchive { - private ArchiveDatabase database; + private ArchiveDatabase? database; /// /// Constructor expects a filepath to an existing file. /// /// /// - public static SevenZipArchive Open(string filePath, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(string filePath, ReaderOptions? readerOptions = null) { filePath.CheckNotNullOrEmpty("filePath"); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); @@ -32,7 +30,7 @@ public class SevenZipArchive : AbstractArchive /// /// - public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { fileInfo.CheckNotNull("fileInfo"); return new SevenZipArchive( @@ -51,7 +49,7 @@ public class SevenZipArchive : AbstractArchive public static SevenZipArchive Open( IEnumerable fileInfos, - ReaderOptions readerOptions = null + ReaderOptions? readerOptions = null ) { fileInfos.CheckNotNull(nameof(fileInfos)); @@ -72,7 +70,7 @@ public class SevenZipArchive : AbstractArchive public static SevenZipArchive Open( IEnumerable streams, - ReaderOptions readerOptions = null + ReaderOptions? readerOptions = null ) { streams.CheckNotNull(nameof(streams)); @@ -91,7 +89,7 @@ public class SevenZipArchive : AbstractArchive /// /// - public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null) + public static SevenZipArchive Open(Stream stream, ReaderOptions? readerOptions = null) { stream.CheckNotNull("stream"); return new SevenZipArchive( @@ -103,13 +101,12 @@ public class SevenZipArchive : AbstractArchive /// - /// internal SevenZipArchive(SourceStream srcStream) : base(ArchiveType.SevenZip, srcStream) { } protected override IEnumerable LoadVolumes(SourceStream srcStream) { - SrcStream.LoadAllParts(); //request all streams + SrcStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams var idx = 0; return new SevenZipVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported } @@ -133,6 +130,10 @@ public class SevenZipArchive : AbstractArchive volumes ) { + if (database is null) + { + return Enumerable.Empty(); + } var stream = volumes.Single().Stream; LoadFactory(stream); var entries = new SevenZipArchiveEntry[database._files.Count]; @@ -196,21 +197,14 @@ public class SevenZipArchive : AbstractArchive Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; - public override long TotalSize - { - get - { - var i = Entries.Count; - return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize); - } - } + public override long TotalSize => database?._packSizes.Aggregate(0L, (total, packSize) => total + packSize) ?? 0; private sealed class SevenZipReader : AbstractReader { private readonly SevenZipArchive archive; - private CFolder currentFolder; - private Stream currentStream; - private CFileItem currentItem; + private CFolder? currentFolder; + private Stream? currentStream; + private CFileItem? currentItem; internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) : base(readerOptions, ArchiveType.SevenZip) => this.archive = archive; @@ -236,7 +230,7 @@ public class SevenZipArchive : AbstractArchive - CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); + CreateEntryStream(new ReadOnlySubStream(currentStream.NotNull("currentStream is not null"), currentItem?.Size ?? 0)); } private class PasswordProvider : IPasswordProvider { - private readonly string _password; + private readonly string? _password; - public PasswordProvider(string password) => _password = password; + public PasswordProvider(string? password) => _password = password; - public string CryptoGetTextPassword() => _password; + public string CryptoGetTextPassword() => _password.NotNull(); } } diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 70ac2413..d1f8cc1d 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -114,7 +114,7 @@ public class TarArchive : AbstractWritableArchive var tarHeader = new TarHeader(new ArchiveEncoding()); var readSucceeded = tarHeader.Read(new BinaryReader(stream)); var isEmptyArchive = - tarHeader.Name.Length == 0 + tarHeader.Name?.Length == 0 && tarHeader.Size == 0 && Enum.IsDefined(typeof(EntryType), tarHeader.EntryType); return readSucceeded || isEmptyArchive; @@ -125,7 +125,7 @@ public class TarArchive : AbstractWritableArchive protected override IEnumerable LoadVolumes(SourceStream srcStream) { - SrcStream.LoadAllParts(); //request all streams + SrcStream.NotNull("SourceStream is null").LoadAllParts(); //request all streams var idx = 0; return new TarVolume(srcStream, ReaderOptions, idx++).AsEnumerable(); //simple single volume or split, multivolume not supported } @@ -134,7 +134,6 @@ public class TarArchive : AbstractWritableArchive /// Constructor with a SourceStream able to handle FileInfo and Streams. /// /// - /// internal TarArchive(SourceStream srcStream) : base(ArchiveType.Tar, srcStream) { } @@ -225,7 +224,7 @@ public class TarArchive : AbstractWritableArchive foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { using var entryStream = entry.OpenEntryStream(); - writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); + writer.Write(entry.Key.NotNull("Entry Key is null"), entryStream, entry.LastModifiedTime, entry.Size); } } diff --git a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs index 2da84d71..d04c4ef8 100644 --- a/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarArchiveEntry.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Archives.Tar; public class TarArchiveEntry : TarEntry, IArchiveEntry { - internal TarArchiveEntry(TarArchive archive, TarFilePart part, CompressionType compressionType) + internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType) : base(part, compressionType) => Archive = archive; public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream(); diff --git a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs index 0bbea709..32be7d9b 100644 --- a/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs +++ b/src/SharpCompress/Archives/Tar/TarWritableArchiveEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 87e9ac25..2d243d42 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -16,10 +16,7 @@ namespace SharpCompress.Archives.Zip; public class ZipArchive : AbstractWritableArchive { -#nullable disable - private readonly SeekableZipHeaderFactory headerFactory; - -#nullable enable + private readonly SeekableZipHeaderFactory? headerFactory; /// /// Gets or sets the compression level applied to files added to the archive, @@ -191,10 +188,11 @@ public class ZipArchive : AbstractWritableArchive protected override IEnumerable LoadVolumes(SourceStream srcStream) { - SrcStream.LoadAllParts(); //request all streams - SrcStream.Position = 0; + var stream = SrcStream.NotNull("SrcStream is null"); + stream.LoadAllParts(); //request all streams + stream.Position = 0; - var streams = SrcStream.Streams.ToList(); + var streams = stream.Streams.ToList(); var idx = 0; if (streams.Count > 1) //test part 2 - true = multipart not split { @@ -203,7 +201,7 @@ public class ZipArchive : AbstractWritableArchive streams[1].Position -= 4; if (isZip) { - SrcStream.IsVolumes = true; + stream.IsVolumes = true; var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end streams.RemoveAt(0); @@ -215,7 +213,7 @@ public class ZipArchive : AbstractWritableArchive } //split mode or single file - return new ZipVolume(SrcStream, ReaderOptions, idx++).AsEnumerable(); + return new ZipVolume(stream, ReaderOptions, idx++).AsEnumerable(); } internal ZipArchive() @@ -224,7 +222,7 @@ public class ZipArchive : AbstractWritableArchive protected override IEnumerable LoadEntries(IEnumerable volumes) { var vols = volumes.ToArray(); - foreach (var h in headerFactory.ReadSeekableHeader(vols.Last().Stream)) + foreach (var h in headerFactory.NotNull().ReadSeekableHeader(vols.Last().Stream)) { if (h != null) { @@ -254,14 +252,14 @@ public class ZipArchive : AbstractWritableArchive yield return new ZipArchiveEntry( this, - new SeekableZipFilePart(headerFactory, deh, s) + new SeekableZipFilePart(headerFactory.NotNull(), deh, s) ); } break; case ZipHeaderType.DirectoryEnd: { var bytes = ((DirectoryEndHeader)h).Comment ?? Array.Empty(); - volumes.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); + vols.Last().Comment = ReaderOptions.ArchiveEncoding.Decode(bytes); yield break; } } @@ -282,7 +280,7 @@ public class ZipArchive : AbstractWritableArchive foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory)) { using var entryStream = entry.OpenEntryStream(); - writer.Write(entry.Key, entryStream, entry.LastModifiedTime); + writer.Write(entry.Key.NotNull("Entry Key is null"), entryStream, entry.LastModifiedTime); } } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index bd341d24..9a551d07 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -6,23 +6,23 @@ namespace SharpCompress.Common.GZip; public class GZipEntry : Entry { - private readonly GZipFilePart _filePart; + private readonly GZipFilePart? _filePart; - internal GZipEntry(GZipFilePart filePart) => _filePart = filePart; + internal GZipEntry(GZipFilePart? filePart) => _filePart = filePart; public override CompressionType CompressionType => CompressionType.GZip; - public override long Crc => _filePart.Crc ?? 0; + public override long Crc => _filePart?.Crc ?? 0; - public override string? Key => _filePart.FilePartName; + public override string? Key => _filePart?.FilePartName; public override string? LinkTarget => null; public override long CompressedSize => 0; - public override long Size => _filePart.UncompressedSize ?? 0; + public override long Size => _filePart?.UncompressedSize ?? 0; - public override DateTime? LastModifiedTime => _filePart.DateModified; + public override DateTime? LastModifiedTime => _filePart?.DateModified; public override DateTime? CreatedTime => null; @@ -36,7 +36,7 @@ public class GZipEntry : Entry public override bool IsSplitAfter => false; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); internal static IEnumerable GetEntries(Stream stream, OptionsBase options) { diff --git a/src/SharpCompress/Common/GZip/GZipVolume.cs b/src/SharpCompress/Common/GZip/GZipVolume.cs index 0dd9b8d9..9cfdb82d 100644 --- a/src/SharpCompress/Common/GZip/GZipVolume.cs +++ b/src/SharpCompress/Common/GZip/GZipVolume.cs @@ -5,7 +5,7 @@ namespace SharpCompress.Common.GZip; public class GZipVolume : Volume { - public GZipVolume(Stream stream, ReaderOptions options, int index = 0) + public GZipVolume(Stream stream, ReaderOptions? options, int index = 0) : base(stream, options, index) { } public GZipVolume(FileInfo fileInfo, ReaderOptions options) diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index 30de1037..a064c2f8 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -25,7 +25,7 @@ public abstract class RarEntry : Entry /// /// The path of the file internal to the Rar Archive. /// - public override string Key => FileHeader.FileName; + public override string? Key => FileHeader.FileName; public override string? LinkTarget => null; diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index 66170ba8..fbcb3d00 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -13,7 +13,7 @@ public class SevenZipEntry : Entry public override long Crc => FilePart.Header.Crc ?? 0; - public override string Key => FilePart.Header.Name; + public override string? Key => FilePart.Header.Name; public override string? LinkTarget => null; diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index a59b74f6..94fdaf92 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Buffers.Binary; using System.IO; @@ -13,8 +11,8 @@ internal sealed class TarHeader public TarHeader(ArchiveEncoding archiveEncoding) => ArchiveEncoding = archiveEncoding; - internal string Name { get; set; } - internal string LinkName { get; set; } + internal string? Name { get; set; } + internal string? LinkName { get; set; } internal long Mode { get; set; } internal long UserId { get; set; } @@ -22,7 +20,7 @@ internal sealed class TarHeader internal long Size { get; set; } internal DateTime LastModifiedTime { get; set; } internal EntryType EntryType { get; set; } - internal Stream PackedStream { get; set; } + internal Stream? PackedStream { get; set; } internal ArchiveEncoding ArchiveEncoding { get; } internal const int BLOCK_SIZE = 512; @@ -36,7 +34,7 @@ internal sealed class TarHeader WriteOctalBytes(0, buffer, 116, 8); // group ID //ArchiveEncoding.UTF8.GetBytes("magic").CopyTo(buffer, 257); - var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name); + var nameByteCount = ArchiveEncoding.GetEncoding().GetByteCount(Name.NotNull("Name is null")); if (nameByteCount > 100) { // Set mock filename and filetype to indicate the next block is the actual name of the file @@ -46,7 +44,7 @@ internal sealed class TarHeader } else { - WriteStringBytes(ArchiveEncoding.Encode(Name), buffer, 100); + WriteStringBytes(ArchiveEncoding.Encode(Name.NotNull("Name is null")), buffer, 100); WriteOctalBytes(Size, buffer, 124, 12); var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; WriteOctalBytes(time, buffer, 136, 12); @@ -77,7 +75,7 @@ internal sealed class TarHeader // // and then infinite recursion is occured in WriteLongFilenameHeader because truncated.Length is 102. Name = ArchiveEncoding.Decode( - ArchiveEncoding.Encode(Name), + ArchiveEncoding.Encode(Name.NotNull("Name is null")), 0, 100 - ArchiveEncoding.GetEncoding().GetMaxByteCount(1) ); @@ -87,7 +85,7 @@ internal sealed class TarHeader private void WriteLongFilenameHeader(Stream output) { - var nameBytes = ArchiveEncoding.Encode(Name); + var nameBytes = ArchiveEncoding.Encode(Name.NotNull("Name is null")); output.Write(nameBytes, 0, nameBytes.Length); // pad to multiple of BlockSize bytes, and make sure a terminating null is added @@ -323,5 +321,5 @@ internal sealed class TarHeader public long? DataStartPosition { get; set; } - public string Magic { get; set; } + public string? Magic { get; set; } } diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 59743070..ae49e1fc 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -10,9 +8,9 @@ namespace SharpCompress.Common.Tar; public class TarEntry : Entry { - private readonly TarFilePart _filePart; + private readonly TarFilePart? _filePart; - internal TarEntry(TarFilePart filePart, CompressionType type) + internal TarEntry(TarFilePart? filePart, CompressionType type) { _filePart = filePart; CompressionType = type; @@ -22,15 +20,15 @@ public class TarEntry : Entry public override long Crc => 0; - public override string Key => _filePart.Header.Name; + public override string? Key => _filePart?.Header.Name; - public override string LinkTarget => _filePart.Header.LinkName; + public override string? LinkTarget => _filePart?.Header.LinkName; - public override long CompressedSize => _filePart.Header.Size; + public override long CompressedSize => _filePart?.Header.Size ?? 0; - public override long Size => _filePart.Header.Size; + public override long Size => _filePart?.Header.Size ?? 0; - public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime; + public override DateTime? LastModifiedTime => _filePart?.Header.LastModifiedTime; public override DateTime? CreatedTime => null; @@ -40,17 +38,17 @@ public class TarEntry : Entry public override bool IsEncrypted => false; - public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory; + public override bool IsDirectory => _filePart?.Header.EntryType == EntryType.Directory; public override bool IsSplitAfter => false; - public long Mode => _filePart.Header.Mode; + public long Mode => _filePart?.Header.Mode ?? 0; - public long UserID => _filePart.Header.UserId; + public long UserID => _filePart?.Header.UserId ?? 0; - public long GroupId => _filePart.Header.GroupId; + public long GroupId => _filePart?.Header.GroupId ?? 0; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); internal static IEnumerable GetEntries( StreamingMode mode, diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index bfd2fe29..88c525aa 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -5,9 +5,9 @@ namespace SharpCompress.Common.Tar; internal sealed class TarFilePart : FilePart { - private readonly Stream _seekableStream; + private readonly Stream? _seekableStream; - internal TarFilePart(TarHeader header, Stream seekableStream) + internal TarFilePart(TarHeader header, Stream? seekableStream) : base(header.ArchiveEncoding) { _seekableStream = seekableStream; @@ -16,7 +16,7 @@ internal sealed class TarFilePart : FilePart internal TarHeader Header { get; } - internal override string FilePartName => Header.Name; + internal override string? FilePartName => Header?.Name; internal override Stream GetCompressedStream() { @@ -25,7 +25,7 @@ internal sealed class TarFilePart : FilePart _seekableStream.Position = Header.DataStartPosition!.Value; return new TarReadOnlySubStream(_seekableStream, Header.Size); } - return Header.PackedStream; + return Header.PackedStream.NotNull(); } internal override Stream? GetRawStream() => null; diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index a29dab75..7be161ec 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -9,11 +9,11 @@ public abstract class Volume : IVolume { private readonly Stream _actualStream; - internal Volume(Stream stream, ReaderOptions readerOptions, int index = 0) + internal Volume(Stream stream, ReaderOptions? readerOptions, int index = 0) { Index = index; - ReaderOptions = readerOptions; - if (readerOptions.LeaveStreamOpen) + ReaderOptions = readerOptions ?? new ReaderOptions(); + if (ReaderOptions.LeaveStreamOpen) { stream = NonDisposingStream.Create(stream); } diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index edba3ebe..0570bf6d 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -20,21 +18,21 @@ internal abstract class ZipFileEntry : ZipHeader { get { - if (Name.EndsWith('/')) + if (Name?.EndsWith('/') ?? false) { return true; } //.NET Framework 4.5 : System.IO.Compression::CreateFromDirectory() probably writes backslashes to headers - return CompressedSize == 0 && UncompressedSize == 0 && Name.EndsWith('\\'); + return CompressedSize == 0 && UncompressedSize == 0 && (Name?.EndsWith('\\') ?? false); } } - internal Stream PackedStream { get; set; } + internal Stream? PackedStream { get; set; } internal ArchiveEncoding ArchiveEncoding { get; } - internal string Name { get; set; } + internal string? Name { get; set; } internal HeaderFlags Flags { get; set; } @@ -48,7 +46,7 @@ internal abstract class ZipFileEntry : ZipHeader internal List Extra { get; set; } - public string Password { get; set; } + public string? Password { get; set; } internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) { @@ -65,7 +63,7 @@ internal abstract class ZipFileEntry : ZipHeader return encryptionData; } - internal WinzipAesEncryptionData WinzipAesEncryptionData { get; set; } + internal WinzipAesEncryptionData? WinzipAesEncryptionData { get; set; } /// /// The last modified date as read from the Local or Central Directory header. @@ -119,7 +117,7 @@ internal abstract class ZipFileEntry : ZipHeader } } - internal ZipFilePart Part { get; set; } + internal ZipFilePart? Part { get; set; } internal bool IsZip64 => CompressedSize >= uint.MaxValue; } diff --git a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs index 1cd1f61f..97e44b6b 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs @@ -13,7 +13,7 @@ internal sealed class StreamingZipFilePart : ZipFilePart internal StreamingZipFilePart(ZipFileEntry header, Stream stream) : base(header, stream) { } - protected override Stream CreateBaseStream() => Header.PackedStream; + protected override Stream CreateBaseStream() => Header.PackedStream.NotNull(); internal override Stream GetCompressedStream() { diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 68817d27..c0af5408 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -42,6 +42,10 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory ) ) { + if (_lastEntryHeader.Part is null) + { + continue; + } reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation( ref rewindableStream ); diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 80e33360..66d19dcc 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using SharpCompress.Common.Zip.Headers; @@ -8,22 +6,23 @@ namespace SharpCompress.Common.Zip; public class ZipEntry : Entry { - private readonly ZipFilePart _filePart; + private readonly ZipFilePart? _filePart; - internal ZipEntry(ZipFilePart filePart) + internal ZipEntry(ZipFilePart? filePart) { - if (filePart != null) + if (filePart == null) { + return; + } _filePart = filePart; LastModifiedTime = Utility.DosDateToDateTime( filePart.Header.LastModifiedDate, filePart.Header.LastModifiedTime ); - } } public override CompressionType CompressionType => - _filePart.Header.CompressionMethod switch + _filePart?.Header.CompressionMethod switch { ZipCompressionMethod.BZip2 => CompressionType.BZip2, ZipCompressionMethod.Deflate => CompressionType.Deflate, @@ -35,15 +34,15 @@ public class ZipEntry : Entry _ => CompressionType.Unknown }; - public override long Crc => _filePart.Header.Crc; + public override long Crc => _filePart?.Header.Crc ?? 0; - public override string Key => _filePart.Header.Name; + public override string? Key => _filePart?.Header.Name; - public override string LinkTarget => null; + public override string? LinkTarget => null; - public override long CompressedSize => _filePart.Header.CompressedSize; + public override long CompressedSize => _filePart?.Header.CompressedSize ?? 0; - public override long Size => _filePart.Header.UncompressedSize; + public override long Size => _filePart?.Header.UncompressedSize ?? 0; public override DateTime? LastModifiedTime { get; } @@ -54,11 +53,11 @@ public class ZipEntry : Entry public override DateTime? ArchivedTime => null; public override bool IsEncrypted => - FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted); + FlagUtility.HasFlag(_filePart?.Header.Flags ?? HeaderFlags.None, HeaderFlags.Encrypted); - public override bool IsDirectory => _filePart.Header.IsDirectory; + public override bool IsDirectory => _filePart?.Header.IsDirectory ?? false; public override bool IsSplitAfter => false; - internal override IEnumerable Parts => _filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.Empty(); } diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index f8a0e8a8..e8516150 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -29,7 +29,7 @@ internal abstract class ZipFilePart : FilePart internal Stream BaseStream { get; } internal ZipFileEntry Header { get; set; } - internal override string FilePartName => Header.Name; + internal override string? FilePartName => Header.Name; internal override Stream GetCompressedStream() { diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 74f6037a..1dbff914 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -2,6 +2,7 @@ using System; using System.Buffers; using System.Collections.Generic; using System.IO; +using System.Linq; using SharpCompress.Readers; namespace SharpCompress; @@ -25,6 +26,22 @@ public static class Utility } return item; } + public static IEnumerable Empty(this IEnumerable? item) where T : class + { + if (item is null) + { + return Enumerable.Empty(); + } + return item; + } + public static IEnumerable Empty(this T? item) where T : class + { + if (item is null) + { + return Enumerable.Empty(); + } + return item.AsEnumerable(); + } public static ReadOnlyCollection ToReadOnly(this ICollection items) => new(items); /// diff --git a/src/SharpCompress/Writers/AbstractWriter.cs b/src/SharpCompress/Writers/AbstractWriter.cs index 209c49f7..1820c986 100644 --- a/src/SharpCompress/Writers/AbstractWriter.cs +++ b/src/SharpCompress/Writers/AbstractWriter.cs @@ -1,28 +1,24 @@ -#nullable disable - -using System; +using System; using System.IO; using SharpCompress.Common; namespace SharpCompress.Writers; -public abstract class AbstractWriter : IWriter +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +public abstract class AbstractWriter(ArchiveType type, WriterOptions writerOptions) : IWriter { private bool _isDisposed; - protected AbstractWriter(ArchiveType type, WriterOptions writerOptions) - { - WriterType = type; - WriterOptions = writerOptions; - } + //always initializes the stream - protected void InitalizeStream(Stream stream) => OutputStream = stream; + protected void InitializeStream(Stream stream) => OutputStream = stream; protected Stream OutputStream { get; private set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public ArchiveType WriterType { get; } + public ArchiveType WriterType { get; } = type; - protected WriterOptions WriterOptions { get; } + protected WriterOptions WriterOptions { get; } = writerOptions; public abstract void Write(string filename, Stream source, DateTime? modificationTime); diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index 5c990a7d..a94a7238 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -18,14 +18,14 @@ public sealed class GZipWriter : AbstractWriter { destination = NonDisposingStream.Create(destination); } - InitalizeStream( - new GZipStream( - destination, - CompressionMode.Compress, - options?.CompressionLevel ?? CompressionLevel.Default, - WriterOptions.ArchiveEncoding.GetEncoding() - ) - ); + InitializeStream( + new GZipStream( + destination, + CompressionMode.Compress, + options?.CompressionLevel ?? CompressionLevel.Default, + WriterOptions.ArchiveEncoding.GetEncoding() + ) + ); } protected override void Dispose(bool isDisposing) diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index 90242857..3826b4bb 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -56,7 +56,7 @@ public class TarWriter : AbstractWriter ); } } - InitalizeStream(destination); + InitializeStream(destination); } public override void Write(string filename, Stream source, DateTime? modificationTime) => diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 9dc094e2..440b210d 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -42,14 +42,14 @@ public class ZipWriter : AbstractWriter { destination = NonDisposingStream.Create(destination); } - InitalizeStream(destination); + InitializeStream(destination); } private PpmdProperties PpmdProperties => ppmdProps ??= new PpmdProperties(); protected override void Dispose(bool isDisposing) { - if (isDisposing) + if (isDisposing && OutputStream is not null) { ulong size = 0; foreach (var entry in entries) @@ -114,7 +114,7 @@ public class ZipWriter : AbstractWriter streamPosition += headersize; return new ZipWritingStream( this, - OutputStream, + OutputStream.NotNull(), entry, compression, options.DeflateCompressionLevel ?? compressionLevel diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index a0c9d8c9..32d978a2 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -182,7 +182,7 @@ public class SevenZipArchiveTests : ArchiveTests using (var archive = SevenZipArchive.Open(stream)) { var entry = archive.Entries.First(); - entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key)); + entry.WriteToFile(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull())); var size = entry.Size; var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "7Zip.Tar.tar")); diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index 1ee6b783..bbf96c00 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -195,7 +195,7 @@ public class TarArchiveTests : ArchiveTests using (var archive = TarArchive.Open(unmodified)) { var entry = archive.Entries.Single(x => - x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); archive.SaveTo(scratchPath, CompressionType.None); diff --git a/tests/SharpCompress.Test/Tar/TarReaderTests.cs b/tests/SharpCompress.Test/Tar/TarReaderTests.cs index 1ee4ae8d..bb10db98 100644 --- a/tests/SharpCompress.Test/Tar/TarReaderTests.cs +++ b/tests/SharpCompress.Test/Tar/TarReaderTests.cs @@ -79,7 +79,7 @@ public class TarReaderTests : ReaderTests { Directory.CreateDirectory(destdir); } - var destinationFileName = Path.Combine(destdir, file); + var destinationFileName = Path.Combine(destdir, file.NotNull()); using var fs = File.OpenWrite(destinationFileName); entryStream.TransferTo(fs); @@ -105,7 +105,7 @@ public class TarReaderTests : ReaderTests { if (!reader.Entry.IsDirectory) { - filePaths.Add(reader.Entry.Key); + filePaths.Add(reader.Entry.Key.NotNull("Entry Key is null")); } } } @@ -135,7 +135,7 @@ public class TarReaderTests : ReaderTests Assert.Equal(CompressionType.BZip2, reader.Entry.CompressionType); using var entryStream = reader.OpenEntryStream(); entryStream.SkipEntry(); - names.Add(reader.Entry.Key); + names.Add(reader.Entry.Key.NotNull()); } } Assert.Equal(3, names.Count); @@ -224,7 +224,7 @@ public class TarReaderTests : ReaderTests { if (reader.Entry.LinkTarget != null) { - var path = Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key); + var path = Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key.NotNull()); var link = new Mono.Unix.UnixSymbolicLinkInfo(path); if (link.HasContents) { diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index afbe7c9e..b6e46d4a 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -193,7 +193,7 @@ public class ZipArchiveTests : ArchiveTests using (var archive = ZipArchive.Open(unmodified)) { var entry = archive.Entries.Single(x => - x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); @@ -249,11 +249,11 @@ public class ZipArchiveTests : ArchiveTests var scratchPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); using var vfs = (ZipArchive)ArchiveFactory.Open(scratchPath); - var e = vfs.Entries.First(v => v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase)); + var e = vfs.Entries.First(v => v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase)); vfs.RemoveEntry(e); Assert.Null( vfs.Entries.FirstOrDefault(v => - v.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + v.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ) ); Assert.Null( @@ -394,12 +394,12 @@ public class ZipArchiveTests : ArchiveTests archive.AddAllFromDirectory(SCRATCH_FILES_PATH); archive.RemoveEntry( archive.Entries.Single(x => - x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ) ); Assert.Null( archive.Entries.FirstOrDefault(x => - x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase) + x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ) ); }