using System; using System.Collections.Generic; using System.IO; using System.Linq; using SharpCompress.Common; using SharpCompress.Common.Tar; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Tar; using SharpCompress.Writers; using SharpCompress.Writers.Tar; namespace SharpCompress.Archives.Tar { public class TarArchive : AbstractWritableArchive { #if !NO_FILE /// /// Constructor expects a filepath to an existing file. /// /// /// public static TarArchive Open(string filePath, ReaderOptions readerOptions = null) { filePath.CheckNotNullOrEmpty("filePath"); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); } /// /// Constructor with a FileInfo object to an existing file. /// /// /// public static TarArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null) { fileInfo.CheckNotNull("fileInfo"); return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions()); } #endif /// /// Takes a seekable Stream as a source /// /// /// public static TarArchive Open(Stream stream, ReaderOptions readerOptions = null) { stream.CheckNotNull("stream"); return new TarArchive(stream, readerOptions ?? new ReaderOptions()); } #if !NO_FILE public static bool IsTarFile(string filePath) { return IsTarFile(new FileInfo(filePath)); } public static bool IsTarFile(FileInfo fileInfo) { if (!fileInfo.Exists) { return false; } using (Stream stream = fileInfo.OpenRead()) { return IsTarFile(stream); } } #endif public static bool IsTarFile(Stream stream) { try { TarHeader tar = new TarHeader(new ArchiveEncoding()); tar.Read(new BinaryReader(stream)); return tar.Name.Length > 0 && Enum.IsDefined(typeof(EntryType), tar.EntryType); } catch { } return false; } #if !NO_FILE /// /// Constructor with a FileInfo object to an existing file. /// /// /// internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions) : base(ArchiveType.Tar, fileInfo, readerOptions) { } protected override IEnumerable LoadVolumes(FileInfo file) { return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable(); } #endif /// /// Takes multiple seekable Streams for a multi-part archive /// /// /// internal TarArchive(Stream stream, ReaderOptions readerOptions) : base(ArchiveType.Tar, stream, readerOptions) { } internal TarArchive() : base(ArchiveType.Tar) { } protected override IEnumerable LoadVolumes(IEnumerable streams) { return new TarVolume(streams.First(), ReaderOptions).AsEnumerable(); } protected override IEnumerable LoadEntries(IEnumerable volumes) { Stream stream = volumes.Single().Stream; TarHeader previousHeader = null; foreach (TarHeader header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ArchiveEncoding)) { if (header != null) { if (header.EntryType == EntryType.LongName) { previousHeader = header; } else { if (previousHeader != null) { var entry = new TarArchiveEntry(this, new TarFilePart(previousHeader, stream), CompressionType.None); var oldStreamPos = stream.Position; using (var entryStream = entry.OpenEntryStream()) { using (var memoryStream = new MemoryStream()) { entryStream.TransferTo(memoryStream); memoryStream.Position = 0; var bytes = memoryStream.ToArray(); header.Name = ReaderOptions.ArchiveEncoding.Decode(bytes).TrimNulls(); } } stream.Position = oldStreamPos; previousHeader = null; } yield return new TarArchiveEntry(this, new TarFilePart(header, stream), CompressionType.None); } } } } public static TarArchive Create() { return new TarArchive(); } protected override TarArchiveEntry CreateEntryInternal(string filePath, Stream source, long size, DateTime? modified, bool closeStream) { return new TarWritableArchiveEntry(this, source, CompressionType.Unknown, filePath, size, modified, closeStream); } protected override void SaveTo(Stream stream, WriterOptions options, IEnumerable oldEntries, IEnumerable newEntries) { using (var writer = new TarWriter(stream, new TarWriterOptions(options))) { foreach (var entry in oldEntries.Concat(newEntries) .Where(x => !x.IsDirectory)) { using (var entryStream = entry.OpenEntryStream()) { writer.Write(entry.Key, entryStream, entry.LastModifiedTime, entry.Size); } } } } protected override IReader CreateReaderForSolidExtraction() { var stream = Volumes.Single().Stream; stream.Position = 0; return TarReader.Open(stream); } } }