// 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());
                }
            }
        }
        public async Task Extract_Archive_File_OverwriteTrue_Async()
        {
            string testCaseName = "file";
            string archivePath  = GetTarFilePath(CompressionMethod.Uncompressed, TestTarFormat.pax, testCaseName);

            using (TempDirectory destination = new TempDirectory())
            {
                string filePath = Path.Join(destination.Path, "file.txt");
                using (FileStream fileStream = File.Create(filePath))
                {
                    using StreamWriter writer = new StreamWriter(fileStream, leaveOpen: false);
                    writer.WriteLine("Original text");
                }

                await TarFile.ExtractToDirectoryAsync(archivePath, destination.Path, overwriteFiles : true);

                Assert.True(File.Exists(filePath));

                using (FileStream fileStream = File.Open(filePath, FileMode.Open))
                {
                    using StreamReader reader = new StreamReader(fileStream);
                    string actualContents = reader.ReadLine();
                    Assert.Equal($"Hello {testCaseName}", actualContents); // Confirm overwrite
                }
            }
        }
        public async Task ExtractEntry_ManySubfolderSegments_NoPrecedingDirectoryEntries_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                string firstSegment        = "a";
                string secondSegment       = Path.Join(firstSegment, "b");
                string fileWithTwoSegments = Path.Join(secondSegment, "c.txt");

                await using (MemoryStream archive = new MemoryStream())
                {
                    await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true))
                    {
                        // No preceding directory entries for the segments
                        UstarTarEntry entry = new UstarTarEntry(TarEntryType.RegularFile, fileWithTwoSegments);

                        entry.DataStream = new MemoryStream();
                        entry.DataStream.Write(new byte[] { 0x1 });
                        entry.DataStream.Seek(0, SeekOrigin.Begin);

                        await writer.WriteEntryAsync(entry);
                    }

                    archive.Seek(0, SeekOrigin.Begin);
                    await TarFile.ExtractToDirectoryAsync(archive, root.Path, overwriteFiles : false);

                    Assert.True(Directory.Exists(Path.Join(root.Path, firstSegment)));
                    Assert.True(Directory.Exists(Path.Join(root.Path, secondSegment)));
                    Assert.True(File.Exists(Path.Join(root.Path, fileWithTwoSegments)));
                }
            }
        }
        public async Task SetsLastModifiedTimeOnExtractedFiles()
        {
            using TempDirectory root = new TempDirectory();

            string inDir  = Path.Join(root.Path, "indir");
            string inFile = Path.Join(inDir, "file");

            string tarFile = Path.Join(root.Path, "file.tar");

            string outDir  = Path.Join(root.Path, "outdir");
            string outFile = Path.Join(outDir, "file");

            Directory.CreateDirectory(inDir);
            File.Create(inFile).Dispose();
            var dt = new DateTime(2001, 1, 2, 3, 4, 5, DateTimeKind.Local);

            File.SetLastWriteTime(inFile, dt);

            await TarFile.CreateFromDirectoryAsync(sourceDirectoryName : inDir, destinationFileName : tarFile, includeBaseDirectory : false);

            Directory.CreateDirectory(outDir);
            await TarFile.ExtractToDirectoryAsync(sourceFileName : tarFile, destinationDirectoryName : outDir, overwriteFiles : false);

            Assert.True(File.Exists(outFile));
            Assert.InRange(File.GetLastWriteTime(outFile).Ticks, dt.AddSeconds(-3).Ticks, dt.AddSeconds(3).Ticks); // include some slop for filesystem granularity
        }
        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 Task ExtractToDirectoryAsync_Cancel()
        {
            CancellationTokenSource cs = new CancellationTokenSource();

            cs.Cancel();
            return(Assert.ThrowsAsync <TaskCanceledException>(() => TarFile.ExtractToDirectoryAsync("file.tar", "directory", overwriteFiles: true, cs.Token)));
        }
        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 async Task InvalidPath_Throws_Async()
        {
            using (MemoryStream archive = new MemoryStream())
            {
                await Assert.ThrowsAsync <ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: null, overwriteFiles: false));

                await Assert.ThrowsAsync <ArgumentException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: string.Empty, overwriteFiles: false));
            }
        }
        public async Task ExtractToDirectoryAsync_Cancel()
        {
            CancellationTokenSource cs = new CancellationTokenSource();

            cs.Cancel();
            using (MemoryStream archiveStream = new MemoryStream())
            {
                await Assert.ThrowsAsync <TaskCanceledException>(() => TarFile.ExtractToDirectoryAsync(archiveStream, "directory", overwriteFiles: true, cs.Token));
            }
        }
        public async Task InvalidPaths_Throw()
        {
            await Assert.ThrowsAsync <ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(sourceFileName: null, destinationDirectoryName: "path", overwriteFiles: false));

            await Assert.ThrowsAsync <ArgumentException>(() => TarFile.ExtractToDirectoryAsync(sourceFileName: string.Empty, destinationDirectoryName: "path", overwriteFiles: false));

            await Assert.ThrowsAsync <ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(sourceFileName: "path", destinationDirectoryName: null, overwriteFiles: false));

            await Assert.ThrowsAsync <ArgumentException>(() => TarFile.ExtractToDirectoryAsync(sourceFileName: "path", destinationDirectoryName: string.Empty, overwriteFiles: false));
        }
 public async Task UnreadableStream_Throws_Async()
 {
     using (MemoryStream archive = new MemoryStream())
     {
         using (WrappedStream unreadable = new WrappedStream(archive, canRead: false, canWrite: true, canSeek: true))
         {
             await Assert.ThrowsAsync <IOException>(() => TarFile.ExtractToDirectoryAsync(unreadable, destinationDirectoryName: "path", overwriteFiles: false));
         }
     }
 }
Esempio n. 12
0
        public async Task TarGz_TarFile_CreateFromDir_ExtractToDir_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                string archivePath = Path.Join(root.Path, "compressed.tar.gz");

                string sourceDirectory = Path.Join(root.Path, "source");
                Directory.CreateDirectory(sourceDirectory);

                string destinationDirectory = Path.Join(root.Path, "destination");
                Directory.CreateDirectory(destinationDirectory);

                string fileName = "file.txt";
                string filePath = Path.Join(sourceDirectory, fileName);
                File.Create(filePath).Dispose();

                FileStreamOptions createOptions = new()
                {
                    Mode    = FileMode.CreateNew,
                    Access  = FileAccess.Write,
                    Options = FileOptions.Asynchronous
                };

                await using (FileStream streamToCompress = File.Open(archivePath, createOptions))
                {
                    await using (GZipStream compressorStream = new GZipStream(streamToCompress, CompressionMode.Compress))
                    {
                        await TarFile.CreateFromDirectoryAsync(sourceDirectory, compressorStream, includeBaseDirectory : false);
                    }
                }

                FileInfo fileInfo = new FileInfo(archivePath);
                Assert.True(fileInfo.Exists);
                Assert.True(fileInfo.Length > 0);

                FileStreamOptions readOptions = new()
                {
                    Mode    = FileMode.Open,
                    Access  = FileAccess.Read,
                    Options = FileOptions.Asynchronous
                };

                await using (FileStream streamToDecompress = File.Open(archivePath, readOptions))
                {
                    await using (GZipStream decompressorStream = new GZipStream(streamToDecompress, CompressionMode.Decompress))
                    {
                        await TarFile.ExtractToDirectoryAsync(decompressorStream, destinationDirectory, overwriteFiles : true);

                        Assert.True(File.Exists(filePath));
                    }
                }
            }
        }
    }
}
        public async Task NonExistentDirectory_Throws_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                string filePath = Path.Join(root.Path, "file.tar");
                string dirPath  = Path.Join(root.Path, "dir");

                File.Create(filePath).Dispose();

                await Assert.ThrowsAsync <DirectoryNotFoundException>(() => TarFile.ExtractToDirectoryAsync(sourceFileName: filePath, destinationDirectoryName: dirPath, overwriteFiles: false));
            }
        }
        public async Task NonExistentDirectory_Throws_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                string dirPath = Path.Join(root.Path, "dir");

                using (MemoryStream archive = new MemoryStream())
                {
                    await Assert.ThrowsAsync <DirectoryNotFoundException>(() => TarFile.ExtractToDirectoryAsync(archive, destinationDirectoryName: dirPath, overwriteFiles: false));
                }
            }
        }
        public async Task Extract_Archive_File_OverwriteFalse_Async()
        {
            using (TempDirectory destination = new TempDirectory())
            {
                string sourceArchiveFileName = GetTarFilePath(CompressionMethod.Uncompressed, TestTarFormat.pax, "file");

                string filePath = Path.Join(destination.Path, "file.txt");

                File.Create(filePath).Dispose();

                await Assert.ThrowsAsync <IOException>(() => TarFile.ExtractToDirectoryAsync(sourceArchiveFileName, destination.Path, overwriteFiles: false));
            }
        }
        public async Task Extract_Archive_File_Async(TestTarFormat testFormat)
        {
            string sourceArchiveFileName = GetTarFilePath(CompressionMethod.Uncompressed, testFormat, "file");

            using (TempDirectory destination = new TempDirectory())
            {
                string filePath = Path.Join(destination.Path, "file.txt");

                await TarFile.ExtractToDirectoryAsync(sourceArchiveFileName, destination.Path, overwriteFiles : false);

                Assert.True(File.Exists(filePath));
            }
        }
Esempio n. 17
0
        public async Task Extract_SpecialFiles_Unix_Unelevated_ThrowsUnauthorizedAccess_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                string originalFileName = GetTarFilePath(CompressionMethod.Uncompressed, TestTarFormat.ustar, "specialfiles");

                string archive     = Path.Join(root.Path, "input.tar");
                string destination = Path.Join(root.Path, "dir");

                // Copying the tar to reduce the chance of other tests failing due to being used by another process
                File.Copy(originalFileName, archive);

                Directory.CreateDirectory(destination);

                await Assert.ThrowsAsync <UnauthorizedAccessException>(() => TarFile.ExtractToDirectoryAsync(archive, destination, overwriteFiles: false));

                Assert.Equal(0, Directory.GetFileSystemEntries(destination).Count());
            }
        }
        public async Task Extract_AllSegmentsOfPath_Async()
        {
            using (TempDirectory source = new TempDirectory())
            {
                string archivePath = Path.Join(source.Path, "archive.tar");
                using (TempDirectory destination = new TempDirectory())
                {
                    FileStreamOptions fileOptions = new()
                    {
                        Access  = FileAccess.Write,
                        Mode    = FileMode.CreateNew,
                        Share   = FileShare.None,
                        Options = FileOptions.Asynchronous
                    };

                    await using (FileStream archiveStream = new FileStream(archivePath, fileOptions))
                    {
                        await using (TarWriter writer = new TarWriter(archiveStream))
                        {
                            PaxTarEntry segment1 = new PaxTarEntry(TarEntryType.Directory, "segment1");
                            await writer.WriteEntryAsync(segment1);

                            PaxTarEntry segment2 = new PaxTarEntry(TarEntryType.Directory, "segment1/segment2");
                            await writer.WriteEntryAsync(segment2);

                            PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "segment1/segment2/file.txt");
                            await writer.WriteEntryAsync(file);
                        }
                    }

                    await TarFile.ExtractToDirectoryAsync(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.");
                }
            }
        }
        public async Task Extract_LinkEntry_TargetOutsideDirectory_Async(TarEntryType entryType)
        {
            await using (MemoryStream archive = new MemoryStream())
            {
                await using (TarWriter writer = new TarWriter(archive, TarEntryFormat.Ustar, leaveOpen: true))
                {
                    UstarTarEntry entry = new UstarTarEntry(entryType, "link");
                    entry.LinkName = PlatformDetection.IsWindows ? @"C:\Windows\System32\notepad.exe" : "/usr/bin/nano";
                    await writer.WriteEntryAsync(entry);
                }

                archive.Seek(0, SeekOrigin.Begin);

                using (TempDirectory root = new TempDirectory())
                {
                    await Assert.ThrowsAsync <IOException>(() => TarFile.ExtractToDirectoryAsync(archive, root.Path, overwriteFiles: false));

                    Assert.Equal(0, Directory.GetFileSystemEntries(root.Path).Count());
                }
            }
        }
        public async Task ExtractArchiveWithEntriesThatStartWithSlashDotPrefix_Async()
        {
            using (TempDirectory root = new TempDirectory())
            {
                await using (MemoryStream archiveStream = GetStrangeTarMemoryStream("prefixDotSlashAndCurrentFolderEntry"))
                {
                    await TarFile.ExtractToDirectoryAsync(archiveStream, root.Path, overwriteFiles : true);

                    archiveStream.Position = 0;

                    await using (TarReader reader = new TarReader(archiveStream, leaveOpen: false))
                    {
                        TarEntry entry;
                        while ((entry = await reader.GetNextEntryAsync()) != null)
                        {
                            // Normalize the path (remove redundant segments), remove trailing separators
                            // this is so the first entry can be skipped if it's the same as the root directory
                            string entryPath = Path.TrimEndingDirectorySeparator(Path.GetFullPath(Path.Join(root.Path, entry.Name)));
                            Assert.True(Path.Exists(entryPath), $"Entry was not extracted: {entryPath}");
                        }
                    }
                }
            }
        }
        public async Task UnixFileModes_Async(bool overwrite)
        {
            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 = TestPermission1;
                writer.WriteEntry(dir);

                PaxTarEntry file = new PaxTarEntry(TarEntryType.RegularFile, "file");
                file.Mode = TestPermission2;
                writer.WriteEntry(file);

                // Archive has no entry for missing_parent.
                PaxTarEntry missingParentDir = new PaxTarEntry(TarEntryType.Directory, "missing_parent/dir");
                missingParentDir.Mode = TestPermission3;
                writer.WriteEntry(missingParentDir);

                // out_of_order_parent/file entry comes before out_of_order_parent entry.
                PaxTarEntry outOfOrderFile = new PaxTarEntry(TarEntryType.RegularFile, "out_of_order_parent/file");
                writer.WriteEntry(outOfOrderFile);

                PaxTarEntry outOfOrderDir = new PaxTarEntry(TarEntryType.Directory, "out_of_order_parent");
                outOfOrderDir.Mode = TestPermission4;
                writer.WriteEntry(outOfOrderDir);
            }

            string dirPath              = Path.Join(destination.Path, "dir");
            string filePath             = Path.Join(destination.Path, "file");
            string missingParentPath    = Path.Join(destination.Path, "missing_parent");
            string missingParentDirPath = Path.Join(missingParentPath, "dir");
            string outOfOrderDirPath    = Path.Join(destination.Path, "out_of_order_parent");

            if (overwrite)
            {
                File.OpenWrite(filePath).Dispose();
                Directory.CreateDirectory(dirPath);
                Directory.CreateDirectory(missingParentDirPath);
                Directory.CreateDirectory(outOfOrderDirPath);
            }

            await TarFile.ExtractToDirectoryAsync(archivePath, destination.Path, overwriteFiles : overwrite);

            Assert.True(Directory.Exists(dirPath), $"{dirPath}' does not exist.");
            AssertFileModeEquals(dirPath, TestPermission1);

            Assert.True(File.Exists(filePath), $"{filePath}' does not exist.");
            AssertFileModeEquals(filePath, TestPermission2);

            // Missing parents are created with CreateDirectoryDefaultMode.
            Assert.True(Directory.Exists(missingParentPath), $"{missingParentPath}' does not exist.");
            AssertFileModeEquals(missingParentPath, CreateDirectoryDefaultMode);

            Assert.True(Directory.Exists(missingParentDirPath), $"{missingParentDirPath}' does not exist.");
            AssertFileModeEquals(missingParentDirPath, TestPermission3);

            // Directory modes that are out-of-order are still applied.
            Assert.True(Directory.Exists(outOfOrderDirPath), $"{outOfOrderDirPath}' does not exist.");
            AssertFileModeEquals(outOfOrderDirPath, TestPermission4);
        }
 public Task NullStream_Throws_Async() =>
 Assert.ThrowsAsync <ArgumentNullException>(() => TarFile.ExtractToDirectoryAsync(source: null, destinationDirectoryName: "path", overwriteFiles: false));