public void Read_Archive_LongPath_Over255(TarFormat format, TestTarFormat testFormat) { string testCaseName = "longpath_over255"; 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); TarEntry directory = reader.GetNextEntry(); 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]); } Verify_Archive_Directory(directory, reader.GlobalExtendedAttributes, "000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555/"); TarEntry file = reader.GetNextEntry(); Verify_Archive_RegularFile(file, format, reader.GlobalExtendedAttributes, "000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555/00000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445.txt", $"Hello {testCaseName}"); Assert.Null(reader.GetNextEntry()); }
public void Read_Archive_File_HardLink(TarFormat format, TestTarFormat testFormat) { string testCaseName = "file_hardlink"; 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); TarEntry file = reader.GetNextEntry(); 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]); } Verify_Archive_RegularFile(file, format, reader.GlobalExtendedAttributes, "file.txt", $"Hello {testCaseName}"); TarEntry hardLink = reader.GetNextEntry(); // The 'tar' tool detects hardlinks as regular files and saves them as such in the archives, for all formats Verify_Archive_RegularFile(hardLink, format, reader.GlobalExtendedAttributes, "hardlink.txt", $"Hello {testCaseName}"); Assert.Null(reader.GetNextEntry()); }
public void Write_RegularFileEntry_As_V7RegularFileEntry(TarFormat entryFormat) { using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, archiveFormat: TarFormat.V7, leaveOpen: true)) { TarEntry entry = entryFormat switch { TarFormat.Ustar => new UstarTarEntry(TarEntryType.RegularFile, InitialEntryName), TarFormat.Pax => new PaxTarEntry(TarEntryType.RegularFile, InitialEntryName), TarFormat.Gnu => new GnuTarEntry(TarEntryType.RegularFile, InitialEntryName), _ => throw new FormatException() }; // Should be written as V7RegularFile writer.WriteEntry(entry); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { TarEntry entry = reader.GetNextEntry(); Assert.True(entry is V7TarEntry); Assert.Equal(TarEntryType.V7RegularFile, entry.EntryType); Assert.Null(reader.GetNextEntry()); } }
// 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; }
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); } }
// When writing an entry that came from an archive of a different format, if its entry type happens to // be an incompatible regular file entry type, convert it to the compatible one. // No change for all other entry types. private TarEntryType GetCorrectTypeFlagForFormat(TarFormat format) { if (format is TarFormat.V7) { if (_typeFlag is TarEntryType.RegularFile) { return(TarEntryType.V7RegularFile); } } else if (_typeFlag is TarEntryType.V7RegularFile) { return(TarEntryType.RegularFile); } return(_typeFlag); }
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 void Add_BlockDevice(TarFormat format) { RemoteExecutor.Invoke((string strFormat) => { TarFormat expectedFormat = Enum.Parse <TarFormat>(strFormat); using TempDirectory root = new TempDirectory(); string blockDevicePath = Path.Join(root.Path, AssetBlockDeviceFileName); // Creating device files needs elevation Interop.CheckIo(Interop.Sys.CreateBlockDevice(blockDevicePath, (int)DefaultMode, TestBlockDeviceMajor, TestBlockDeviceMinor)); using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, expectedFormat, leaveOpen: true)) { writer.WriteEntry(fileName: blockDevicePath, entryName: AssetBlockDeviceFileName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); PosixTarEntry entry = reader.GetNextEntry() as PosixTarEntry; Assert.Equal(expectedFormat, reader.Format); Assert.NotNull(entry); Assert.Equal(AssetBlockDeviceFileName, entry.Name); Assert.Equal(DefaultLinkName, entry.LinkName); Assert.Equal(TarEntryType.BlockDevice, entry.EntryType); Assert.Null(entry.DataStream); VerifyPlatformSpecificMetadata(blockDevicePath, entry); // TODO: Fix how these values are collected, the numbers don't match even though https://github.com/dotnet/runtime/issues/68230 // they come from stat's dev and from the major/minor syscalls // Assert.Equal(TestBlockDeviceMajor, entry.DeviceMajor); // Assert.Equal(TestBlockDeviceMinor, entry.DeviceMinor); Assert.Null(reader.GetNextEntry()); } }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); }
public void Add_File(TarFormat 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); } using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true)) { writer.WriteEntry(fileName: filePath, entryName: fileName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); TarEntry entry = reader.GetNextEntry(); Assert.NotNull(entry); Assert.Equal(format, reader.Format); Assert.Equal(fileName, entry.Name); TarEntryType expectedEntryType = format is TarFormat.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(reader.GetNextEntry()); } }
public void Add_BlockDevice(TarFormat format) { RemoteExecutor.Invoke((string strFormat) => { TarFormat expectedFormat = Enum.Parse <TarFormat>(strFormat); using TempDirectory root = new TempDirectory(); string blockDevicePath = Path.Join(root.Path, AssetBlockDeviceFileName); // Creating device files needs elevation Interop.CheckIo(Interop.Sys.CreateBlockDevice(blockDevicePath, (int)DefaultMode, TestBlockDeviceMajor, TestBlockDeviceMinor)); using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, expectedFormat, leaveOpen: true)) { writer.WriteEntry(fileName: blockDevicePath, entryName: AssetBlockDeviceFileName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); PosixTarEntry entry = reader.GetNextEntry() as PosixTarEntry; Assert.Equal(expectedFormat, reader.Format); Assert.NotNull(entry); Assert.Equal(AssetBlockDeviceFileName, entry.Name); Assert.Equal(DefaultLinkName, entry.LinkName); Assert.Equal(TarEntryType.BlockDevice, entry.EntryType); Assert.Null(entry.DataStream); VerifyPlatformSpecificMetadata(blockDevicePath, entry); Assert.Equal(TestBlockDeviceMajor, entry.DeviceMajor); Assert.Equal(TestBlockDeviceMinor, entry.DeviceMinor); Assert.Null(reader.GetNextEntry()); } }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); }
public void Read_Archive_FolderSymbolicLink_Folder_Subfolder_File(TarFormat format, TestTarFormat testFormat) { string testCaseName = "foldersymlink_folder_subfolder_file"; 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); TarEntry childlink = reader.GetNextEntry(); 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]); } Verify_Archive_SymbolicLink(childlink, reader.GlobalExtendedAttributes, "childlink", "parent/child"); TarEntry parent = reader.GetNextEntry(); Verify_Archive_Directory(parent, reader.GlobalExtendedAttributes, "parent/"); TarEntry child = reader.GetNextEntry(); Verify_Archive_Directory(child, reader.GlobalExtendedAttributes, "parent/child/"); TarEntry file = reader.GetNextEntry(); Verify_Archive_RegularFile(file, format, reader.GlobalExtendedAttributes, "parent/child/file.txt", $"Hello {testCaseName}"); Assert.Null(reader.GetNextEntry()); }
public void Add_SymbolicLink(TarFormat format, bool createTarget) { using TempDirectory root = new TempDirectory(); string targetName = "file.txt"; string linkName = "link.txt"; string targetPath = Path.Join(root.Path, targetName); string linkPath = Path.Join(root.Path, linkName); if (createTarget) { File.Create(targetPath).Dispose(); } FileInfo linkInfo = new FileInfo(linkPath); linkInfo.CreateAsSymbolicLink(targetName); using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true)) { writer.WriteEntry(fileName: linkPath, entryName: linkName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); TarEntry entry = reader.GetNextEntry(); Assert.Equal(format, reader.Format); Assert.NotNull(entry); Assert.Equal(linkName, entry.Name); Assert.Equal(targetName, entry.LinkName); Assert.Equal(TarEntryType.SymbolicLink, entry.EntryType); Assert.Null(entry.DataStream); VerifyPlatformSpecificMetadata(linkPath, entry); Assert.Null(reader.GetNextEntry()); } }
public void Add_Fifo(TarFormat format) { RemoteExecutor.Invoke((string strFormat) => { TarFormat expectedFormat = Enum.Parse <TarFormat>(strFormat); using TempDirectory root = new TempDirectory(); string fifoName = "fifofile"; string fifoPath = Path.Join(root.Path, fifoName); Interop.CheckIo(Interop.Sys.MkFifo(fifoPath, (int)DefaultMode)); using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, expectedFormat, leaveOpen: true)) { writer.WriteEntry(fileName: fifoPath, entryName: fifoName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); PosixTarEntry entry = reader.GetNextEntry() as PosixTarEntry; Assert.Equal(expectedFormat, reader.Format); Assert.NotNull(entry); Assert.Equal(fifoName, entry.Name); Assert.Equal(DefaultLinkName, entry.LinkName); Assert.Equal(TarEntryType.Fifo, entry.EntryType); Assert.Null(entry.DataStream); VerifyPlatformSpecificMetadata(fifoPath, entry); Assert.Null(reader.GetNextEntry()); } }, format.ToString(), new RemoteInvokeOptions { RunAsSudo = true }).Dispose(); }
/// <summary> /// Initializes a <see cref="TarWriter"/> instance that can write tar entries to the specified stream, optionally leave the stream open upon disposal of this instance, and can specify the format of the underlying archive. /// </summary> /// <param name="archiveStream">The stream to write to.</param> /// <param name="archiveFormat">The format of the archive.</param> /// <param name="leaveOpen"><see langword="false"/> to dispose the <paramref name="archiveStream"/> when this instance is disposed; <see langword="true"/> to leave the stream open.</param> /// <remarks><para>If the selected <paramref name="archiveFormat"/> is <see cref="TarFormat.Pax"/>, no Global Extended Attributes entry is written. To write a PAX archive with a Global Extended Attributes entry inserted at the beginning of the archive, use the <see cref="TarWriter(Stream, IEnumerable{KeyValuePair{string, string}}?, bool)"/> constructor instead.</para> /// <para>The recommended format is <see cref="TarFormat.Pax"/> for its flexibility.</para></remarks> /// <exception cref="ArgumentNullException"><paramref name="archiveStream"/> is <see langword="null"/>.</exception> /// <exception cref="IOException"><paramref name="archiveStream"/> is unwritable.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="archiveFormat"/> is either <see cref="TarFormat.Unknown"/>, or not one of the other enum values.</exception> public TarWriter(Stream archiveStream, TarFormat archiveFormat, bool leaveOpen = false) { ArgumentNullException.ThrowIfNull(archiveStream); if (!archiveStream.CanWrite) { throw new IOException(SR.IO_NotSupported_UnwritableStream); } if (archiveFormat is not TarFormat.V7 and not TarFormat.Ustar and not TarFormat.Pax and not TarFormat.Gnu) { throw new ArgumentOutOfRangeException(nameof(archiveFormat)); } _archiveStream = archiveStream; Format = archiveFormat; _leaveOpen = leaveOpen; _isDisposed = false; _wroteEntries = false; _wroteGEA = false; _globalExtendedAttributes = null; }
public void Add_Directory(TarFormat format, bool withContents) { using TempDirectory root = new TempDirectory(); string dirName = "dir"; string dirPath = Path.Join(root.Path, dirName); Directory.CreateDirectory(dirPath); if (withContents) { // Add a file inside the directory, we need to ensure the contents // of the directory are ignored when using AddFile File.Create(Path.Join(dirPath, "file.txt")).Dispose(); } using MemoryStream archive = new MemoryStream(); using (TarWriter writer = new TarWriter(archive, format, leaveOpen: true)) { writer.WriteEntry(fileName: dirPath, entryName: dirName); } archive.Seek(0, SeekOrigin.Begin); using (TarReader reader = new TarReader(archive)) { Assert.Equal(TarFormat.Unknown, reader.Format); TarEntry entry = reader.GetNextEntry(); Assert.Equal(format, reader.Format); Assert.NotNull(entry); Assert.Equal(dirName, entry.Name); Assert.Equal(TarEntryType.Directory, entry.EntryType); Assert.Null(entry.DataStream); VerifyPlatformSpecificMetadata(dirPath, entry); Assert.Null(reader.GetNextEntry()); // If the dir had contents, they should've been excluded } }
public void Read_Archive_SpecialFiles(TarFormat format, TestTarFormat testFormat) { string testCaseName = "specialfiles"; 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); PosixTarEntry blockDevice = reader.GetNextEntry() as PosixTarEntry; 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]); } Verify_Archive_BlockDevice(blockDevice, reader.GlobalExtendedAttributes, AssetBlockDeviceFileName); PosixTarEntry characterDevice = reader.GetNextEntry() as PosixTarEntry; Verify_Archive_CharacterDevice(characterDevice, reader.GlobalExtendedAttributes, AssetCharacterDeviceFileName); PosixTarEntry fifo = reader.GetNextEntry() as PosixTarEntry; Verify_Archive_Fifo(fifo, reader.GlobalExtendedAttributes, "fifofile"); Assert.Null(reader.GetNextEntry()); }
// Constructor called when creating a new 'TarEntry*' instance that can be passed to a TarWriter. internal PosixTarEntry(TarEntryType entryType, string entryName, TarFormat format) : base(entryType, entryName, format) { }