/// <summary> /// Fill in a TarHeader given only the entry's name. /// </summary> /// <param name="hdr"> /// The TarHeader to fill in. /// </param> /// <param name="name"> /// The tar entry name. /// </param> public void NameTarHeader(TarHeader hdr, string name) { bool isDir = name.EndsWith("/"); hdr.Name = name; hdr.Mode = isDir ? 1003 : 33216; hdr.UserId = 0; hdr.GroupId = 0; hdr.Size = 0; hdr.ModTime = DateTime.UtcNow; hdr.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; hdr.LinkName = String.Empty; hdr.UserName = String.Empty; hdr.GroupName = String.Empty; hdr.DevMajor = 0; hdr.DevMinor = 0; }
/// <summary> /// Initialization code common to all pseudo constructors. /// </summary> void Initialize() { this.file = null; this.header = new TarHeader(); }
/// <summary> /// Fill in a TarHeader with information from a File. /// </summary> /// <param name="hdr"> /// The TarHeader to fill in. /// </param> /// <param name="file"> /// The file from which to get the header information. /// </param> public void GetFileTarHeader(TarHeader hdr, string file) { this.file = file; // bugfix from torhovl from #D forum: string name = file; #if !COMPACT_FRAMEWORK // 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory if (name.IndexOf(Environment.CurrentDirectory) == 0) { name = name.Substring(Environment.CurrentDirectory.Length); } #endif /* if (Path.DirectorySeparatorChar == '\\') { // check if the OS is Windows // Strip off drive letters! if (name.Length > 2) { char ch1 = name[0]; char ch2 = name[1]; if (ch2 == ':' && Char.IsLetter(ch1)) { name = name.Substring(2); } } } */ name = name.Replace(Path.DirectorySeparatorChar, '/'); // No absolute pathnames // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\", // so we loop on starting /'s. while (name.StartsWith("/")) { name = name.Substring(1); } hdr.LinkName = String.Empty; hdr.Name = name; if (Directory.Exists(file)) { hdr.Mode = 1003; // Magic number for security access for a UNIX filesystem hdr.TypeFlag = TarHeader.LF_DIR; if (hdr.Name.Length == 0 || hdr.Name[hdr.Name.Length - 1] != '/') { hdr.Name = hdr.Name + "/"; } hdr.Size = 0; } else { hdr.Mode = 33216; // Magic number for security access for a UNIX filesystem hdr.TypeFlag = TarHeader.LF_NORMAL; hdr.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; } hdr.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); hdr.DevMajor = 0; hdr.DevMinor = 0; }
/// <summary> /// Get the next entry in this tar archive. This will skip /// over any remaining data in the current entry, if there /// is one, and place the input stream at the header of the /// next entry, and read the header and instantiate a new /// TarEntry from the header bytes and return that entry. /// If there are no more entries in the archive, null will /// be returned to indicate that the end of the archive has /// been reached. /// </summary> /// <returns> /// The next TarEntry in the archive, or null. /// </returns> public TarEntry GetNextEntry() { if (this.hasHitEOF) { return null; } if (this.currEntry != null) { SkipToNextEntry(); } byte[] headerBuf = this.buffer.ReadBlock(); if (headerBuf == null) { this.hasHitEOF = true; } else if (this.buffer.IsEOFBlock(headerBuf)) { this.hasHitEOF = true; } if (this.hasHitEOF) { this.currEntry = null; } else { try { TarHeader header = new TarHeader(); header.ParseBuffer(headerBuf); if ( !header.IsChecksumValid ) { throw new TarException("Header checksum is invalid"); } this.entryOffset = 0; this.entrySize = header.Size; StringBuilder longName = null; if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) { byte[] nameBuffer = new byte[TarBuffer.BlockSize]; long numToRead = this.entrySize; longName = new StringBuilder(); while (numToRead > 0) { int numRead = this.Read(nameBuffer, 0, (numToRead > nameBuffer.Length ? nameBuffer.Length : (int)numToRead)); if (numRead == -1) { throw new InvalidHeaderException("Failed to read long name entry"); } longName.Append(TarHeader.ParseName(nameBuffer, 0, numRead).ToString()); numToRead -= numRead; } SkipToNextEntry(); headerBuf = this.buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GHDR) { // POSIX global extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = this.buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_XHDR) { // POSIX extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = this.buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) { // TODO: could show volume name when verbose SkipToNextEntry(); headerBuf = this.buffer.ReadBlock(); } else if (header.TypeFlag != TarHeader.LF_NORMAL && header.TypeFlag != TarHeader.LF_OLDNORM && header.TypeFlag != TarHeader.LF_DIR) { // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = this.buffer.ReadBlock(); } if (this.eFactory == null) { this.currEntry = new TarEntry(headerBuf); if (longName != null) { currEntry.Name = longName.ToString(); } } else { this.currEntry = this.eFactory.CreateEntry(headerBuf); } // Magic was checked here for 'ustar' but there are multiple valid possibilities // so this is not done anymore. this.entryOffset = 0; // TODO: Review How do we resolve this discrepancy?! this.entrySize = this.currEntry.Size; } catch (InvalidHeaderException ex) { this.entrySize = 0; this.entryOffset = 0; this.currEntry = null; throw new InvalidHeaderException("bad header in record " + this.buffer.GetCurrentBlockNum() + " block " + this.buffer.GetCurrentBlockNum() + ", " + ex.Message); } } return this.currEntry; }
/// <summary> /// Construct a TarEntry using the <paramref name="header">header</paramref> provided /// </summary> /// <param name="header">Header details for entry</param> public TarEntry(TarHeader header) { file = null; this.header = header; }
/// <summary> /// Add the checksum integer to header buffer. /// </summary> /// <param name = "val"></param> /// <param name = "buf">The header buffer to set the checksum for</param> /// <param name = "offset">The offset into the buffer for the checksum</param> /// <param name = "length">The number of header bytes to update. /// It's formatted differently from the other fields: it has 6 digits, a /// null, then a space -- rather than digits, a space, then a null. /// The final space is already there, from checksumming /// </param> /// <returns>The modified buffer offset</returns> private static int GetCheckSumOctalBytes(long val, byte[] buf, int offset, int length) { TarHeader.GetOctalBytes(val, buf, offset, length - 1); return(offset + length); }
/// <summary> /// Put an entry on the output stream. This writes the entry's /// header and positions the output stream for writing /// the contents of the entry. Once this method is called, the /// stream is ready for calls to write() to write the entry's /// contents. Once the contents are written, closeEntry() /// <B>MUST</B> be called to ensure that all buffered data /// is completely written to the output stream. /// </summary> /// <param name="entry"> /// The TarEntry to be written to the archive. /// </param> public void PutNextEntry(TarEntry entry) { if (entry.TarHeader.Name.Length >= TarHeader.NAMELEN) { TarHeader longHeader = new TarHeader(); longHeader.TypeFlag = TarHeader.LF_GNU_LONGNAME; longHeader.Name = longHeader.Name + "././@LongLink"; longHeader.UserId = 0; longHeader.GroupId = 0; longHeader.GroupName = ""; longHeader.UserName = ""; longHeader.LinkName = ""; longHeader.Size = entry.TarHeader.Name.Length; longHeader.WriteHeader(this.blockBuf); this.buffer.WriteBlock(this.blockBuf); // Add special long filename header block int nameCharIndex = 0; while (nameCharIndex < entry.TarHeader.Name.Length) { Array.Clear(blockBuf, 0, blockBuf.Length); TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuf, 0, TarBuffer.BlockSize); nameCharIndex += TarBuffer.BlockSize; this.buffer.WriteBlock(this.blockBuf); } } entry.WriteEntryHeader(this.blockBuf); this.buffer.WriteBlock(this.blockBuf); this.currBytes = 0; this.currSize = entry.IsDirectory ? 0 : entry.Size; }
/// <summary> /// Fill in a TarHeader with information from a File. /// </summary> /// <param name="hdr"> /// The TarHeader to fill in. /// </param> /// <param name="file"> /// The file from which to get the header information. /// </param> public void GetFileTarHeader(TarHeader hdr, string file) { this.file = file; // bugfix from torhovl from #D forum: string name = file; #if !COMPACT_FRAMEWORK // 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory if (name.IndexOf(Environment.CurrentDirectory) == 0) { name = name.Substring(Environment.CurrentDirectory.Length); } #endif /* * if (Path.DirectorySeparatorChar == '\\') * { // check if the OS is Windows * // Strip off drive letters! * if (name.Length > 2) * { * char ch1 = name[0]; * char ch2 = name[1]; * * if (ch2 == ':' && Char.IsLetter(ch1)) * { * name = name.Substring(2); * } * } * } */ name = name.Replace(Path.DirectorySeparatorChar, '/'); // No absolute pathnames // Windows (and Posix?) paths can start with UNC style "\\NetworkDrive\", // so we loop on starting /'s. while (name.StartsWith("/")) { name = name.Substring(1); } hdr.LinkName = String.Empty; hdr.Name = name; if (Directory.Exists(file)) { hdr.Mode = 1003; // Magic number for security access for a UNIX filesystem hdr.TypeFlag = TarHeader.LF_DIR; if (hdr.Name.Length == 0 || hdr.Name[hdr.Name.Length - 1] != '/') { hdr.Name = hdr.Name + "/"; } hdr.Size = 0; } else { hdr.Mode = 33216; // Magic number for security access for a UNIX filesystem hdr.TypeFlag = TarHeader.LF_NORMAL; hdr.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; } hdr.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); hdr.DevMajor = 0; hdr.DevMinor = 0; }
/// <summary> /// Convenience method that will modify an entry's name directly /// in place in an entry header buffer byte array. /// </summary> /// <param name="outbuf"> /// The buffer containing the entry header to modify. /// </param> /// <param name="newName"> /// The new name to place into the header buffer. /// </param> public void AdjustEntryName(byte[] outbuf, string newName) { int offset = 0; TarHeader.GetNameBytes(newName, outbuf, offset, TarHeader.NAMELEN); }
/// <summary> /// Clone a TAR header. /// </summary> public object Clone() { TarHeader hdr = new TarHeader(); hdr.Name = Name; hdr.Mode = this.Mode; hdr.UserId = this.UserId; hdr.GroupId = this.GroupId; hdr.Size = this.Size; hdr.ModTime = this.ModTime; hdr.TypeFlag = this.TypeFlag; hdr.LinkName = this.LinkName; hdr.Magic = this.Magic; hdr.Version = this.Version; hdr.UserName = this.UserName; hdr.GroupName = this.GroupName; hdr.DevMajor = this.DevMajor; hdr.DevMinor = this.DevMinor; return hdr; }