/// <summary> /// Extracts an an ISO file /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { using var cd = new CDReader(fileEntry.Content, true); var entries = cd.Root.GetFiles("*.*", SearchOption.AllDirectories); if (entries != null) { foreach (var file in entries) { var fileInfo = file; governor.CheckResourceGovernor(fileInfo.Length); Stream?stream = null; try { stream = fileInfo.OpenRead(); } catch (Exception e) { Logger.Debug("Failed to extract {0} from ISO {1}. ({2}:{3})", fileInfo.Name, fileEntry.FullPath, e.GetType(), e.Message); } if (stream != null) { var name = fileInfo.Name.Replace('/', Path.DirectorySeparatorChar); var newFileEntry = await FileEntry.FromStreamAsync(name, stream, fileEntry); var innerEntries = Context.ExtractAsync(newFileEntry, options, governor); await foreach (var entry in innerEntries) { yield return(entry); } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an a RAR archive /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { var rarArchive = GetRarArchive(fileEntry, options); if (rarArchive != null) { var entries = rarArchive.Entries.Where(x => x.IsComplete && !x.IsDirectory); if (options.Parallel) { var files = new ConcurrentStack <FileEntry>(); while (entries.Any()) { var batchSize = Math.Min(options.BatchSize, entries.Count()); var streams = entries.Take(batchSize).Select(entry => (entry, entry.OpenEntryStream())).ToList(); governor.CheckResourceGovernor(streams.Sum(x => x.Item2.Length)); streams.AsParallel().ForAll(streampair => { try { var newFileEntry = new FileEntry(streampair.entry.Key, streampair.Item2, fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); governor.CurrentOperationProcessedBytesLeft = -1; } else { files.PushRange(Context.Extract(newFileEntry, options, governor).ToArray()); } } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.RAR, fileEntry.FullPath, streampair.entry.Key, e.GetType()); } }); governor.CheckResourceGovernor(0); entries = entries.Skip(streams.Count); while (files.TryPop(out var result)) { if (result != null) { yield return(result); } } } } else { foreach (var entry in entries) { governor.CheckResourceGovernor(entry.Size); FileEntry?newFileEntry = null; try { var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); newFileEntry = new FileEntry(name, entry.OpenEntryStream(), fileEntry); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.RAR, fileEntry.FullPath, entry.Key, e.GetType()); } if (newFileEntry != null) { if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an a RAR archive /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { var rarArchive = GetRarArchive(fileEntry, options); if (rarArchive != null) { var entries = rarArchive.Entries.Where(x => x.IsComplete && !x.IsDirectory); foreach (var entry in entries) { governor.CheckResourceGovernor(entry.Size); var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); FileEntry newFileEntry = await FileEntry.FromStreamAsync(name, entry.OpenEntryStream(), fileEntry); if (newFileEntry != null) { if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } await foreach (var extractedFile in Context.ExtractAsync(newFileEntry, options, governor)) { yield return(extractedFile); } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Dump the FileEntries from a Logical Volume asynchronously /// </summary> /// <param name="volume">The Volume to dump</param> /// <param name="parentPath">The Path to the parent Disc</param> /// <param name="options">Extractor Options to use</param> /// <param name="governor">Resource Governor to use</param> /// <param name="Context">Extractor context to use</param> /// <param name="parent">The Parent FilEntry</param> /// <returns></returns> public static async IAsyncEnumerable <FileEntry> DumpLogicalVolumeAsync(LogicalVolumeInfo volume, string parentPath, ExtractorOptions options, ResourceGovernor governor, Extractor Context, FileEntry?parent = null) { DiscUtils.FileSystemInfo[]? fsInfos = null; try { fsInfos = FileSystemManager.DetectFileSystems(volume); } catch (Exception e) { Logger.Debug("Failed to get file systems from logical volume {0} Image {1} ({2}:{3})", volume.Identity, parentPath, e.GetType(), e.Message); } foreach (var fsInfo in fsInfos ?? Array.Empty <DiscUtils.FileSystemInfo>()) { using var fs = fsInfo.Open(volume); var diskFiles = fs.GetFiles(fs.Root.FullName, "*.*", SearchOption.AllDirectories).ToList(); foreach (var file in diskFiles) { Stream? fileStream = null; DiscFileInfo?fi = null; try { fi = fs.GetFileInfo(file); governor.CheckResourceGovernor(fi.Length); fileStream = fi.OpenRead(); } catch (Exception e) { Logger.Debug(e, "Failed to open {0} in volume {1}", file, volume.Identity); } if (fileStream != null && fi != null) { var newFileEntry = await FileEntry.FromStreamAsync($"{volume.Identity}{Path.DirectorySeparatorChar}{fi.FullName}", fileStream, parent); var entries = Context.ExtractAsync(newFileEntry, options, governor); await foreach (var entry in entries) { yield return(entry); } } } } }
/// <summary> /// Dump the FileEntries from a Logical Volume /// </summary> /// <param name="volume">The Volume to dump</param> /// <param name="parentPath">The Path to the parent Disc</param> /// <param name="options">Extractor Options to use</param> /// <param name="governor">Resource Governor to use</param> /// <param name="Context">Extractor context to use</param> /// <param name="parent">The Parent FilEntry</param> /// <returns></returns> public static IEnumerable <FileEntry> DumpLogicalVolume(LogicalVolumeInfo volume, string parentPath, ExtractorOptions options, ResourceGovernor governor, Extractor Context, FileEntry?parent = null) { DiscUtils.FileSystemInfo[]? fsInfos = null; try { fsInfos = FileSystemManager.DetectFileSystems(volume); } catch (Exception e) { Logger.Debug("Failed to get file systems from logical volume {0} Image {1} ({2}:{3})", volume.Identity, parentPath, e.GetType(), e.Message); } foreach (var fsInfo in fsInfos ?? Array.Empty <DiscUtils.FileSystemInfo>()) { using var fs = fsInfo.Open(volume); var diskFiles = fs.GetFiles(fs.Root.FullName, "*.*", SearchOption.AllDirectories).ToList(); if (options.Parallel) { var files = new ConcurrentStack <FileEntry>(); while (diskFiles.Any()) { var batchSize = Math.Min(options.BatchSize, diskFiles.Count); var range = diskFiles.GetRange(0, batchSize); var fileinfos = new List <(DiscFileInfo, Stream)>(); long totalLength = 0; foreach (var r in range) { try { var fi = fs.GetFileInfo(r); totalLength += fi.Length; fileinfos.Add((fi, fi.OpenRead())); } catch (Exception e) { Logger.Debug("Failed to get FileInfo from {0} in Volume {1} @ {2} ({3}:{4})", r, volume.Identity, parentPath, e.GetType(), e.Message); } } governor.CheckResourceGovernor(totalLength); fileinfos.AsParallel().ForAll(file => { if (file.Item2 != null) { var newFileEntry = new FileEntry($"{volume.Identity}{Path.DirectorySeparatorChar}{file.Item1.FullName}", file.Item2, parent); var entries = Context.ExtractFile(newFileEntry, options, governor); files.PushRange(entries.ToArray()); } }); diskFiles.RemoveRange(0, batchSize); while (files.TryPop(out var result)) { if (result != null) { yield return(result); } } } } else { foreach (var file in diskFiles) { Stream?fileStream = null; try { var fi = fs.GetFileInfo(file); governor.CheckResourceGovernor(fi.Length); fileStream = fi.OpenRead(); } catch (Exception e) { Logger.Debug(e, "Failed to open {0} in volume {1}", file, volume.Identity); } if (fileStream != null) { var newFileEntry = new FileEntry($"{volume.Identity}{Path.DirectorySeparatorChar}{file}", fileStream, parent); var entries = Context.ExtractFile(newFileEntry, options, governor); foreach (var entry in entries) { yield return(entry); } } } } } }
/// <summary> /// Extracts an archive file created with GNU ar /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> /// public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { await foreach (var entry in ArFile.GetFileEntriesAsync(fileEntry, options, governor)) { await foreach (var extractedFile in Context.ExtractAsync(entry, options, governor)) { yield return(extractedFile); } } }
/// Extracts an BZip2 file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { BZip2Stream?bzip2Stream = null; try { bzip2Stream = new BZip2Stream(fileEntry.Content, SharpCompress.Compressors.CompressionMode.Decompress, false); governor.CheckResourceGovernor(bzip2Stream.Length); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.BZIP2, fileEntry.FullPath, string.Empty, e.GetType()); } if (bzip2Stream != null) { var newFilename = Path.GetFileNameWithoutExtension(fileEntry.Name); var entryStream = bzip2Stream.Length > options.MemoryStreamCutoff ? new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose) : (Stream) new MemoryStream((int)bzip2Stream.Length); var newFileEntry = new FileEntry(newFilename, bzip2Stream, fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); bzip2Stream.Dispose(); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } bzip2Stream.Dispose(); } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an zip file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { ZipFile?zipFile = null; try { zipFile = new ZipFile(fileEntry.Content); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.ZIP, fileEntry.FullPath, string.Empty, e.GetType()); } if (zipFile != null) { var buffer = new byte[BUFFER_SIZE]; var passwordFound = false; foreach (ZipEntry?zipEntry in zipFile) { if (zipEntry is null || zipEntry.IsDirectory || !zipEntry.CanDecompress) { continue; } if (zipEntry.IsCrypted && !passwordFound) { zipFile.Password = GetZipPassword(fileEntry, zipFile, zipEntry, options) ?? string.Empty; passwordFound = true; } governor.CheckResourceGovernor(zipEntry.Size); using var fs = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose); try { var zipStream = zipFile.GetInputStream(zipEntry); StreamUtils.Copy(zipStream, fs, buffer); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.ZIP, fileEntry.FullPath, zipEntry.Name, e.GetType()); } var name = zipEntry.Name.Replace('/', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry(name, fs, fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } await foreach (var extractedFile in Context.ExtractAsync(newFileEntry, options, governor)) { yield return(extractedFile); } } } }
/// <summary> /// Extracts an a VHD file /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { using var disk = new DiscUtils.Vhd.Disk(fileEntry.Content, Ownership.None); LogicalVolumeInfo[]? logicalVolumes = null; try { var manager = new VolumeManager(disk); logicalVolumes = manager.GetLogicalVolumes(); } catch (Exception e) { Logger.Debug("Error reading {0} disk at {1} ({2}:{3})", disk.GetType(), fileEntry.FullPath, e.GetType(), e.Message); } if (logicalVolumes != null) { foreach (var volume in logicalVolumes) { foreach (var entry in DiscCommon.DumpLogicalVolume(volume, fileEntry.FullPath, options, governor, Context, fileEntry)) { yield return(entry); } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts a WIM file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { DiscUtils.Wim.WimFile?baseFile = null; try { baseFile = new DiscUtils.Wim.WimFile(fileEntry.Content); } catch (Exception e) { Logger.Debug(e, "Failed to init WIM image."); } if (baseFile != null) { for (var i = 0; i < baseFile.ImageCount; i++) { var image = baseFile.GetImage(i); foreach (var file in image.GetFiles(image.Root.FullName, "*.*", SearchOption.AllDirectories)) { Stream?stream = null; try { var info = image.GetFileInfo(file); stream = info.OpenRead(); governor.CheckResourceGovernor(info.Length); } catch (Exception e) { Logger.Debug("Error reading {0} from WIM {1} ({2}:{3})", file, image.FriendlyName, e.GetType(), e.Message); } if (stream != null) { var name = file.Replace('\\', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry($"{image.FriendlyName}{Path.DirectorySeparatorChar}{name}", stream, fileEntry); foreach (var entry in Context.Extract(newFileEntry, options, governor)) { yield return(entry); } stream.Dispose(); } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts a WIM file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { DiscUtils.Wim.WimFile?baseFile = null; try { baseFile = new DiscUtils.Wim.WimFile(fileEntry.Content); } catch (Exception e) { Logger.Debug(e, "Failed to init WIM image."); } if (baseFile != null) { if (options.Parallel) { var files = new ConcurrentStack <FileEntry>(); for (var i = 0; i < baseFile.ImageCount; i++) { var image = baseFile.GetImage(i); var fileList = image.GetFiles(image.Root.FullName, "*.*", SearchOption.AllDirectories).ToList(); while (fileList.Count > 0) { var batchSize = Math.Min(options.BatchSize, fileList.Count); var range = fileList.Take(batchSize); var streamsAndNames = new List <(DiscFileInfo, Stream)>(); foreach (var file in range) { try { var info = image.GetFileInfo(file); var read = info.OpenRead(); streamsAndNames.Add((info, read)); } catch (Exception e) { Logger.Debug("Error reading {0} from WIM {1} ({2}:{3})", file, image.FriendlyName, e.GetType(), e.Message); } } governor.CheckResourceGovernor(streamsAndNames.Sum(x => x.Item1.Length)); streamsAndNames.AsParallel().ForAll(file => { var newFileEntry = new FileEntry($"{image.FriendlyName}\\{file.Item1.FullName}", file.Item2, fileEntry); var entries = Context.Extract(newFileEntry, options, governor); if (entries.Any()) { files.PushRange(entries.ToArray()); } }); fileList.RemoveRange(0, batchSize); while (files.TryPop(out var result)) { if (result != null) { yield return(result); } } } } } else { for (var i = 0; i < baseFile.ImageCount; i++) { var image = baseFile.GetImage(i); foreach (var file in image.GetFiles(image.Root.FullName, "*.*", SearchOption.AllDirectories)) { Stream?stream = null; try { var info = image.GetFileInfo(file); stream = info.OpenRead(); governor.CheckResourceGovernor(info.Length); } catch (Exception e) { Logger.Debug("Error reading {0} from WIM {1} ({2}:{3})", file, image.FriendlyName, e.GetType(), e.Message); } if (stream != null) { var name = file.Replace('\\', Path.DirectorySeparatorChar); var newFileEntry = await FileEntry.FromStreamAsync($"{image.FriendlyName}{Path.DirectorySeparatorChar}{name}", stream, fileEntry); await foreach (var entry in Context.ExtractAsync(newFileEntry, options, governor)) { yield return(entry); } stream.Dispose(); } } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an Gzip file contained in fileEntry. Since this function is recursive, even though /// Gzip only supports a single compressed file, that inner file could itself contain multiple others. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { GZipArchive?gzipArchive = null; try { gzipArchive = GZipArchive.Open(fileEntry.Content); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.GZIP, fileEntry.FullPath, string.Empty, e.GetType()); } if (gzipArchive != null) { foreach (var entry in gzipArchive.Entries) { if (entry.IsDirectory) { continue; } governor.CheckResourceGovernor(entry.Size); var newFilename = Path.GetFileNameWithoutExtension(fileEntry.Name); if (fileEntry.Name.EndsWith(".tgz", StringComparison.InvariantCultureIgnoreCase)) { newFilename = newFilename[0..^ 4] + ".tar";
/// <summary> /// Extracts a 7-Zip file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { var sevenZipArchive = GetSevenZipArchive(fileEntry, options); if (sevenZipArchive != null) { var entries = sevenZipArchive.Entries.Where(x => !x.IsDirectory && x.IsComplete).ToList(); if (options.Parallel) { var files = new ConcurrentStack <FileEntry>(); while (entries.Count() > 0) { var batchSize = Math.Min(options.BatchSize, entries.Count()); var selectedEntries = entries.GetRange(0, batchSize).Select(entry => (entry, entry.OpenEntryStream())); governor.CheckResourceGovernor(selectedEntries.Sum(x => x.entry.Size)); try { selectedEntries.AsParallel().ForAll(entry => { try { var name = entry.entry.Key.Replace('/', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry(name, entry.Item2, fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); governor.CurrentOperationProcessedBytesLeft = -1; } else { files.PushRange(Context.Extract(newFileEntry, options, governor).ToArray()); } } catch (Exception e) when(e is OverflowException) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.P7ZIP, fileEntry.FullPath, entry.entry.Key, e.GetType()); throw; } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.P7ZIP, fileEntry.FullPath, entry.entry.Key, e.GetType()); } }); } catch (Exception e) when(e is AggregateException) { if (e.InnerException?.GetType() == typeof(OverflowException)) { throw e.InnerException; } throw; } governor.CheckResourceGovernor(0); entries.RemoveRange(0, batchSize); while (files.TryPop(out var result)) { if (result != null) { yield return(result); } } } } else { foreach (var entry in entries) { governor.CheckResourceGovernor(entry.Size); var name = entry.Key.Replace('/', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry(name, entry.OpenEntryStream(), fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an archive file created with GNU ar /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> /// public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { IEnumerable <FileEntry>?fileEntries = null; try { fileEntries = ArFile.GetFileEntries(fileEntry, options, governor); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.AR, fileEntry.FullPath, string.Empty, e.GetType()); if (e is OverflowException) { throw; } } if (fileEntries != null) { if (options.Parallel) { while (fileEntries.Any()) { var tempStore = new ConcurrentStack <FileEntry>(); var selectedEntries = fileEntries.Take(options.BatchSize); selectedEntries.AsParallel().ForAll(arEntry => { tempStore.PushRange(Context.ExtractFile(arEntry, options, governor).ToArray()); }); fileEntries = fileEntries.Skip(selectedEntries.Count()); while (tempStore.TryPop(out var result)) { if (result != null) { yield return(result); } } } } else { foreach (var entry in fileEntries) { foreach (var extractedFile in Context.ExtractFile(entry, options, governor)) { yield return(extractedFile); } } } } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an an ISO file /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { using var cd = new CDReader(fileEntry.Content, true); var entries = cd.Root.GetFiles("*.*", SearchOption.AllDirectories); if (entries != null) { if (options.Parallel) { var files = new ConcurrentStack <FileEntry>(); var batchSize = Math.Min(options.BatchSize, entries.Length); var selectedFileEntries = entries[0..batchSize];
/// <summary> /// Extracts an BZip2 file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public async IAsyncEnumerable <FileEntry> ExtractAsync(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { BZip2Stream?bzip2Stream = null; try { bzip2Stream = new BZip2Stream(fileEntry.Content, SharpCompress.Compressors.CompressionMode.Decompress, false); governor.CheckResourceGovernor(bzip2Stream.Length); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.BZIP2, fileEntry.FullPath, string.Empty, e.GetType()); } if (bzip2Stream != null) { var newFilename = Path.GetFileNameWithoutExtension(fileEntry.Name); var newFileEntry = await FileEntry.FromStreamAsync(newFilename, bzip2Stream, fileEntry); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); bzip2Stream.Dispose(); throw new OverflowException(); } await foreach (var extractedFile in Context.ExtractAsync(newFileEntry, options, governor)) { yield return(extractedFile); } bzip2Stream.Dispose(); } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an zip file contained in fileEntry. /// </summary> /// <param name="fileEntry"> FileEntry to extract </param> /// <returns> Extracted files </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { XZStream?xzStream = null; try { xzStream = new XZStream(fileEntry.Content); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.XZ, fileEntry.FullPath, string.Empty, e.GetType()); } if (xzStream != null) { var newFilename = Path.GetFileNameWithoutExtension(fileEntry.Name); var newFileEntry = new FileEntry(newFilename, xzStream, fileEntry); // SharpCompress does not expose metadata without a full read, so we need to decompress first, // and then abort if the bytes exceeded the governor's capacity. var streamLength = xzStream.Index?.Records?.Select(r => r.UncompressedSize) .Aggregate((ulong?)0, (a, b) => a + b); // BUG: Technically, we're casting a ulong to a long, but we don't expect 9 exabyte steams, so // low risk. if (streamLength.HasValue) { governor.CheckResourceGovernor((long)streamLength.Value); } if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } xzStream.Dispose(); } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }
/// <summary> /// Extracts an a Tar archive /// </summary> /// <param name="fileEntry"> </param> /// <returns> </returns> public IEnumerable <FileEntry> Extract(FileEntry fileEntry, ExtractorOptions options, ResourceGovernor governor) { TarEntry tarEntry; TarInputStream?tarStream = null; try { tarStream = new TarInputStream(fileEntry.Content); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.TAR, fileEntry.FullPath, string.Empty, e.GetType()); } if (tarStream != null) { while ((tarEntry = tarStream.GetNextEntry()) != null) { if (tarEntry.IsDirectory) { continue; } var fs = new FileStream(Path.GetTempFileName(), FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose); governor.CheckResourceGovernor(tarStream.Length); try { tarStream.CopyEntryContents(fs); } catch (Exception e) { Logger.Debug(Extractor.DEBUG_STRING, ArchiveFileType.TAR, fileEntry.FullPath, tarEntry.Name, e.GetType()); } var name = tarEntry.Name.Replace('/', Path.DirectorySeparatorChar); var newFileEntry = new FileEntry(name, fs, fileEntry, true); if (Extractor.IsQuine(newFileEntry)) { Logger.Info(Extractor.IS_QUINE_STRING, fileEntry.Name, fileEntry.FullPath); throw new OverflowException(); } foreach (var extractedFile in Context.Extract(newFileEntry, options, governor)) { yield return(extractedFile); } } tarStream.Dispose(); } else { if (options.ExtractSelfOnFail) { yield return(fileEntry); } } }