From 192b9c1e8bb36b76fb2b05ebd36918982bb1d6bf Mon Sep 17 00:00:00 2001 From: Mark Final Date: Thu, 1 Nov 2018 21:48:48 +0000 Subject: [PATCH] Ref #248. Ref #132. Tar reader support for symlinks for .NET standard 2 and Posix platforms Extracts linkname from the tar header, and exposes this on IEntry as the LinkTarget (string) property. If an entry is not a symlink, then that property is null. Uses Mono.Posix.NETStandard nuget to create a symlink. However, this is only applicable to .NET standard 2.0+. So far, unable to find a nuget that works for older versions. Also, not sure what to do on Windows. --- src/SharpCompress/Common/Entry.cs | 5 +++ src/SharpCompress/Common/ExtractionMethods.cs | 41 ++++++++++++++----- src/SharpCompress/Common/GZip/GZipEntry.cs | 2 + src/SharpCompress/Common/IEntry.cs | 1 + src/SharpCompress/Common/Rar/RarEntry.cs | 2 + .../Common/SevenZip/SevenZipEntry.cs | 2 + .../Common/Tar/Headers/TarHeader.cs | 7 ++++ src/SharpCompress/Common/Tar/TarEntry.cs | 2 + src/SharpCompress/Common/Zip/ZipEntry.cs | 2 + src/SharpCompress/SharpCompress.csproj | 1 + 10 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/SharpCompress/Common/Entry.cs b/src/SharpCompress/Common/Entry.cs index b5f58055..c12cdf6f 100644 --- a/src/SharpCompress/Common/Entry.cs +++ b/src/SharpCompress/Common/Entry.cs @@ -15,6 +15,11 @@ namespace SharpCompress.Common /// public abstract string Key { get; } + /// + /// The target of a symlink entry internal to the Archive. Will be null if not a symlink. + /// + public abstract string LinkTarget { get; } + /// /// The compressed file size /// diff --git a/src/SharpCompress/Common/ExtractionMethods.cs b/src/SharpCompress/Common/ExtractionMethods.cs index 71e67a97..5d59910c 100644 --- a/src/SharpCompress/Common/ExtractionMethods.cs +++ b/src/SharpCompress/Common/ExtractionMethods.cs @@ -68,19 +68,40 @@ namespace SharpCompress.Common ExtractionOptions options, Action openAndWrite) { - FileMode fm = FileMode.Create; - options = options ?? new ExtractionOptions() - { - Overwrite = true - }; - - if (!options.Overwrite) +#if NETSTANDARD2_0 + if (entry.LinkTarget != null) { - fm = FileMode.CreateNew; + /* + var parDir = System.IO.Path.GetDirectoryName(destinationFileName); + if (!System.IO.Directory.Exists(parDir)) + { + System.IO.Directory.CreateDirectory(parDir); + } + */ + var link = new Mono.Unix.UnixSymbolicLinkInfo(destinationFileName); + if (System.IO.File.Exists(destinationFileName)) + { + link.Delete(); // equivalent to ln -s -f + } + link.CreateSymbolicLinkTo(entry.LinkTarget); } + else +#endif + { + FileMode fm = FileMode.Create; + options = options ?? new ExtractionOptions() + { + Overwrite = true + }; - openAndWrite(destinationFileName, fm); - entry.PreserveExtractionOptions(destinationFileName, options); + if (!options.Overwrite) + { + fm = FileMode.CreateNew; + } + + openAndWrite(destinationFileName, fm); + entry.PreserveExtractionOptions(destinationFileName, options); + } } #endif } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 0b43cd45..8b2d3e93 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -20,6 +20,8 @@ namespace SharpCompress.Common.GZip public override string Key => _filePart.FilePartName; + public override string LinkTarget => null; + public override long CompressedSize => 0; public override long Size => 0; diff --git a/src/SharpCompress/Common/IEntry.cs b/src/SharpCompress/Common/IEntry.cs index e8f13a30..84cfcb33 100644 --- a/src/SharpCompress/Common/IEntry.cs +++ b/src/SharpCompress/Common/IEntry.cs @@ -10,6 +10,7 @@ namespace SharpCompress.Common long Crc { get; } DateTime? CreatedTime { get; } string Key { get; } + string LinkTarget { get; } bool IsDirectory { get; } bool IsEncrypted { get; } bool IsSplitAfter { get; } diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index 85be0bb3..c461f42a 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -22,6 +22,8 @@ namespace SharpCompress.Common.Rar /// public override string Key => FileHeader.FileName; + public override string LinkTarget => null; + /// /// The entry last modified time in the archive, if recorded /// diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index 584bfb95..c2ea2efe 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -18,6 +18,8 @@ namespace SharpCompress.Common.SevenZip public override string Key => FilePart.Header.Name; + public override string LinkTarget => null; + public override long CompressedSize => 0; public override long Size => FilePart.Header.Size; diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index 7cdfcef3..db6d5cf4 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -15,6 +15,7 @@ namespace SharpCompress.Common.Tar.Headers } internal string Name { get; set; } + internal string LinkName { get; set; } //internal int Mode { get; set; } //internal int UserId { get; set; } @@ -98,6 +99,12 @@ namespace SharpCompress.Common.Tar.Headers return false; } + // for symlinks, additionally read the linkname + if (ReadEntryType(buffer) == EntryType.SymLink) + { + LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls(); + } + if (ReadEntryType(buffer) == EntryType.LongName) { Name = ReadLongName(reader, buffer); diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 82e707d0..6ec5d319 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -23,6 +23,8 @@ namespace SharpCompress.Common.Tar public override string Key => _filePart.Header.Name; + public override string LinkTarget => _filePart.Header.LinkName; + public override long CompressedSize => _filePart.Header.Size; public override long Size => _filePart.Header.Size; diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 379534ff..3f65adae 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -60,6 +60,8 @@ namespace SharpCompress.Common.Zip public override string Key => _filePart.Header.Name; + public override string LinkTarget => null; + public override long CompressedSize => _filePart.Header.CompressedSize; public override long Size => _filePart.Header.UncompressedSize; diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 1e009ded..66775ecc 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -34,6 +34,7 @@ $(DefineConstants);NETCORE + \ No newline at end of file