public async void Write_To_UnseekableStream_Async() { await using (MemoryStream inner = new MemoryStream()) { await using (WrappedStream wrapped = new WrappedStream(inner, canRead: true, canWrite: true, canSeek: false)) { await using (TarWriter writer = new TarWriter(wrapped, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry paxEntry = new PaxTarEntry(TarEntryType.RegularFile, "file.txt"); await writer.WriteEntryAsync(paxEntry); } // The final records should get written, and the length should not be set because position cannot be read inner.Seek(0, SeekOrigin.Begin); // Rewind the base stream (wrapped cannot be rewound) await using (TarReader reader = new TarReader(wrapped)) { TarEntry entry = await reader.GetNextEntryAsync(); Assert.Equal(TarEntryFormat.Pax, entry.Format); Assert.Equal(TarEntryType.RegularFile, entry.EntryType); Assert.Null(await reader.GetNextEntryAsync()); } } } }
public async Task WritePaxAttributes_Timestamps_UserProvided_Async() { Dictionary <string, string> extendedAttributes = new(); extendedAttributes.Add(PaxEaATime, GetTimestampStringFromDateTimeOffset(TestAccessTime)); extendedAttributes.Add(PaxEaCTime, GetTimestampStringFromDateTimeOffset(TestChangeTime)); await using (MemoryStream archiveStream = new MemoryStream()) { await using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName, extendedAttributes); regularFile.ModificationTime = TestModificationTime; await writer.WriteEntryAsync(regularFile); } archiveStream.Position = 0; await using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry regularFile = await reader.GetNextEntryAsync() as PaxTarEntry; AssertExtensions.GreaterThanOrEqualTo(regularFile.ExtendedAttributes.Count, 4); VerifyExtendedAttributeTimestamp(regularFile, PaxEaMTime, TestModificationTime); VerifyExtendedAttributeTimestamp(regularFile, PaxEaATime, TestAccessTime); VerifyExtendedAttributeTimestamp(regularFile, PaxEaCTime, TestChangeTime); } } }
public async Task UnixFileModes_RestrictiveParentDir_Async() { using TempDirectory source = new TempDirectory(); using TempDirectory destination = new TempDirectory(); string archivePath = Path.Join(source.Path, "archive.tar"); using FileStream archiveStream = File.Create(archivePath); using (TarWriter writer = new TarWriter(archiveStream)) { PaxTarEntry dir = new PaxTarEntry(TarEntryType.Directory, "dir"); dir.Mode = UnixFileMode.None; // Restrict permissions. writer.WriteEntry(dir); PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "dir/file"); file.Mode = TestPermission1; writer.WriteEntry(file); } await TarFile.ExtractToDirectoryAsync(archivePath, destination.Path, overwriteFiles : false); string dirPath = Path.Join(destination.Path, "dir"); Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist."); AssertFileModeEquals(dirPath, UnixFileMode.None); // Set dir permissions so we can access file. SetUnixFileMode(dirPath, UserAll); string filePath = Path.Join(dirPath, "file"); Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); AssertFileModeEquals(filePath, TestPermission1); }
public void WritePaxAttributes_Timestamps() { Dictionary <string, string> extendedAttributes = new(); extendedAttributes.Add("atime", ConvertDateTimeOffsetToDouble(TestAccessTime).ToString("F6", CultureInfo.InvariantCulture)); extendedAttributes.Add("ctime", ConvertDateTimeOffsetToDouble(TestChangeTime).ToString("F6", CultureInfo.InvariantCulture)); using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName, extendedAttributes); SetRegularFile(regularFile); VerifyRegularFile(regularFile, isWritable: true); writer.WriteEntry(regularFile); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry regularFile = reader.GetNextEntry() as PaxTarEntry; VerifyRegularFile(regularFile, isWritable: false); Assert.NotNull(regularFile.ExtendedAttributes); Assert.True(regularFile.ExtendedAttributes.Count >= 4); Assert.Contains("path", regularFile.ExtendedAttributes); VerifyExtendedAttributeTimestamp(regularFile, "mtime", TestModificationTime); VerifyExtendedAttributeTimestamp(regularFile, "atime", TestAccessTime); VerifyExtendedAttributeTimestamp(regularFile, "ctime", TestChangeTime); } }
public void SupportedEntryType_CharacterDevice() { PaxTarEntry characterDevice = new PaxTarEntry(TarEntryType.CharacterDevice, InitialEntryName); SetCharacterDevice(characterDevice); VerifyCharacterDevice(characterDevice); }
public void SupportedEntryType_Directory() { PaxTarEntry directory = new PaxTarEntry(TarEntryType.Directory, InitialEntryName); SetDirectory(directory); VerifyDirectory(directory); }
public void SupportedEntryType_SymbolicLink() { PaxTarEntry symbolicLink = new PaxTarEntry(TarEntryType.SymbolicLink, InitialEntryName); SetSymbolicLink(symbolicLink); VerifySymbolicLink(symbolicLink); }
public void SupportedEntryType_HardLink() { PaxTarEntry hardLink = new PaxTarEntry(TarEntryType.HardLink, InitialEntryName); SetHardLink(hardLink); VerifyHardLink(hardLink); }
public void SupportedEntryType_Fifo() { PaxTarEntry fifo = new PaxTarEntry(TarEntryType.Fifo, InitialEntryName); SetFifo(fifo); VerifyFifo(fifo); }
public void Constructor_ConversionFromPax_From_UnseekableTarReader(TarEntryFormat writerFormat) { using MemoryStream source = GetTarMemoryStream(CompressionMethod.Uncompressed, TestTarFormat.pax, "file"); using WrappedStream wrappedSource = new WrappedStream(source, canRead: true, canWrite: false, canSeek: false); using TarReader sourceReader = new TarReader(wrappedSource, leaveOpen: true); PaxTarEntry paxEntry = sourceReader.GetNextEntry(copyData: false) as PaxTarEntry; V7TarEntry v7Entry = new V7TarEntry(other: paxEntry); // Convert, and avoid advancing wrappedSource position using MemoryStream destination = new MemoryStream(); using (TarWriter writer = new TarWriter(destination, writerFormat, leaveOpen: true)) { writer.WriteEntry(v7Entry); // Write DataStream exactly where the wrappedSource position was left } destination.Position = 0; // Rewind using (TarReader destinationReader = new TarReader(destination, leaveOpen: false)) { V7TarEntry resultEntry = destinationReader.GetNextEntry() as V7TarEntry; Assert.NotNull(resultEntry); using (StreamReader streamReader = new StreamReader(resultEntry.DataStream)) { Assert.Equal("Hello file", streamReader.ReadToEnd()); } } }
public void SupportedEntryType_BlockDevice() { PaxTarEntry blockDevice = new PaxTarEntry(TarEntryType.BlockDevice, InitialEntryName); SetBlockDevice(blockDevice); VerifyBlockDevice(blockDevice); }
public async Task Extract_UnseekableStream_BlockAlignmentPadding_DoesNotAffectNextEntries_Async(int contentSize) { byte[] fileContents = new byte[contentSize]; Array.Fill <byte>(fileContents, 0x1); using var archive = new MemoryStream(); using (var compressor = new GZipStream(archive, CompressionMode.Compress, leaveOpen: true)) { using var writer = new TarWriter(compressor); var entry1 = new PaxTarEntry(TarEntryType.RegularFile, "file"); entry1.DataStream = new MemoryStream(fileContents); await writer.WriteEntryAsync(entry1); var entry2 = new PaxTarEntry(TarEntryType.RegularFile, "next-file"); await writer.WriteEntryAsync(entry2); } archive.Position = 0; using var decompressor = new GZipStream(archive, CompressionMode.Decompress); using var reader = new TarReader(decompressor); using TempDirectory destination = new TempDirectory(); await TarFile.ExtractToDirectoryAsync(decompressor, destination.Path, overwriteFiles : true); Assert.Equal(2, Directory.GetFileSystemEntries(destination.Path, "*", SearchOption.AllDirectories).Count()); }
public void SupportedEntryType_RegularFile() { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); SetRegularFile(regularFile); VerifyRegularFile(regularFile, isWritable: true); }
public void Extract_AllSegmentsOfPath() { using TempDirectory source = new TempDirectory(); using TempDirectory destination = new TempDirectory(); string archivePath = Path.Join(source.Path, "archive.tar"); using FileStream archiveStream = File.Create(archivePath); using (TarWriter writer = new TarWriter(archiveStream)) { PaxTarEntry segment1 = new PaxTarEntry(TarEntryType.Directory, "segment1"); writer.WriteEntry(segment1); PaxTarEntry segment2 = new PaxTarEntry(TarEntryType.Directory, "segment1/segment2"); writer.WriteEntry(segment2); PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "segment1/segment2/file.txt"); writer.WriteEntry(file); } TarFile.ExtractToDirectory(archivePath, destination.Path, overwriteFiles: false); string segment1Path = Path.Join(destination.Path, "segment1"); Assert.True(Directory.Exists(segment1Path), $"{segment1Path}' does not exist."); string segment2Path = Path.Join(segment1Path, "segment2"); Assert.True(Directory.Exists(segment2Path), $"{segment2Path}' does not exist."); string filePath = Path.Join(segment2Path, "file.txt"); Assert.True(File.Exists(filePath), $"{filePath}' does not exist."); }
protected void VerifyExtendedAttributeTimestamps(PaxTarEntry pax) { Assert.NotNull(pax.ExtendedAttributes); AssertExtensions.GreaterThanOrEqualTo(pax.ExtendedAttributes.Count, 3); // Expect to at least collect mtime, ctime and atime VerifyExtendedAttributeTimestamp(pax, PaxEaMTime, MinimumTime); VerifyExtendedAttributeTimestamp(pax, PaxEaATime, MinimumTime); VerifyExtendedAttributeTimestamp(pax, PaxEaCTime, MinimumTime); }
public async Task WriteEntry_AfterDispose_Throws_Async() { using MemoryStream archiveStream = new MemoryStream(); TarWriter writer = new TarWriter(archiveStream); await writer.DisposeAsync(); PaxTarEntry entry = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); await Assert.ThrowsAsync <ObjectDisposedException>(() => writer.WriteEntryAsync(entry)); }
// The fixed size fields for mtime, atime and ctime can fit 12 ASCII characters, but the last character is reserved for an ASCII space. // We internally use long to represent the seconds since Unix epoch, not int. // If the max allowed value is 77,777,777,777 in octal, then the max allowed seconds since the Unix epoch are 8,589,934,591, // which represents the date "2242/03/16 12:56:32 +00:00". // Pax should survive after this date because it stores the timestamps in the extended attributes dictionary // without size restrictions. public async Task WriteTimestampsBeyondOctalLimitInPax_Async() { DateTimeOffset overLimitTimestamp = new DateTimeOffset(2242, 3, 16, 12, 56, 33, TimeSpan.Zero); // One second past the octal limit string strOverLimitTimestamp = GetTimestampStringFromDateTimeOffset(overLimitTimestamp); Dictionary <string, string> ea = new Dictionary <string, string>() { { PaxEaATime, strOverLimitTimestamp }, { PaxEaCTime, strOverLimitTimestamp } }; PaxTarEntry entry = new PaxTarEntry(TarEntryType.Directory, "dir", ea); entry.ModificationTime = overLimitTimestamp; Assert.Equal(overLimitTimestamp, entry.ModificationTime); Assert.Contains(PaxEaATime, entry.ExtendedAttributes); DateTimeOffset atime = GetDateTimeOffsetFromTimestampString(entry.ExtendedAttributes, PaxEaATime); Assert.Equal(overLimitTimestamp, atime); Assert.Contains(PaxEaCTime, entry.ExtendedAttributes); DateTimeOffset ctime = GetDateTimeOffsetFromTimestampString(entry.ExtendedAttributes, PaxEaCTime); Assert.Equal(overLimitTimestamp, ctime); using MemoryStream archiveStream = new MemoryStream(); TarWriter writer = new TarWriter(archiveStream, leaveOpen: true); await using (writer) { await writer.WriteEntryAsync(entry); } archiveStream.Position = 0; TarReader reader = new TarReader(archiveStream); await using (reader) { PaxTarEntry readEntry = await reader.GetNextEntryAsync() as PaxTarEntry; Assert.NotNull(readEntry); Assert.Equal(overLimitTimestamp, readEntry.ModificationTime); Assert.Contains(PaxEaATime, readEntry.ExtendedAttributes); DateTimeOffset actualATime = GetDateTimeOffsetFromTimestampString(readEntry.ExtendedAttributes, PaxEaATime); Assert.Equal(overLimitTimestamp, actualATime); Assert.Contains(PaxEaCTime, readEntry.ExtendedAttributes); DateTimeOffset actualCTime = GetDateTimeOffsetFromTimestampString(readEntry.ExtendedAttributes, PaxEaCTime); Assert.Equal(overLimitTimestamp, actualCTime); } }
// Y2K38 will happen one second after "2038/19/01 03:14:07 +00:00". This timestamp represents the seconds since the Unix epoch with a // value of int.MaxValue: 2,147,483,647. // The fixed size fields for mtime, atime and ctime can fit 12 ASCII characters, but the last character is reserved for an ASCII space. // All our entry types should survive the Epochalypse because we internally use long to represent the seconds since Unix epoch, not int. // So if the max allowed value is 77,777,777,777 in octal, then the max allowed seconds since the Unix epoch are 8,589,934,591, which // is way past int MaxValue, but still within the long limits. That number represents the date "2242/16/03 12:56:32 +00:00". public async Task WriteTimestampsBeyondEpochalypseInPax_Async() { DateTimeOffset epochalypse = new DateTimeOffset(2038, 1, 19, 3, 14, 8, TimeSpan.Zero); string strEpochalypse = GetTimestampStringFromDateTimeOffset(epochalypse); Dictionary <string, string> ea = new Dictionary <string, string>() { { PaxEaATime, strEpochalypse }, { PaxEaCTime, strEpochalypse } }; PaxTarEntry entry = new PaxTarEntry(TarEntryType.Directory, "dir", ea); entry.ModificationTime = epochalypse; Assert.Equal(epochalypse, entry.ModificationTime); Assert.Contains(PaxEaATime, entry.ExtendedAttributes); DateTimeOffset atime = GetDateTimeOffsetFromTimestampString(entry.ExtendedAttributes, PaxEaATime); Assert.Equal(epochalypse, atime); Assert.Contains(PaxEaCTime, entry.ExtendedAttributes); DateTimeOffset ctime = GetDateTimeOffsetFromTimestampString(entry.ExtendedAttributes, PaxEaCTime); Assert.Equal(epochalypse, ctime); using MemoryStream archiveStream = new MemoryStream(); TarWriter writer = new TarWriter(archiveStream, leaveOpen: true); await using (writer) { await writer.WriteEntryAsync(entry); } archiveStream.Position = 0; TarReader reader = new TarReader(archiveStream); await using (reader) { PaxTarEntry readEntry = await reader.GetNextEntryAsync() as PaxTarEntry; Assert.NotNull(readEntry); Assert.Equal(epochalypse, readEntry.ModificationTime); Assert.Contains(PaxEaATime, readEntry.ExtendedAttributes); DateTimeOffset actualATime = GetDateTimeOffsetFromTimestampString(readEntry.ExtendedAttributes, PaxEaATime); Assert.Equal(epochalypse, actualATime); Assert.Contains(PaxEaCTime, readEntry.ExtendedAttributes); DateTimeOffset actualCTime = GetDateTimeOffsetFromTimestampString(readEntry.ExtendedAttributes, PaxEaCTime); Assert.Equal(epochalypse, actualCTime); } }
public void WriteEntry_AfterDispose_Throws() { using MemoryStream archiveStream = new MemoryStream(); TarWriter writer = new TarWriter(archiveStream); writer.Dispose(); PaxTarEntry entry = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); Assert.Throws <ObjectDisposedException>(() => writer.WriteEntry(entry)); }
public async Task WritePaxAttributes_LongGroupName_LongUserName_Async() { string userName = "******"; string groupName = "IAmAGroupNameWhoseLengthIsWayBeyondTheThirtyTwoByteLimit"; using MemoryStream archiveStream = new MemoryStream(); TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true); await using (writer) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); SetRegularFile(regularFile); VerifyRegularFile(regularFile, isWritable: true); regularFile.UserName = userName; regularFile.GroupName = groupName; await writer.WriteEntryAsync(regularFile); } archiveStream.Position = 0; TarReader reader = new TarReader(archiveStream); await using (reader) { PaxTarEntry regularFile = await reader.GetNextEntryAsync() as PaxTarEntry; VerifyRegularFile(regularFile, isWritable: false); Assert.NotNull(regularFile.ExtendedAttributes); // path, mtime, atime and ctime are always collected by default AssertExtensions.GreaterThanOrEqualTo(regularFile.ExtendedAttributes.Count, 6); Assert.Contains(PaxEaName, regularFile.ExtendedAttributes); Assert.Contains(PaxEaMTime, regularFile.ExtendedAttributes); Assert.Contains(PaxEaATime, regularFile.ExtendedAttributes); Assert.Contains(PaxEaCTime, regularFile.ExtendedAttributes); Assert.Contains(PaxEaUName, regularFile.ExtendedAttributes); Assert.Equal(userName, regularFile.ExtendedAttributes[PaxEaUName]); Assert.Contains(PaxEaGName, regularFile.ExtendedAttributes); Assert.Equal(groupName, regularFile.ExtendedAttributes[PaxEaGName]); // They should also get exposed via the regular properties Assert.Equal(groupName, regularFile.GroupName); Assert.Equal(userName, regularFile.UserName); } }
public void Constructor_Name_FullPath_DestinationDirectory_Mismatch_Throws() { using TempDirectory root = new TempDirectory(); string fullPath = Path.Join(Path.GetPathRoot(root.Path), "dir", "file.txt"); PaxTarEntry entry = new PaxTarEntry(TarEntryType.RegularFile, fullPath); entry.DataStream = new MemoryStream(); entry.DataStream.Write(new byte[] { 0x1 }); entry.DataStream.Seek(0, SeekOrigin.Begin); Assert.Throws <IOException>(() => entry.ExtractToFile(root.Path, overwrite: false)); Assert.False(File.Exists(fullPath)); }
public void Constructor_Name_FullPath_DestinationDirectory_Match() { using TempDirectory root = new TempDirectory(); string fullPath = Path.Join(root.Path, "file.txt"); PaxTarEntry entry = new PaxTarEntry(TarEntryType.RegularFile, fullPath); entry.DataStream = new MemoryStream(); entry.DataStream.Write(new byte[] { 0x1 }); entry.DataStream.Seek(0, SeekOrigin.Begin); entry.ExtractToFile(fullPath, overwrite: false); Assert.True(File.Exists(fullPath)); }
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"; PaxTarEntry entry = new PaxTarEntry(entryType, fileName); entry.LinkName = linkTarget; Assert.Throws <InvalidOperationException>(() => entry.ExtractToFile(fileName, overwrite: false)); Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count()); }
public void WriteSymbolicLink() { using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry symbolicLink = new PaxTarEntry(TarEntryType.SymbolicLink, InitialEntryName); SetSymbolicLink(symbolicLink); VerifySymbolicLink(symbolicLink); writer.WriteEntry(symbolicLink); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry symbolicLink = reader.GetNextEntry() as PaxTarEntry; VerifySymbolicLink(symbolicLink); } }
public void WriteBlockDevice() { using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry blockDevice = new PaxTarEntry(TarEntryType.BlockDevice, InitialEntryName); SetBlockDevice(blockDevice); VerifyBlockDevice(blockDevice); writer.WriteEntry(blockDevice); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry blockDevice = reader.GetNextEntry() as PaxTarEntry; VerifyBlockDevice(blockDevice); } }
public void WriteFifo() { using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry fifo = new PaxTarEntry(TarEntryType.Fifo, InitialEntryName); SetFifo(fifo); VerifyFifo(fifo); writer.WriteEntry(fifo); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry fifo = reader.GetNextEntry() as PaxTarEntry; VerifyFifo(fifo); } }
public void WriteDirectory() { using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry directory = new PaxTarEntry(TarEntryType.Directory, InitialEntryName); SetDirectory(directory); VerifyDirectory(directory); writer.WriteEntry(directory); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry directory = reader.GetNextEntry() as PaxTarEntry; VerifyDirectory(directory); } }
public void WriteRegularFile() { using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); SetRegularFile(regularFile); VerifyRegularFile(regularFile, isWritable: true); writer.WriteEntry(regularFile); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry regularFile = reader.GetNextEntry() as PaxTarEntry; VerifyRegularFile(regularFile, isWritable: false); } }
public async Task WritePaxAttributes_Name_AutomaticallyAdded_Async() { using MemoryStream archiveStream = new MemoryStream(); await using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); await writer.WriteEntryAsync(regularFile); } archiveStream.Position = 0; await using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry regularFile = await reader.GetNextEntryAsync() as PaxTarEntry; AssertExtensions.GreaterThanOrEqualTo(regularFile.ExtendedAttributes.Count, 4); Assert.Contains(PaxEaName, regularFile.ExtendedAttributes); } }
public void WritePaxAttributes_LongGroupName_LongUserName() { string userName = "******"; string groupName = "IAmAGroupNameWhoseLengthIsWayBeyondTheThirtyTwoByteLimit"; using MemoryStream archiveStream = new MemoryStream(); using (TarWriter writer = new TarWriter(archiveStream, TarEntryFormat.Pax, leaveOpen: true)) { PaxTarEntry regularFile = new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName); SetRegularFile(regularFile); VerifyRegularFile(regularFile, isWritable: true); regularFile.UserName = userName; regularFile.GroupName = groupName; writer.WriteEntry(regularFile); } archiveStream.Position = 0; using (TarReader reader = new TarReader(archiveStream)) { PaxTarEntry regularFile = reader.GetNextEntry() as PaxTarEntry; VerifyRegularFile(regularFile, isWritable: false); Assert.NotNull(regularFile.ExtendedAttributes); // path, mtime, atime and ctime are always collected by default Assert.True(regularFile.ExtendedAttributes.Count >= 6); Assert.Contains("path", regularFile.ExtendedAttributes); Assert.Contains("mtime", regularFile.ExtendedAttributes); Assert.Contains("atime", regularFile.ExtendedAttributes); Assert.Contains("ctime", regularFile.ExtendedAttributes); Assert.Contains("uname", regularFile.ExtendedAttributes); Assert.Equal(userName, regularFile.ExtendedAttributes["uname"]); Assert.Contains("gname", regularFile.ExtendedAttributes); Assert.Equal(groupName, regularFile.ExtendedAttributes["gname"]); // They should also get exposed via the regular properties Assert.Equal(groupName, regularFile.GroupName); Assert.Equal(userName, regularFile.UserName); } }