// This test would not pass for the V7 and Ustar formats in some OSs like MacCatalyst, tvOSSimulator and OSX, because the TempDirectory gets created in // a folder with a path longer than 100 bytes, and those tar formats have no way of handling pathnames and linknames longer than that length. // The rest of the OSs create the TempDirectory in a path that does not surpass the 100 bytes, so the 'subfolder' parameter gives a chance to extend // the base directory past that length, to ensure this scenario is tested everywhere. private async Task Extract_LinkEntry_TargetInsideDirectory_Internal_Async(TarEntryType entryType, TarEntryFormat format, string subfolder) { using (TempDirectory root = new TempDirectory()) { string baseDir = string.IsNullOrEmpty(subfolder) ? root.Path : Path.Join(root.Path, subfolder); Directory.CreateDirectory(baseDir); string linkName = "link"; string targetName = "target"; string targetPath = Path.Join(baseDir, targetName); File.Create(targetPath).Dispose(); await using (MemoryStream archive = new MemoryStream()) { await using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true)) { TarEntry entry = InvokeTarEntryCreationConstructor(format, entryType, linkName); entry.LinkName = targetPath; await writer.WriteEntryAsync(entry); } archive.Seek(0, SeekOrigin.Begin); await TarFile.ExtractToDirectoryAsync(archive, baseDir, overwriteFiles : false); Assert.Equal(2, Directory.GetFileSystemEntries(baseDir).Count()); } } }
// Writes the current header as a GNU entry into the archive stream. internal void WriteAsGnuInternal(Stream archiveStream, Span <byte> buffer) { // Unused GNU fields: offset, longnames, unused, sparse struct, isextended and realsize // If this header came from another archive, it will have a value // If it was constructed by the user, it will be an empty array _gnuUnusedBytes ??= new byte[FieldLengths.AllGnuUnused]; long actualLength = GetTotalDataBytesToWrite(); TarEntryType actualEntryType = GetCorrectTypeFlagForFormat(TarEntryFormat.Gnu); int checksum = WriteName(buffer, out _); checksum += WriteCommonFields(buffer, actualLength, actualEntryType); checksum += WriteGnuMagicAndVersion(buffer); checksum += WritePosixAndGnuSharedFields(buffer); checksum += WriteGnuFields(buffer); WriteChecksum(checksum, buffer); archiveStream.Write(buffer); if (_dataStream != null) { WriteData(archiveStream, _dataStream, actualLength); } }
public async Task Write_LongName_And_LongLinkName_Async(TarEntryType entryType) { // Both the Name and LinkName fields in header only fit 100 bytes string longName = new string('a', 101); string longLinkName = new string('a', 101); await using (MemoryStream archiveStream = new MemoryStream()) { await using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Gnu, leaveOpen: true)) { GnuTarEntry entry = new GnuTarEntry(entryType, longName); entry.LinkName = longLinkName; await writer.WriteEntryAsync(entry); } archiveStream.Position = 0; await using (TarReader reader = new TarReader(archiveStream)) { GnuTarEntry entry = await reader.GetNextEntryAsync() as GnuTarEntry; Assert.Equal(entryType, entry.EntryType); Assert.Equal(longName, entry.Name); Assert.Equal(longLinkName, entry.LinkName); } } }
// Constructor called when creating a new 'TarEntry*' instance that can be passed to a TarWriter. internal TarEntry(TarEntryType entryType, string entryName, TarFormat format) { ArgumentException.ThrowIfNullOrEmpty(entryName); // Throws if format is unknown or out of range TarHelpers.VerifyEntryTypeIsSupported(entryType, format, forWriting: false); _readerOfOrigin = null; _header = default; _header._extendedAttributes = new Dictionary <string, string>(); _header._name = entryName; _header._linkName = string.Empty; _header._typeFlag = entryType; _header._mode = (int)TarHelpers.DefaultMode; _header._gName = string.Empty; _header._uName = string.Empty; DateTimeOffset now = DateTimeOffset.Now; _header._mTime = now; _header._aTime = now; _header._cTime = now; }
// Constructor called when the user creates a TarEntry instance from scratch. internal PosixTarEntry(TarEntryType entryType, string entryName, TarEntryFormat format, bool isGea) : base(entryType, entryName, format, isGea) { _header._uName = string.Empty; _header._gName = string.Empty; _header._devMajor = 0; _header._devMinor = 0; }
/// <summary> /// Initializes a new <see cref="PaxTarEntry"/> instance with the specified entry type, entry name, and the default extended attributes. /// </summary> /// <param name="entryType">The type of the entry.</param> /// <param name="entryName">A string with the path and file name of this entry.</param> /// <exception cref="ArgumentException"><paramref name="entryName"/> is null or empty.</exception> /// <exception cref="InvalidOperationException">The entry type is not supported for creating an entry.</exception> /// <remarks><para>When creating an instance using the <see cref="PaxTarEntry(TarEntryType, string)"/> constructor, only the following entry types are supported:</para> /// <list type="bullet"> /// <item>In all platforms: <see cref="TarEntryType.Directory"/>, <see cref="TarEntryType.HardLink"/>, <see cref="TarEntryType.SymbolicLink"/>, <see cref="TarEntryType.RegularFile"/>.</item> /// <item>In Unix platforms only: <see cref="TarEntryType.BlockDevice"/>, <see cref="TarEntryType.CharacterDevice"/> and <see cref="TarEntryType.Fifo"/>.</item> /// </list> /// <para>Use the <see cref="PaxTarEntry(TarEntryType, string, IEnumerable{KeyValuePair{string, string}})"/> constructor to include additional extended attributes when creating the entry.</para> /// <para>The following entries are always found in the Extended Attributes dictionary of any PAX entry:</para> /// <list type="bullet"> /// <item>Modification time, under the name <c>mtime</c>, as a <see cref="double"/> number.</item> /// <item>Access time, under the name <c>atime</c>, as a <see cref="double"/> number.</item> /// <item>Change time, under the name <c>ctime</c>, as a <see cref="double"/> number.</item> /// <item>Path, under the name <c>path</c>, as a string.</item> /// </list> /// <para>The following entries are only found in the Extended Attributes dictionary of a PAX entry if certain conditions are met:</para> /// <list type="bullet"> /// <item>Group name, under the name <c>gname</c>, as a string, if it is larger than 32 bytes.</item> /// <item>User name, under the name <c>uname</c>, as a string, if it is larger than 32 bytes.</item> /// <item>File length, under the name <c>size</c>, as an <see cref="int"/>, if the string representation of the number is larger than 12 bytes.</item> /// </list> /// </remarks> public PaxTarEntry(TarEntryType entryType, string entryName) : base(entryType, entryName, TarEntryFormat.Pax, isGea: false) { _header._prefix = string.Empty; Debug.Assert(_header._mTime != default); AddNewAccessAndChangeTimestampsIfNotExist(useMTime: true); }
// Constructor called when creating an entry with default common fields. internal TarHeader(TarEntryFormat format, string name = "", int mode = 0, DateTimeOffset mTime = default, TarEntryType typeFlag = TarEntryType.RegularFile) { _format = format; _name = name; _mode = mode; _mTime = mTime; _typeFlag = typeFlag; _magic = GetMagicForFormat(format); _version = GetVersionForFormat(format); }
protected void VerifyCommonRegularFile(TarEntry regularFile, bool isFromWriter, bool isV7RegularFile = false) { Assert.NotNull(regularFile); TarEntryType entryType = isV7RegularFile ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; Assert.Equal(entryType, regularFile.EntryType); VerifyCommonProperties(regularFile); VerifyUnsupportedLinkProperty(regularFile); VerifyDataStream(regularFile, isFromWriter); }
public async Task Extract_SpecialFiles_Async(TarEntryFormat format, TarEntryType entryType) { using TempDirectory root = new TempDirectory(); (string entryName, string destination, PosixTarEntry entry) = Prepare_Extract_SpecialFiles(root, format, entryType); await entry.ExtractToFileAsync(destination, overwrite : true); Verify_Extract_SpecialFiles(destination, entry, entryType); }
public void Extract(TarEntryFormat format, TarEntryType entryType) { using TempDirectory root = new TempDirectory(); (string entryName, string destination, TarEntry entry) = Prepare_Extract(root, format, entryType); entry.ExtractToFile(destination, overwrite: true); Verify_Extract(destination, entry, entryType); }
// Constructor called when creating an entry using the common fields from another entry. // The *TarEntry constructor calling this should take care of setting any format-specific fields. internal TarHeader(TarEntryFormat format, TarEntryType typeFlag, TarHeader other) : this(format, other._name, other._mode, other._mTime, typeFlag) { _uid = other._uid; _gid = other._gid; _size = other._size; _checksum = other._checksum; _linkName = other._linkName; _dataStream = other._dataStream; }
// Creates and returns a GNU long metadata header, with the specified long text written into its data stream. private static TarHeader GetGnuLongMetadataHeader(TarEntryType entryType, string longText) { TarHeader longMetadataHeader = GetDefaultGnuLongMetadataHeader(longText.Length, entryType); Debug.Assert(longMetadataHeader._dataStream != null); longMetadataHeader._dataStream.Write(Encoding.UTF8.GetBytes(longText)); longMetadataHeader._dataStream.Seek(0, SeekOrigin.Begin); // Ensure it gets written into the archive from the beginning return(longMetadataHeader); }
protected void SetCommonRegularFile(TarEntry regularFile, bool isV7RegularFile = false) { Assert.NotNull(regularFile); TarEntryType entryType = isV7RegularFile ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; Assert.Equal(entryType, regularFile.EntryType); SetCommonProperties(regularFile); // Data stream Assert.Null(regularFile.DataStream); }
/// <summary> /// Initializes a new <see cref="PaxTarEntry"/> instance with the specified entry type, entry name and Extended Attributes enumeration. /// </summary> /// <param name="entryType">The type of the entry.</param> /// <param name="entryName">A string with the path and file name of this entry.</param> /// <param name="extendedAttributes">An enumeration of string key-value pairs that represents the metadata to include in the Extended Attributes entry that precedes the current entry.</param> /// <exception cref="ArgumentNullException"><paramref name="extendedAttributes"/> is <see langword="null"/>.</exception> /// <exception cref="ArgumentException"><paramref name="entryName"/> is null or empty.</exception> /// <exception cref="InvalidOperationException">The entry type is not supported for creating an entry.</exception> /// <remarks>When creating an instance using the <see cref="PaxTarEntry(TarEntryType, string)"/> constructor, only the following entry types are supported: /// <list type="bullet"> /// <item>In all platforms: <see cref="TarEntryType.Directory"/>, <see cref="TarEntryType.HardLink"/>, <see cref="TarEntryType.SymbolicLink"/>, <see cref="TarEntryType.RegularFile"/>.</item> /// <item>In Unix platforms only: <see cref="TarEntryType.BlockDevice"/>, <see cref="TarEntryType.CharacterDevice"/> and <see cref="TarEntryType.Fifo"/>.</item> /// </list> /// The specified <paramref name="extendedAttributes"/> get appended to the default attributes, unless the specified enumeration overrides any of them. /// <para>The following entries are always found in the Extended Attributes dictionary of any PAX entry:</para> /// <list type="bullet"> /// <item>Modification time, under the name <c>mtime</c>, as a <see cref="double"/> number.</item> /// <item>Access time, under the name <c>atime</c>, as a <see cref="double"/> number.</item> /// <item>Change time, under the name <c>ctime</c>, as a <see cref="double"/> number.</item> /// <item>Path, under the name <c>path</c>, as a string.</item> /// </list> /// <para>The following entries are only found in the Extended Attributes dictionary of a PAX entry if certain conditions are met:</para> /// <list type="bullet"> /// <item>Group name, under the name <c>gname</c>, as a string, if it is larger than 32 bytes.</item> /// <item>User name, under the name <c>uname</c>, as a string, if it is larger than 32 bytes.</item> /// <item>File length, under the name <c>size</c>, as an <see cref="int"/>, if the string representation of the number is larger than 12 bytes.</item> /// </list> /// </remarks> public PaxTarEntry(TarEntryType entryType, string entryName, IEnumerable <KeyValuePair <string, string> > extendedAttributes) : base(entryType, entryName, TarEntryFormat.Pax, isGea: false) { ArgumentNullException.ThrowIfNull(extendedAttributes); _header._prefix = string.Empty; _header._extendedAttributes = new Dictionary <string, string>(extendedAttributes); Debug.Assert(_header._mTime != default); AddNewAccessAndChangeTimestampsIfNotExist(useMTime: true); }
// Writes the V7 header fields to the specified buffer, calculates and writes the checksum, then returns the final data length. private long WriteV7FieldsToBuffer(Span <byte> buffer) { long actualLength = GetTotalDataBytesToWrite(); TarEntryType actualEntryType = TarHelpers.GetCorrectTypeFlagForFormat(TarEntryFormat.V7, _typeFlag); int tmpChecksum = WriteName(buffer, out _); tmpChecksum += WriteCommonFields(buffer, actualLength, actualEntryType); _checksum = WriteChecksum(tmpChecksum, buffer); return(actualLength); }
/// <summary> /// Parsing a tar header from the <code>BaseStream</code>. /// And set properties to this instance. /// </summary> /// <returns></returns> /// <exception cref="TarHeaderParsingException"></exception> private void Parse() { bool isCompleted = false; TarEntryType currentType = TarEntryType.Incompleted; Dump("Start Parsing"); do { switch (currentType) { case TarEntryType.GNU_LongName: Dump("Parsing GNU_LongName"); Name = ParseString(ReadHeader((int)Size), 0, (int)Size); currentType = TarEntryType.Incompleted; break; case TarEntryType.GNU_LongLink: Dump("Parsing GNU_LongLink"); LinkName = ParseString(ReadHeader((int)Size), 0, (int)Size); currentType = TarEntryType.Incompleted; break; case TarEntryType.Incompleted: currentType = ParseStandardHeader(); break; } switch (currentType) { case TarEntryType.Incompleted: case TarEntryType.GNU_LongName: case TarEntryType.GNU_LongLink: isCompleted = false; break; case TarEntryType.Unkown: throw new TarHeaderParsingException( string.Format("Unkown tar header type: {0}", Type)); case TarEntryType.EndOfEntry: Type = currentType; isCompleted = true; return; default: isCompleted = true; break; } } while (!isCompleted); Dump("End Parsing"); Dump(ToString()); }
private TarEntry GetFirstEntry(MemoryStream dataStream, TarEntryType entryType, TarEntryFormat format) { TarEntry firstEntry = InvokeTarEntryCreationConstructor(format, entryType, "file.txt"); firstEntry.Gid = TestGid; firstEntry.Uid = TestUid; firstEntry.Mode = TestMode; // Modification Time is set to 'DateTimeOffset.UtcNow' in the constructor if (entryType is TarEntryType.V7RegularFile or TarEntryType.RegularFile) { firstEntry.DataStream = dataStream; }
/// <summary> /// Read the next entry in the tar file. /// </summary> /// <param name="filePath"></param> /// <returns>the data for the entry. Returns null if there are no more entries</returns> public byte[] ReadEntry(out string filePath, out TarEntryType entryType) { filePath = String.Empty; entryType = TarEntryType.TYPE_UNKNOWN; TarHeader header = ReadHeader(); if (null == header) return null; entryType = header.EntryType; filePath = header.FilePath; return ReadData(header.FileSize); }
// Writes the Ustar header fields to the specified buffer, calculates and writes the checksum, then returns the final data length. private long WriteUstarFieldsToBuffer(Span <byte> buffer) { long actualLength = GetTotalDataBytesToWrite(); TarEntryType actualEntryType = TarHelpers.GetCorrectTypeFlagForFormat(TarEntryFormat.Ustar, _typeFlag); int tmpChecksum = WritePosixName(buffer); tmpChecksum += WriteCommonFields(buffer, actualLength, actualEntryType); tmpChecksum += WritePosixMagicAndVersion(buffer); tmpChecksum += WritePosixAndGnuSharedFields(buffer); _checksum = WriteChecksum(tmpChecksum, buffer); return(actualLength); }
public void Read_Archive_Many_Small_Files(TarFormat format, TestTarFormat testFormat) { string testCaseName = "many_small_files"; using MemoryStream ms = GetTarMemoryStream(CompressionMethod.Uncompressed, testFormat, testCaseName); using TarReader reader = new TarReader(ms); if (testFormat == TestTarFormat.pax_gea) { // The GEA are collected after reading the first entry, not on the constructor Assert.Null(reader.GlobalExtendedAttributes); } // Format is determined after reading the first entry, not on the constructor Assert.Equal(TarFormat.Unknown, reader.Format); List <TarEntry> entries = new List <TarEntry>(); TarEntry entry; bool isFirstEntry = true; while ((entry = reader.GetNextEntry()) != null) { if (isFirstEntry) { Assert.Equal(format, reader.Format); if (testFormat == TestTarFormat.pax_gea) { Assert.NotNull(reader.GlobalExtendedAttributes); Assert.True(reader.GlobalExtendedAttributes.Any()); Assert.Contains(AssetPaxGeaKey, reader.GlobalExtendedAttributes); Assert.Equal(AssetPaxGeaValue, reader.GlobalExtendedAttributes[AssetPaxGeaKey]); } isFirstEntry = false; } entries.Add(entry); } int directoriesCount = entries.Count(e => e.EntryType == TarEntryType.Directory); Assert.Equal(10, directoriesCount); TarEntryType regularFileEntryType = format == TarFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; for (int i = 0; i < 10; i++) { int filesCount = entries.Count(e => e.EntryType == regularFileEntryType && e.Name.StartsWith($"{i}/")); Assert.Equal(10, filesCount); } }
// Asynchronously creates and returns a GNU long metadata header, with the specified long text written into its data stream. private static async Task <TarHeader> GetGnuLongMetadataHeaderAsync(TarEntryType entryType, string longText, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); TarHeader longMetadataHeader = GetDefaultGnuLongMetadataHeader(longText.Length, entryType); Debug.Assert(longMetadataHeader._dataStream != null); await longMetadataHeader._dataStream.WriteAsync(Encoding.UTF8.GetBytes(longText), cancellationToken).ConfigureAwait(false); longMetadataHeader._dataStream.Seek(0, SeekOrigin.Begin); // Ensure it gets written into the archive from the beginning return(longMetadataHeader); }
// Constructor called when the user creates a TarEntry instance from scratch. internal TarEntry(TarEntryType entryType, string entryName, TarEntryFormat format, bool isGea) { ArgumentException.ThrowIfNullOrEmpty(entryName); Debug.Assert(!isGea || entryType is TarEntryType.GlobalExtendedAttributes); if (!isGea) { TarHelpers.ThrowIfEntryTypeNotSupported(entryType, format); } // Default values for fields shared by all supported formats _header = new TarHeader(format, entryName, TarHelpers.GetDefaultMode(entryType), DateTimeOffset.UtcNow, entryType); }
protected void TestConstructionConversion( TarEntryType originalEntryType, TarEntryFormat firstFormat, TarEntryFormat formatToConvert) { DateTimeOffset now = DateTimeOffset.UtcNow; using MemoryStream dataStream = new MemoryStream(); TarEntryType actualEntryType = GetTarEntryTypeForTarEntryFormat(originalEntryType, firstFormat); TarEntry firstEntry = GetFirstEntry(dataStream, actualEntryType, firstFormat); TarEntry otherEntry = ConvertAndVerifyEntry(firstEntry, originalEntryType, formatToConvert, now); }
// Constructor called when converting an entry to the selected format. internal TarEntry(TarEntry other, TarEntryFormat format) { if (other is PaxGlobalExtendedAttributesTarEntry) { throw new InvalidOperationException(SR.TarCannotConvertPaxGlobalExtendedAttributesEntry); } TarEntryType compatibleEntryType = TarHelpers.GetCorrectTypeFlagForFormat(format, other.EntryType); TarHelpers.ThrowIfEntryTypeNotSupported(compatibleEntryType, format); _readerOfOrigin = other._readerOfOrigin; _header = new TarHeader(format, compatibleEntryType, other._header); }
/// <summary> /// Read the next entry in the tar file. /// </summary> /// <param name="filePath"></param> /// <returns>the data for the entry. Returns null if there are no more entries</returns> public byte[] ReadEntry(out string filePath, out TarEntryType entryType) { filePath = String.Empty; entryType = TarEntryType.TYPE_UNKNOWN; TarHeader header = ReadHeader(); if (null == header) { return(null); } entryType = header.EntryType; filePath = header.FilePath; return(ReadData(header.FileSize)); }
private TarEntryType ParseStandardHeader() { var type = TarEntryType.Incompleted; var buffer = ReadHeader(); if (buffer[0] == 0) // reached end of tar data. { Dump("this block is the end of tar data."); return(TarEntryType.EndOfEntry); } var offset = 0; if (!Type.HasFlag(TarEntryType.GNU_LongName)) { Name = ParseString(buffer, offset, NAME_LENGTH); } offset += NAME_LENGTH; Permission = (int)ParseOctet(buffer, offset, MODE_LENGTH); offset += MODE_LENGTH; Uid = (int)ParseOctet(buffer, offset, UID_LENGTH); offset += UID_LENGTH; Gid = (int)ParseOctet(buffer, offset, GID_LENGTH); offset += GID_LENGTH; Size = ParseOctet(buffer, offset, SIZE_LENGTH); offset += SIZE_LENGTH; Mtime = Epoch2Date(ParseOctet(buffer, offset, MTIME_ELNGTH)); offset += MTIME_ELNGTH; Checksum = (int)ParseOctet(buffer, offset, CHECKSUM_LENGTH); offset += CHECKSUM_LENGTH; type = GetEntryType(buffer[offset++]); Type |= type; if (!Type.HasFlag(TarEntryType.GNU_LongLink)) { LinkName = ParseString(buffer, offset, LINKNAME_LENGTH); } offset += LINKNAME_LENGTH; Magic = ParseString(buffer, offset, MAGIC_LENGTH).Trim(); offset += MAGIC_LENGTH; Version = ParseString(buffer, offset, VERSION_LENGTH); offset += VERSION_LENGTH; Uname = ParseString(buffer, offset, UNAME_LENGTH); offset += UNAME_LENGTH; Gname = ParseString(buffer, offset, GNAME_LENGTH); offset += GNAME_LENGTH; return(type); }
public void ExtractToFile_Link_Throws(TarEntryType entryType) { using TempDirectory root = new TempDirectory(); string fileName = "mylink"; string fullPath = Path.Join(root.Path, fileName); string linkTarget = PlatformDetection.IsWindows ? @"C:\Windows\system32\notepad.exe" : "/usr/bin/nano"; UstarTarEntry entry = new UstarTarEntry(entryType, fileName); entry.LinkName = linkTarget; Assert.Throws <InvalidOperationException>(() => entry.ExtractToFile(fileName, overwrite: false)); Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count()); }
protected void TestConstructionConversionBackAndForth( TarEntryType originalEntryType, TarEntryFormat firstAndLastFormat, TarEntryFormat formatToConvert) { DateTimeOffset now = DateTimeOffset.UtcNow; using MemoryStream dataStream = new MemoryStream(); TarEntryType firstAndLastEntryType = GetTarEntryTypeForTarEntryFormat(originalEntryType, firstAndLastFormat); TarEntry firstEntry = GetFirstEntry(dataStream, firstAndLastEntryType, firstAndLastFormat); TarEntry otherEntry = ConvertAndVerifyEntry(firstEntry, originalEntryType, formatToConvert, now); // First conversion DateTimeOffset secondNow = DateTimeOffset.UtcNow; ConvertAndVerifyEntry(otherEntry, firstAndLastEntryType, firstAndLastFormat, secondNow); // Convert back to original format }
private void Verify_Archive_RegularFile(TarEntry file, TarFormat format, IReadOnlyDictionary <string, string> gea, string expectedFileName, string expectedContents) { Assert.NotNull(file); Assert.True(file.Checksum > 0); Assert.NotNull(file.DataStream); Assert.True(file.DataStream.Length > 0); Assert.True(file.DataStream.CanRead); Assert.True(file.DataStream.CanSeek); file.DataStream.Seek(0, SeekOrigin.Begin); using (StreamReader reader = new StreamReader(file.DataStream, leaveOpen: true)) { string contents = reader.ReadLine(); Assert.Equal(expectedContents, contents); } TarEntryType expectedEntryType = format == TarFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; Assert.Equal(expectedEntryType, file.EntryType); Assert.Equal(AssetGid, file.Gid); Assert.Equal(file.Length, file.DataStream.Length); Assert.Equal(DefaultLinkName, file.LinkName); Assert.Equal(AssetMode, file.Mode); Assert.True(file.ModificationTime > DateTimeOffset.UnixEpoch); Assert.Equal(expectedFileName, file.Name); Assert.Equal(AssetUid, file.Uid); if (file is PosixTarEntry posix) { Assert.Equal(DefaultDeviceMajor, posix.DeviceMajor); Assert.Equal(DefaultDeviceMinor, posix.DeviceMinor); Assert.Equal(AssetGName, posix.GroupName); Assert.Equal(AssetUName, posix.UserName); if (posix is PaxTarEntry pax) { VerifyAssetExtendedAttributes(pax, gea); } else if (posix is GnuTarEntry gnu) { Assert.True(gnu.AccessTime >= DateTimeOffset.UnixEpoch); Assert.True(gnu.ChangeTime >= DateTimeOffset.UnixEpoch); } } }
public async Task Add_File_Async(TarEntryFormat format) { using (TempDirectory root = new TempDirectory()) { string fileName = "file.txt"; string filePath = Path.Join(root.Path, fileName); string fileContents = "Hello world"; using (StreamWriter streamWriter = File.CreateText(filePath)) { streamWriter.Write(fileContents); } await using (MemoryStream archive = new MemoryStream()) { await using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true)) { await writer.WriteEntryAsync(fileName : filePath, entryName : fileName); } archive.Seek(0, SeekOrigin.Begin); await using (TarReader reader = new TarReader(archive)) { TarEntry entry = await reader.GetNextEntryAsync(); Assert.NotNull(entry); Assert.Equal(format, entry.Format); Assert.Equal(fileName, entry.Name); TarEntryType expectedEntryType = format is TarEntryFormat.V7 ? TarEntryType.V7RegularFile : TarEntryType.RegularFile; Assert.Equal(expectedEntryType, entry.EntryType); Assert.True(entry.Length > 0); Assert.NotNull(entry.DataStream); entry.DataStream.Seek(0, SeekOrigin.Begin); using StreamReader dataReader = new StreamReader(entry.DataStream); string dataContents = dataReader.ReadLine(); Assert.Equal(fileContents, dataContents); VerifyPlatformSpecificMetadata(filePath, entry); Assert.Null(await reader.GetNextEntryAsync()); } } } }
public async Task ExtractToFile_Link_Throws_Async(TarEntryFormat format, TarEntryType entryType) { using (TempDirectory root = new TempDirectory()) { string fileName = "mylink"; string fullPath = Path.Join(root.Path, fileName); string linkTarget = PlatformDetection.IsWindows ? @"C:\Windows\system32\notepad.exe" : "/usr/bin/nano"; TarEntry entry = InvokeTarEntryCreationConstructor(format, entryType, fileName); entry.LinkName = linkTarget; await Assert.ThrowsAsync <InvalidOperationException>(() => entry.ExtractToFileAsync(fileName, overwrite: false)); Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count()); } }