Skip to content

Commit

Permalink
Fix icsharpcode#337 (tar): Files added under CurrentDirectory must re…
Browse files Browse the repository at this point in the history
…spect RootPath

This changes the behavior of TarArchive when files are added under the
CurrentDirectory.

When a TarEntry is created from a file which is under the
CurrentDirectory, the path is made relative to the CurrentDirectory.
This behavior affects how TarArchive removes a prefix from files set by
the RootPath directory.

The expectation for RootPath is that a matching substring will be
removed from the prefix of entries before they are added to the
TarArchive. Because the TarEntry for files under CurrentDirectory are
modified, RootPath will not work if it is an absolute path under
CurrentDirectory.

This change fixes this behavior. If RootPath is a directory under
CurrentDirectory, it will apply to entres added to TarArchive.

An attempt was made to implement this change without additional string
manipulation. This could be done with a modified IndexOf, that enables
searching for a substring of the pattern within the target substring.
This may improve performance.
  • Loading branch information
Chris-Johnston committed Aug 26, 2022
1 parent 519ed73 commit 1719e91
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 2 deletions.
26 changes: 24 additions & 2 deletions src/ICSharpCode.SharpZipLib/Tar/TarArchive.cs
Expand Up @@ -857,12 +857,34 @@ private void WriteEntryCore(TarEntry sourceEntry, bool recurse)

if (!String.IsNullOrEmpty(rootPath))
{
if (entry.Name.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase))
string newRootPath = rootPath;

// remove CurrentDirectory from RootPath to be consistent with TarEntry behavior
var currentDirectory = Directory.GetCurrentDirectory()
.ToTarArchivePath();
if (rootPath.StartsWith(currentDirectory, StringComparison.OrdinalIgnoreCase))
{
newName = entry.Name.Substring(rootPath.Length + 1);
if (rootPath.Length == currentDirectory.Length)
{
// if they are the same, rootPath would be empty
// and so the entry name is not modified
newRootPath = string.Empty;
}
else
{
// TarEntry would have removed the current directory from the entry name, and so do the same here
newRootPath = rootPath.Substring(currentDirectory.Length + 1);
}
}

if (!string.IsNullOrEmpty(newRootPath) &&
entry.Name.StartsWith(newRootPath, StringComparison.OrdinalIgnoreCase))
{
newName = entry.Name.Substring(newRootPath.Length + 1);
}
}


if (pathPrefix != null)
{
newName = (newName == null) ? pathPrefix + "/" + entry.Name : pathPrefix + "/" + newName;
Expand Down
113 changes: 113 additions & 0 deletions test/ICSharpCode.SharpZipLib.Tests/Tar/TarTests.cs
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework.Internal;
using System.Linq;

namespace ICSharpCode.SharpZipLib.Tests.Tar
{
Expand Down Expand Up @@ -916,5 +917,117 @@ public void RootPathIsRespected()
}
}
}

/// <summary>
/// This tests the behavior of the <see cref="TarArchive.RootPath"/> property on
/// extracted files.
/// </summary>
/// <param name="rootPath"></param>
/// <param name="setRootPathPrefix"></param>
/// <param name="filePathSegments"></param>
/// <param name="setFilePathPrefix"></param>
/// <param name="setCurrentDirectory"></param>
/// <param name="expectedFilePath"></param>
[Test]
[Category("Tar")]
// {workingDirectory}/temp {workingDirectory}/temp/file.txt -> "file.txt"
[TestCase("temp", true, new string[] { "temp", "file.txt" }, true, true, new string[] { "file.txt" })]
// (unset) {workingDirectory}/temp/file.txt -> "temp/file.txt"
[TestCase("", false, new string[] { "temp", "file.txt" }, true, true, new string[] { "temp", "file.txt" })]
// "temp" {workingDirectory}/temp/file.txt -> "file.txt"
[TestCase("temp", false, new string[] { "temp", "file.txt" }, true, true, new string[] { "file.txt" })]
// nonWorkDir/temp/ nonWorkDir/temp/file.txt -> "file.txt"
[TestCase("temp", true, new string[] { "temp", "file.txt" }, true, false, new string[] { "file.txt" })]
// (unset) /nonWorkDir/temp/file.txt -> "/nonWorkDir/temp/file.txt"
[TestCase("", true, new string[] { "temp", "file.txt" }, true, false, new string[] { "temp", "file.txt" })]
public void TestRootPathBehavior(string rootPath, bool setRootPathPrefix, string[] filePathSegments, bool setFilePathPrefix, bool setCurrentDirectory, string[] expectedFilePath)
{
// when overrideWorkingDirectory is false, assumption is that working directory is that of the assembly
// which is not the same as the temporary directory

using (var workDir = new TempDir())
using (var tarFileName = new TempFile())
using (var extractDirectory = new TempDir())
{
if (setCurrentDirectory)
{
Environment.CurrentDirectory = workDir;
}

if (setRootPathPrefix)
{
rootPath = Path.Combine(Environment.CurrentDirectory, rootPath);
}

string filePath = Path.Combine(filePathSegments);

if (setFilePathPrefix)
{
filePath = Path.Combine(Environment.CurrentDirectory, filePath);
}
else
{
filePath = Path.Combine(workDir, filePath);
}

//var expectDir = Path.Combine(workDir.FullName, "temp");
//Directory.CreateDirectory(expectDir);
//var inputFile = Path.Combine(expectDir, "file.txt");
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
File.WriteAllText(filePath, Guid.NewGuid().ToString());

// extract files under the given path
using (var tarFile = File.Open(tarFileName.FullName, FileMode.Create))
{
using (var tarOutputStream = TarArchive.CreateOutputTarArchive(tarFile))
{
if (rootPath != string.Empty)
{
tarOutputStream.RootPath = rootPath;
}
string assignedRootPath = tarOutputStream.RootPath;

var entry = TarEntry.CreateEntryFromFile(filePath);

// TarEntry.Name may be relative or absolute depending on if it
// was created under the CurrentDirectory
if (setCurrentDirectory)
{
Assert.AreEqual("temp/file.txt", entry.Name);
}
else
{
Assert.IsTrue(entry.Name.EndsWith("temp/file.txt"));
}

tarOutputStream.WriteEntry(entry, true);

// RootPath property does not change
Assert.AreEqual(assignedRootPath, tarOutputStream.RootPath);
}
}

using (var file = File.OpenRead(tarFileName.FullName))
{
using (var archive = TarArchive.CreateInputTarArchive(file, Encoding.UTF8))
{
archive.ExtractContents(extractDirectory.FullName);
}
}

// the resulting files must be the same as the expectation dir, should no longer have the "temp" prefix
var expectationDirectory = new DirectoryInfo(extractDirectory.FullName);
var expectedFile = expectationDirectory.GetFiles("", SearchOption.AllDirectories)
.First();

var expected = Path.Combine(extractDirectory.FullName, Path.Combine(expectedFilePath));

// the archive should contain the entry "testFile.txt", not "temp/testFile.txt", because
// the root dir is configured to "{CurrentDirectory}/tmp/"
// FileAssert.DoesNotExist(Path.Combine(extractDirectory.FullName, "temp", "testFile.txt"));
FileAssert.Exists(expected); // handle directory separators?
FileAssert.AreEqual(filePath, expected);
}
}
}
}

0 comments on commit 1719e91

Please sign in to comment.