private List <TarEntry> _internal_ListOrExtract(string archive, bool wantExtract, string path) { var entryList = new List <TarEntry>(); byte[] block = new byte[512]; int n = 0; int blocksToMunch = 0; int remainingBytes = 0; Stream output = null; DateTime mtime = DateTime.Now; string name = null; TarEntry entry = null; var deferredDirTimestamp = new Dictionary <String, DateTime>(); if (!File.Exists(archive)) { throw new InvalidOperationException("The specified file does not exist."); } using (Stream fs = _internal_GetInputStream(archive)) { while ((n = fs.Read(block, 0, block.Length)) > 0) { if (blocksToMunch > 0) { if (output != null) { int bytesToWrite = (block.Length < remainingBytes) ? block.Length : remainingBytes; output.Write(block, 0, bytesToWrite); remainingBytes -= bytesToWrite; } blocksToMunch--; if (blocksToMunch == 0) { if (output != null) { if (output is MemoryStream) { entry.Name = name = System.Text.Encoding.ASCII.GetString((output as MemoryStream).ToArray()).TrimNull(); } output.Close(); output.Dispose(); if (output is FileStream && !TarOptions.DoNotSetTime) { File.SetLastWriteTimeUtc(name, mtime); } output = null; } } continue; } HeaderBlock hb = serializer.RawDeserialize(block); if (!hb.VerifyChksum()) { throw new Exception("header checksum is invalid."); } if (entry == null || entry.Type != TarEntryType.GnuLongName) { name = hb.GetName(); } if (name == null || name.Length == 0) { break; } mtime = hb.GetMtime(); remainingBytes = hb.GetSize(); if (hb.typeflag == 0) { hb.typeflag = (byte)'0'; } entry = new TarEntry() { Name = name, Mtime = mtime, Size = remainingBytes, @Type = (TarEntryType)hb.typeflag }; if (entry.Type != TarEntryType.GnuLongName) { entryList.Add(entry); } blocksToMunch = (remainingBytes > 0) ? ((remainingBytes - 1) / 512) + 1 : 0; if (entry.Type == TarEntryType.GnuLongName) { if (name != "././@LongLink") { if (wantExtract) { throw new Exception(String.Format("unexpected name for type 'L' (expected '././@LongLink', got '{0}')", name)); } } output = new MemoryStream(); continue; } if (wantExtract) { name = path + name; switch (entry.Type) { case TarEntryType.Directory: if (!Directory.Exists(name)) { Directory.CreateDirectory(name); if (!TarOptions.DoNotSetTime) { deferredDirTimestamp.Add(name.TrimSlash(), mtime); } } else if (TarOptions.Overwrite) { if (!TarOptions.DoNotSetTime) { deferredDirTimestamp.Add(name.TrimSlash(), mtime); } } break; case TarEntryType.File_Old: case TarEntryType.File: case TarEntryType.File_Contiguous: string p = Path.GetDirectoryName(name); if (!String.IsNullOrEmpty(p)) { if (!Directory.Exists(p)) { Directory.CreateDirectory(p); } } output = _internal_GetExtractOutputStream(name); break; case TarEntryType.GnuVolumeHeader: case TarEntryType.CharSpecial: case TarEntryType.BlockSpecial: break; case TarEntryType.SymbolicLink: break; default: throw new Exception(String.Format("unsupported entry type ({0})", hb.typeflag)); } } } } if (deferredDirTimestamp.Count > 0) { foreach (var s in deferredDirTimestamp.Keys) { Directory.SetLastWriteTimeUtc(s, deferredDirTimestamp[s]); } } return(entryList); }
private List <TarEntry> listOrExtract(Stream file, string extractDirectory) { bool wantExtract = extractDirectory != null; var entryList = new List <TarEntry>(); byte[] block = new byte[512]; int n = 0; int blocksToMunch = 0; int remainingBytes = 0; Stream output = null; DateTime mtime = DateTime.Now; string name = null; TarEntry entry = null; var deferredDirTimestamp = new Dictionary <String, DateTime>(); while ((n = file.Read(block, 0, block.Length)) > 0) { if (blocksToMunch > 0) { if (output != null) { int bytesToWrite = (block.Length < remainingBytes) ? block.Length : remainingBytes; output.Write(block, 0, bytesToWrite); remainingBytes -= bytesToWrite; } blocksToMunch--; //System.Diagnostics.Debugger.Break(); if (blocksToMunch == 0) { if (output != null) { if (output is MemoryStream) { entry.Name = name = System.Text.Encoding.ASCII.GetString((output as MemoryStream).ToArray()).TrimNull(); } output.Close(); output.Dispose(); if (output is FileStream) { File.SetLastWriteTimeUtc(Path.Combine(extractDirectory, name), mtime); } output = null; } } continue; } HeaderBlock hb = serializer.RawDeserialize(block); //System.Diagnostics.Debugger.Break(); if (!hb.VerifyChksum()) { throw new Exception("header checksum is invalid."); } // if this is the first entry, or if the prior entry is not a GnuLongName if (entry == null || entry.Type != TarEntryType.GnuLongName) { name = hb.GetName(); } if (name == null || name.Length == 0) { break; // EOF } mtime = hb.GetMtime(); remainingBytes = hb.GetSize(); if (hb.typeflag == 0) { hb.typeflag = (byte)'0'; // coerce old-style GNU type to posix tar type } entry = new TarEntry() { Name = name, Mtime = mtime, Size = remainingBytes, @Type = (TarEntryType)hb.typeflag }; if (entry.Type != TarEntryType.GnuLongName) { entryList.Add(entry); } blocksToMunch = (remainingBytes > 0) ? ((remainingBytes - 1) / 512) + 1 : 0; if (entry.Type == TarEntryType.GnuLongName) { if (name != "././@LongLink") { if (wantExtract) { throw new Exception(String.Format("unexpected name for type 'L' (expected '././@LongLink', got '{0}')", name)); } } // for GNU long names, we extract the long name info into a memory stream output = new MemoryStream(); continue; } if (wantExtract) { var extractPath = Path.Combine(extractDirectory, name); switch (entry.Type) { case TarEntryType.Directory: if (!Directory.Exists(extractPath)) { Directory.CreateDirectory(extractPath); // cannot set the time on the directory now, or it will be updated // by future file writes. Defer until after all file writes are done. deferredDirTimestamp.Add(extractPath.TrimSlash(), mtime); } else { deferredDirTimestamp.Add(extractPath.TrimSlash(), mtime); } break; case TarEntryType.File_Old: case TarEntryType.File: case TarEntryType.File_Contiguous: string p = Path.GetDirectoryName(extractPath); if (!String.IsNullOrEmpty(p)) { if (!Directory.Exists(p)) { Directory.CreateDirectory(p); } } output = getOutputStream(extractPath); break; case TarEntryType.GnuVolumeHeader: case TarEntryType.CharSpecial: case TarEntryType.BlockSpecial: // do nothing on extract break; case TarEntryType.SymbolicLink: break; // can support other types here - links, etc default: throw new Exception(String.Format("unsupported entry type ({0})", hb.typeflag)); } } } // apply the deferred timestamps on the directories if (deferredDirTimestamp.Count > 0) { foreach (var s in deferredDirTimestamp.Keys) { Directory.SetLastWriteTimeUtc(s, deferredDirTimestamp[s]); } } return(entryList); }