mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-22 23:15:20 +00:00
@@ -15,6 +15,11 @@ namespace SharpCompress.Common
|
||||
/// </summary>
|
||||
public abstract string Key { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The target of a symlink entry internal to the Archive. Will be null if not a symlink.
|
||||
/// </summary>
|
||||
public abstract string LinkTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The compressed file size
|
||||
/// </summary>
|
||||
|
||||
@@ -68,19 +68,30 @@ namespace SharpCompress.Common
|
||||
ExtractionOptions options,
|
||||
Action<string, FileMode> openAndWrite)
|
||||
{
|
||||
FileMode fm = FileMode.Create;
|
||||
options = options ?? new ExtractionOptions()
|
||||
{
|
||||
Overwrite = true
|
||||
};
|
||||
|
||||
if (!options.Overwrite)
|
||||
if (entry.LinkTarget != null)
|
||||
{
|
||||
fm = FileMode.CreateNew;
|
||||
if (null == options.WriteSymbolicLink)
|
||||
{
|
||||
throw new ExtractionException("Entry is a symbolic link but ExtractionOptions.WriteSymbolicLink delegate is null");
|
||||
}
|
||||
options.WriteSymbolicLink(destinationFileName, entry.LinkTarget);
|
||||
}
|
||||
else
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -21,5 +21,14 @@
|
||||
/// preserve windows file attributes
|
||||
/// </summary>
|
||||
public bool PreserveAttributes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Delegate for writing symbolic links to disk.
|
||||
/// sourcePath is where the symlink is created.
|
||||
/// targetPath is what the symlink refers to.
|
||||
/// </summary>
|
||||
public delegate void SymbolicLinkWriterDelegate(string sourcePath, string targetPath);
|
||||
|
||||
public SymbolicLinkWriterDelegate WriteSymbolicLink;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace SharpCompress.Common.Rar
|
||||
/// </summary>
|
||||
public override string Key => FileHeader.FileName;
|
||||
|
||||
public override string LinkTarget => null;
|
||||
|
||||
/// <summary>
|
||||
/// The entry last modified time in the archive, if recorded
|
||||
/// </summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="Xunit.SkippableFact" Version="1.3.6" />
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -186,5 +186,65 @@ namespace SharpCompress.Test.Tar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tar_GZip_With_Symlink_Entries()
|
||||
{
|
||||
var isWindows = System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(
|
||||
System.Runtime.InteropServices.OSPlatform.Windows);
|
||||
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "TarWithSymlink.tar.gz")))
|
||||
using (var reader = TarReader.Open(stream))
|
||||
{
|
||||
List<string> names = new List<string>();
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
if (reader.Entry.IsDirectory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
reader.WriteEntryToDirectory(SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true,
|
||||
WriteSymbolicLink = (sourcePath, targetPath) =>
|
||||
{
|
||||
if (!isWindows)
|
||||
{
|
||||
var link = new Mono.Unix.UnixSymbolicLinkInfo(sourcePath);
|
||||
if (System.IO.File.Exists(sourcePath))
|
||||
{
|
||||
link.Delete(); // equivalent to ln -s -f
|
||||
}
|
||||
link.CreateSymbolicLinkTo(targetPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!isWindows)
|
||||
{
|
||||
if (reader.Entry.LinkTarget != null)
|
||||
{
|
||||
var path = System.IO.Path.Combine(SCRATCH_FILES_PATH, reader.Entry.Key);
|
||||
var link = new Mono.Unix.UnixSymbolicLinkInfo(path);
|
||||
if (link.HasContents)
|
||||
{
|
||||
// need to convert the link to an absolute path for comparison
|
||||
var target = reader.Entry.LinkTarget;
|
||||
var realTarget = System.IO.Path.GetFullPath(
|
||||
System.IO.Path.Combine($"{System.IO.Path.GetDirectoryName(path)}",
|
||||
target)
|
||||
);
|
||||
|
||||
Assert.Equal(realTarget, link.GetContents().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.True(false, "Symlink has no target");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
tests/TestArchives/Archives/TarWithSymlink.tar.gz
Normal file
BIN
tests/TestArchives/Archives/TarWithSymlink.tar.gz
Normal file
Binary file not shown.
Reference in New Issue
Block a user