/// <summary> /// Write an entry to the archive. This method will call the putNextEntry /// and then write the contents of the entry, and finally call closeEntry() /// for entries that are files. For directories, it will call putNextEntry(), /// and then, if the recurse flag is true, process each entry that is a /// child of the directory. /// </summary> /// <param name="sourceEntry"> /// The TarEntry representing the entry to write to the archive. /// </param> /// <param name="recurse"> /// If true, process the children of directory entries. /// </param> void InternalWriteEntry(TarEntry sourceEntry, bool recurse) { bool asciiTrans = false; string tempFileName = null; string entryFilename = sourceEntry.File; TarEntry entry = (TarEntry)sourceEntry.Clone(); if (applyUserInfoOverrides) { entry.GroupId = groupId; entry.GroupName = groupName; entry.UserId = userId; entry.UserName = userName; } OnProgressMessageEvent(entry, null); if (this.asciiTranslate && !entry.IsDirectory) { asciiTrans = !IsBinary(entryFilename); if (asciiTrans) { tempFileName = Path.GetTempFileName(); StreamReader inStream = File.OpenText(entryFilename); Stream outStream = File.Create(tempFileName); while (true) { string line = inStream.ReadLine(); if (line == null) { break; } byte[] data = Encoding.ASCII.GetBytes(line); outStream.Write(data, 0, data.Length); outStream.WriteByte((byte)'\n'); } inStream.Close(); outStream.Flush(); outStream.Close(); entry.Size = new FileInfo(tempFileName).Length; entryFilename = tempFileName; } } string newName = null; if (this.rootPath != null) { if (entry.Name.StartsWith(this.rootPath)) { newName = entry.Name.Substring(this.rootPath.Length + 1); } } if (this.pathPrefix != null) { newName = (newName == null) ? this.pathPrefix + "/" + entry.Name : this.pathPrefix + "/" + newName; } if (newName != null) { entry.Name = newName; } this.tarOut.PutNextEntry(entry); if (entry.IsDirectory) { if (recurse) { TarEntry[] list = entry.GetDirectoryEntries(); for (int i = 0; i < list.Length; ++i) { InternalWriteEntry(list[i], recurse); } } } else { Stream inputStream = File.OpenRead(entryFilename); int numWritten = 0; byte[] eBuf = new byte[32 * 1024]; while (true) { int numRead = inputStream.Read(eBuf, 0, eBuf.Length); if (numRead <= 0) { break; } this.tarOut.Write(eBuf, 0, numRead); numWritten += numRead; } inputStream.Close(); if (tempFileName != null && tempFileName.Length > 0) { File.Delete(tempFileName); } this.tarOut.CloseEntry(); } }
/// <summary> /// Extract an entry from the archive. This method assumes that the /// tarIn stream has been properly set with a call to getNextEntry(). /// </summary> /// <param name="destDir"> /// The destination directory into which to extract. /// </param> /// <param name="entry"> /// The TarEntry returned by tarIn.getNextEntry(). /// </param> void ExtractEntry(string destDir, TarEntry entry) { OnProgressMessageEvent(entry, null); string name = entry.Name; if (Path.IsPathRooted(name) == true) { // NOTE: // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt name = name.Substring(Path.GetPathRoot(name).Length); } name = name.Replace('/', Path.DirectorySeparatorChar); string destFile = Path.Combine(destDir, name); if (entry.IsDirectory) { EnsureDirectoryExists(destFile); } else { string parentDirectory = Path.GetDirectoryName(destFile); EnsureDirectoryExists(parentDirectory); bool process = true; FileInfo fileInfo = new FileInfo(destFile); if (fileInfo.Exists) { if (this.keepOldFiles) { OnProgressMessageEvent(entry, "Destination file already exists"); process = false; } else if ((fileInfo.Attributes & FileAttributes.ReadOnly) != 0) { OnProgressMessageEvent(entry, "Destination file already exists, and is read-only"); process = false; } } if (process) { bool asciiTrans = false; Stream outputStream = File.Create(destFile); if (this.asciiTranslate) { asciiTrans = !IsBinary(destFile); } StreamWriter outw = null; if (asciiTrans) { outw = new StreamWriter(outputStream); } byte[] rdbuf = new byte[32 * 1024]; while (true) { int numRead = this.tarIn.Read(rdbuf, 0, rdbuf.Length); if (numRead <= 0) { break; } if (asciiTrans) { for (int off = 0, b = 0; b < numRead; ++b) { if (rdbuf[b] == 10) { string s = Encoding.ASCII.GetString(rdbuf, off, (b - off)); outw.WriteLine(s); off = b + 1; } } } else { outputStream.Write(rdbuf, 0, numRead); } } if (asciiTrans) { outw.Close(); } else { outputStream.Close(); } } } }
/// <summary> /// Determine if the given entry is a descendant of this entry. /// Descendancy is determined by the name of the descendant /// starting with this entry's name. /// </summary> /// <param name = "desc"> /// Entry to be checked as a descendent of this. /// </param> /// <returns> /// True if entry is a descendant of this. /// </returns> public bool IsDescendent(TarEntry desc) { return(desc.Name.StartsWith(Name)); }
/// <summary> /// Create a tar entry with details obtained from <paramref name="fileName">file</paramref> /// </summary> public TarEntry CreateEntryFromFile(string fileName) { return(TarEntry.CreateEntryFromFile(fileName)); }
/// <summary> /// Create a TarEntry based on named /// </summary> public TarEntry CreateEntry(string name) { return(TarEntry.CreateTarEntry(name)); }
/// <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); }