public static HeaderBlock CreateHeaderBlock() { var hb = new HeaderBlock { name = new byte[100], mode = new byte[8], uid = new byte[8], gid = new byte[8], size = new byte[12], modifiedTime = new byte[12], checkSum = new byte[8], linkName = new byte[100], magic = new byte[6], version = new byte[2], userName = new byte[32], groupName = new byte[32], devMajor = new byte[8], devMinor = new byte[8], prefix = new byte[155], pad = new byte[12], }; Array.Copy(Encoding.ASCII.GetBytes("ustar "), 0, hb.magic, 0, 6); hb.version[0] = hb.version[1] = (byte)TarEntryType.File; return(hb); }
/// <summary>Adds a file to the tar Archive</summary> /// <param name="fullName">The file to add to the archive</param><param name="directory"></param> public void AddFile(string fullName, string directory = null) { if (string.IsNullOrEmpty(directory)) { directory = Path.GetDirectoryName(fullName); } // is it a symlink (ReparsePoint)? FileAttributes a = File.GetAttributes(fullName); if ((a & FileAttributes.ReparsePoint) != 0) { this.AddSymlink(fullName); return; } if (this.TarOptions.StatusWriter != null) { this.TarOptions.StatusWriter.WriteLine("{0}", fullName); } HeaderBlock hb = HeaderBlock.CreateHeaderBlock(); string file = Path.Combine(directory, Path.GetFileName(fullName)); if (file == fullName) { file = Path.GetFileName(fullName); } hb.InsertName(file, fullName); hb.TypeFlag = (byte)TarEntryType.File; // 0 + (byte)'0' ; var fi = new FileInfo(fullName); hb.SetSize((int)fi.Length); hb.SetChksum(); byte[] block = this.Serializer.RawSerialize(hb); this.outFS.Write(block, 0, block.Length); using (FileStream fs = File.Open(fullName, FileMode.Open, FileAccess.Read)) { Array.Clear(block, 0, block.Length); while (fs.Read(block, 0, block.Length) > 0) { this.outFS.Write(block, 0, block.Length); // not n!! Array.Clear(block, 0, block.Length); } } }
/// <summary>Adds a symbolic link to a Tar archive</summary> /// <param name="name">The filename of the symbolic link</param> private void AddSymlink(string name) { if (this.TarOptions.StatusWriter != null) { this.TarOptions.StatusWriter.WriteLine("{0}", name); } // add the block for the symlink, right here. HeaderBlock hb = HeaderBlock.CreateHeaderBlock(); hb.InsertName(name, null); hb.InsertLinkName(name); hb.TypeFlag = (byte)TarEntryType.SymbolicLink; hb.SetSize(0); hb.SetChksum(); byte[] block = this.Serializer.RawSerialize(hb); this.outFS.Write(block, 0, block.Length); }
/// <summary>Adds a directory to the tar archive</summary> /// <param name="dirName">The path to the directory</param><param name="parent"></param> public void AddDirectory(string dirName, string parent = null) { if (parent == null) { parent = dirName; } // insure trailing slash if (!dirName.EndsWith(Path.DirectorySeparatorChar.ToString())) { dirName += Path.DirectorySeparatorChar; } if (this.TarOptions.StatusWriter != null) { this.TarOptions.StatusWriter.WriteLine("{0}", dirName); } string dir = dirName.Replace(parent, null).TrimSlash(); if (dir != string.Empty) { // add the block for the dir, right here. HeaderBlock hb = HeaderBlock.CreateHeaderBlock(); hb.InsertName(dir, dirName); hb.TypeFlag = 5 + (byte)'0'; hb.SetSize(0); // some impls use agg size of all files contained hb.SetChksum(); byte[] block = this.Serializer.RawSerialize(hb); this.outFS.Write(block, 0, block.Length); } // add the files: string[] filenames = Directory.GetFiles(dirName); foreach (string filename in filenames) { this.AddFile(filename, dir); } // add the subdirectories: string[] dirnames = Directory.GetDirectories(dirName); foreach (string d in dirnames) { // handle reparse points FileAttributes a = File.GetAttributes(d); if ((a & FileAttributes.ReparsePoint) == 0) { // not a symlink this.AddDirectory(d, Path.GetDirectoryName(dirName)); } else if (this.TarOptions.FollowSymLinks) { // isa symlink, and we want to follow it this.AddDirectory(d, Path.GetDirectoryName(dirName)); } else { // not following symlinks; add it this.AddSymlink(d); } } }
/// <param name="archive"></param><param name="extractDirectory"></param> <param name="wantExtract"></param> /// <returns></returns> private List <TarEntry> InternalListOrExtract(string archive, string extractDirectory, bool wantExtract) { var entryList = new List <TarEntry>(); var block = new byte[512]; 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 FileNotFoundException("The archive does not exist", archive); } using (Stream fs = this.InternalGetInputStream(archive)) { while (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 = Encoding.ASCII.GetString((output as MemoryStream).ToArray()).TrimNull(); } output.Close(); output.Dispose(); if (output is FileStream && !this.TarOptions.DoNotSetTime) { File.SetLastWriteTimeUtc(Path.Combine(extractDirectory, name), mtime); } output = null; } } continue; } HeaderBlock hb = this.Serializer.RawDeserialize(block); 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 (string.IsNullOrEmpty(name)) { 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) { } else { switch (entry.Type) { case TarEntryType.Directory: if (!Directory.Exists(Path.Combine(extractDirectory, name))) { Directory.CreateDirectory(Path.Combine(extractDirectory, name)); // 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. if (!this.TarOptions.DoNotSetTime) { deferredDirTimestamp.Add( Path.Combine(extractDirectory, name).TrimSlash(), mtime); } } else if (this.TarOptions.Overwrite) { if (!this.TarOptions.DoNotSetTime) { deferredDirTimestamp.Add( Path.Combine(extractDirectory, name).TrimSlash(), mtime); } } break; case TarEntryType.FileOld: case TarEntryType.File: case TarEntryType.FileContiguous: string p = Path.GetDirectoryName(Path.Combine(extractDirectory, name)); if (!string.IsNullOrEmpty(p)) { if (!Directory.Exists(p)) { Directory.CreateDirectory(p); } } output = this.InternalGetExtractOutputStream(name, extractDirectory); break; case TarEntryType.GnuVolumeHeader: case TarEntryType.CharSpecial: case TarEntryType.BlockSpecial: // do nothing on extract break; case TarEntryType.SymbolicLink: break; default: throw new Exception(string.Format("unsupported entry type ({0})", hb.TypeFlag)); } } } } // apply the deferred timestamps on the directories if (deferredDirTimestamp.Count > 0) { foreach (string key in deferredDirTimestamp.Keys) { Directory.SetLastWriteTimeUtc(key, deferredDirTimestamp[key]); } } return(entryList); }