Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/libraries/System.Formats.Tar/ref/System.Formats.Tar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ public enum TarEntryType : byte
public static partial class TarFile
{
public static void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination, bool includeBaseDirectory) { }
public static void CreateFromDirectory(string sourceDirectoryName, System.IO.Stream destination, bool includeBaseDirectory, System.Formats.Tar.TarEntryFormat format) { }
public static void CreateFromDirectory(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory) { }
public static void CreateFromDirectory(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, System.Formats.Tar.TarEntryFormat format) { }
public static System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, System.IO.Stream destination, bool includeBaseDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, System.IO.Stream destination, bool includeBaseDirectory, System.Formats.Tar.TarEntryFormat format, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static System.Threading.Tasks.Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, System.Formats.Tar.TarEntryFormat format, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public static void ExtractToDirectory(System.IO.Stream source, string destinationDirectoryName, bool overwriteFiles) { }
public static void ExtractToDirectory(string sourceFileName, string destinationDirectoryName, bool overwriteFiles) { }
public static System.Threading.Tasks.Task ExtractToDirectoryAsync(System.IO.Stream source, string destinationDirectoryName, bool overwriteFiles, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
Expand Down
104 changes: 94 additions & 10 deletions src/libraries/System.Formats.Tar/src/System/Formats/Tar/TarFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ public static class TarFile
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, bool includeBaseDirectory)
=> CreateFromDirectory(sourceDirectoryName, destination, includeBaseDirectory, TarEntryFormat.Pax);

/// <summary>
/// Creates a tar stream that contains all the filesystem entries from the specified directory.
/// </summary>
/// <param name="sourceDirectoryName">The path of the directory to archive.</param>
/// <param name="destination">The destination stream of the archive.</param>
/// <param name="includeBaseDirectory"><see langword="true"/> to include the base directory name as the first segment in all the names of the archive entries. <see langword="false"/> to exclude the base directory name from the archive entry names.</param>
/// <param name="format">The format of the tar entries.</param>
/// <exception cref="ArgumentNullException"><paramref name="sourceDirectoryName"/> or <paramref name="destination"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><para><paramref name="sourceDirectoryName"/> is empty.</para>
/// <para>-or-</para>
/// <para><paramref name="destination"/> does not support writing.</para></exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> is either <see cref="TarEntryFormat.Unknown"/>, or not one of the other enum values.</exception>
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static void CreateFromDirectory(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, TarEntryFormat format)
{
ArgumentException.ThrowIfNullOrEmpty(sourceDirectoryName);
ArgumentNullException.ThrowIfNull(destination);
Expand All @@ -43,10 +60,12 @@ public static void CreateFromDirectory(string sourceDirectoryName, Stream destin
throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceDirectoryName));
}

ValidateTarEntryFormat(format);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this validation necessary, or will it be handled implicitly by the underlying TarWriter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, TarWriter already validates the format, I added so it fails early. But yes, it is redundant


// Rely on Path.GetFullPath for validation of paths
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);

CreateFromDirectoryInternal(sourceDirectoryName, destination, includeBaseDirectory, leaveOpen: true);
CreateFromDirectoryInternal(sourceDirectoryName, destination, includeBaseDirectory, format, leaveOpen: true);
}

/// <summary>
Expand All @@ -64,6 +83,25 @@ public static void CreateFromDirectory(string sourceDirectoryName, Stream destin
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static Task CreateFromDirectoryAsync(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, CancellationToken cancellationToken = default)
=> CreateFromDirectoryAsync(sourceDirectoryName, destination, includeBaseDirectory, TarEntryFormat.Pax, cancellationToken);

/// <summary>
/// Asynchronously creates a tar stream that contains all the filesystem entries from the specified directory.
/// </summary>
/// <param name="sourceDirectoryName">The path of the directory to archive.</param>
/// <param name="destination">The destination stream of the archive.</param>
/// <param name="includeBaseDirectory"><see langword="true"/> to include the base directory name as the first path segment in all the names of the archive entries. <see langword="false"/> to exclude the base directory name from the entry name paths.</param>
/// <param name="format">The format of the tar entries.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous creation operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="sourceDirectoryName"/> or <paramref name="destination"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><para><paramref name="sourceDirectoryName"/> is empty.</para>
/// <para>-or-</para>
/// <para><paramref name="destination"/> does not support writing.</para></exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> is either <see cref="TarEntryFormat.Unknown"/>, or not one of the other enum values.</exception>
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static Task CreateFromDirectoryAsync(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, TarEntryFormat format, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
Expand All @@ -82,10 +120,12 @@ public static Task CreateFromDirectoryAsync(string sourceDirectoryName, Stream d
return Task.FromException(new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceDirectoryName)));
}

ValidateTarEntryFormat(format);

// Rely on Path.GetFullPath for validation of paths
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);

return CreateFromDirectoryInternalAsync(sourceDirectoryName, destination, includeBaseDirectory, leaveOpen: true, cancellationToken);
return CreateFromDirectoryInternalAsync(sourceDirectoryName, destination, includeBaseDirectory, format, leaveOpen: true, cancellationToken);
}

/// <summary>
Expand All @@ -99,10 +139,27 @@ public static Task CreateFromDirectoryAsync(string sourceDirectoryName, Stream d
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static void CreateFromDirectory(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory)
=> CreateFromDirectory(sourceDirectoryName, destinationFileName, includeBaseDirectory, TarEntryFormat.Pax);

/// <summary>
/// Creates a tar file that contains all the filesystem entries from the specified directory.
/// </summary>
/// <param name="sourceDirectoryName">The path of the directory to archive.</param>
/// <param name="destinationFileName">The path of the destination archive file.</param>
/// <param name="includeBaseDirectory"><see langword="true"/> to include the base directory name as the first path segment in all the names of the archive entries. <see langword="false"/> to exclude the base directory name from the entry name paths.</param>
/// <param name="format">The format of the tar entries.</param>
/// <exception cref="ArgumentNullException"><paramref name="sourceDirectoryName"/> or <paramref name="destinationFileName"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="sourceDirectoryName"/> or <paramref name="destinationFileName"/> is empty.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> is either <see cref="TarEntryFormat.Unknown"/>, or not one of the other enum values.</exception>
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static void CreateFromDirectory(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, TarEntryFormat format)
{
ArgumentException.ThrowIfNullOrEmpty(sourceDirectoryName);
ArgumentException.ThrowIfNullOrEmpty(destinationFileName);

ValidateTarEntryFormat(format);

// Rely on Path.GetFullPath for validation of paths
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);
destinationFileName = Path.GetFullPath(destinationFileName);
Expand All @@ -115,7 +172,7 @@ public static void CreateFromDirectory(string sourceDirectoryName, string destin
// Throws if the destination file exists
using FileStream fs = new(destinationFileName, FileMode.CreateNew, FileAccess.Write);

CreateFromDirectoryInternal(sourceDirectoryName, fs, includeBaseDirectory, leaveOpen: false);
CreateFromDirectoryInternal(sourceDirectoryName, fs, includeBaseDirectory, format, leaveOpen: false);
}

/// <summary>
Expand All @@ -131,6 +188,23 @@ public static void CreateFromDirectory(string sourceDirectoryName, string destin
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, CancellationToken cancellationToken = default)
=> CreateFromDirectoryAsync(sourceDirectoryName, destinationFileName, includeBaseDirectory, TarEntryFormat.Pax, cancellationToken);

/// <summary>
/// Asynchronously creates a tar archive from the contents of the specified directory, and outputs them into the specified path. Can optionally include the base directory as the prefix for the entry names.
/// </summary>
/// <param name="sourceDirectoryName">The path of the directory to archive.</param>
/// <param name="destinationFileName">The path of the destination archive file.</param>
/// <param name="includeBaseDirectory"><see langword="true"/> to include the base directory name as the first path segment in all the names of the archive entries. <see langword="false"/> to exclude the base directory name from the entry name paths.</param>
/// <param name="format">The format of the tar entries.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous creation operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="sourceDirectoryName"/> or <paramref name="destinationFileName"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="sourceDirectoryName"/> or <paramref name="destinationFileName"/> is empty.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="format"/> is either <see cref="TarEntryFormat.Unknown"/>, or not one of the other enum values.</exception>
/// <exception cref="DirectoryNotFoundException">The <paramref name="sourceDirectoryName"/> directory path was not found.</exception>
/// <exception cref="IOException">An I/O exception occurred.</exception>
public static Task CreateFromDirectoryAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, TarEntryFormat format, CancellationToken cancellationToken = default)
{
if (cancellationToken.IsCancellationRequested)
{
Expand All @@ -139,6 +213,8 @@ public static Task CreateFromDirectoryAsync(string sourceDirectoryName, string d
ArgumentException.ThrowIfNullOrEmpty(sourceDirectoryName);
ArgumentException.ThrowIfNullOrEmpty(destinationFileName);

ValidateTarEntryFormat(format);

// Rely on Path.GetFullPath for validation of paths
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);
destinationFileName = Path.GetFullPath(destinationFileName);
Expand All @@ -148,7 +224,7 @@ public static Task CreateFromDirectoryAsync(string sourceDirectoryName, string d
return Task.FromException(new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, sourceDirectoryName)));
}

return CreateFromDirectoryInternalAsync(sourceDirectoryName, destinationFileName, includeBaseDirectory, cancellationToken);
return CreateFromDirectoryInternalAsync(sourceDirectoryName, destinationFileName, includeBaseDirectory, format, cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -323,11 +399,11 @@ public static Task ExtractToDirectoryAsync(string sourceFileName, string destina

// Creates an archive from the contents of a directory.
// It assumes the sourceDirectoryName is a fully qualified path, and allows choosing if the archive stream should be left open or not.
private static void CreateFromDirectoryInternal(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, bool leaveOpen)
private static void CreateFromDirectoryInternal(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, TarEntryFormat format, bool leaveOpen)
{
VerifyCreateFromDirectoryArguments(sourceDirectoryName, destination);

using (TarWriter writer = new TarWriter(destination, TarEntryFormat.Pax, leaveOpen))
using (TarWriter writer = new TarWriter(destination, format, leaveOpen))
{
DirectoryInfo di = new(sourceDirectoryName);

Expand All @@ -353,7 +429,7 @@ private static void CreateFromDirectoryInternal(string sourceDirectoryName, Stre
}

// Asynchronously creates a tar archive from the contents of the specified directory, and outputs them into the specified path.
private static async Task CreateFromDirectoryInternalAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, CancellationToken cancellationToken)
private static async Task CreateFromDirectoryInternalAsync(string sourceDirectoryName, string destinationFileName, bool includeBaseDirectory, TarEntryFormat format, CancellationToken cancellationToken)
{
Debug.Assert(!string.IsNullOrEmpty(sourceDirectoryName));
Debug.Assert(!string.IsNullOrEmpty(destinationFileName));
Expand All @@ -370,18 +446,18 @@ private static async Task CreateFromDirectoryInternalAsync(string sourceDirector
FileStream archive = new(destinationFileName, options);
await using (archive.ConfigureAwait(false))
{
await CreateFromDirectoryInternalAsync(sourceDirectoryName, archive, includeBaseDirectory, leaveOpen: false, cancellationToken).ConfigureAwait(false);
await CreateFromDirectoryInternalAsync(sourceDirectoryName, archive, includeBaseDirectory, format, leaveOpen: false, cancellationToken).ConfigureAwait(false);
}
}

// Asynchronously creates an archive from the contents of a directory.
// It assumes the sourceDirectoryName is a fully qualified path, and allows choosing if the archive stream should be left open or not.
private static async Task CreateFromDirectoryInternalAsync(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, bool leaveOpen, CancellationToken cancellationToken)
private static async Task CreateFromDirectoryInternalAsync(string sourceDirectoryName, Stream destination, bool includeBaseDirectory, TarEntryFormat format, bool leaveOpen, CancellationToken cancellationToken)
{
VerifyCreateFromDirectoryArguments(sourceDirectoryName, destination);
cancellationToken.ThrowIfCancellationRequested();

TarWriter writer = new TarWriter(destination, TarEntryFormat.Pax, leaveOpen);
TarWriter writer = new TarWriter(destination, format, leaveOpen);
await using (writer.ConfigureAwait(false))
{
DirectoryInfo di = new(sourceDirectoryName);
Expand Down Expand Up @@ -534,5 +610,13 @@ private static void VerifyExtractToDirectoryArguments(Stream source, string dest
Debug.Assert(Path.IsPathFullyQualified(destinationDirectoryPath));
Debug.Assert(source.CanRead);
}

private static void ValidateTarEntryFormat(TarEntryFormat format)
{
if (format is not (TarEntryFormat.V7 or TarEntryFormat.Ustar or TarEntryFormat.Pax or TarEntryFormat.Gnu))
{
throw new ArgumentOutOfRangeException(nameof(format));
}
}
}
}
Loading
Loading