public void HeaderEquality() { TarHeader h1 = new TarHeader(); TarHeader h2 = new TarHeader(); Assert.IsTrue(h1.Equals(h2)); h1.Name = "ABCDEFG"; Assert.IsFalse(h1.Equals(h2)); h2.Name = h1.Name; Assert.IsTrue(h1.Equals(h2)); h1.Mode = 33188; Assert.IsFalse(h1.Equals(h2)); h2.Mode = h1.Mode; Assert.IsTrue(h1.Equals(h2)); h1.UserId = 654; Assert.IsFalse(h1.Equals(h2)); h2.UserId = h1.UserId; Assert.IsTrue(h1.Equals(h2)); h1.GroupId = 654; Assert.IsFalse(h1.Equals(h2)); h2.GroupId = h1.GroupId; Assert.IsTrue(h1.Equals(h2)); h1.Size = 654; Assert.IsFalse(h1.Equals(h2)); h2.Size = h1.Size; Assert.IsTrue(h1.Equals(h2)); h1.ModTime = DateTime.Now; Assert.IsFalse(h1.Equals(h2)); h2.ModTime = h1.ModTime; Assert.IsTrue(h1.Equals(h2)); h1.TypeFlag = 165; Assert.IsFalse(h1.Equals(h2)); h2.TypeFlag = h1.TypeFlag; Assert.IsTrue(h1.Equals(h2)); h1.LinkName = "link"; Assert.IsFalse(h1.Equals(h2)); h2.LinkName = h1.LinkName; Assert.IsTrue(h1.Equals(h2)); h1.Magic = "ustar"; Assert.IsFalse(h1.Equals(h2)); h2.Magic = h1.Magic; Assert.IsTrue(h1.Equals(h2)); h1.Version = "1"; Assert.IsFalse(h1.Equals(h2)); h2.Version = h1.Version; Assert.IsTrue(h1.Equals(h2)); h1.UserName = "******"; Assert.IsFalse(h1.Equals(h2)); h2.UserName = h1.UserName; Assert.IsTrue(h1.Equals(h2)); h1.GroupName = "group"; Assert.IsFalse(h1.Equals(h2)); h2.GroupName = h1.GroupName; Assert.IsTrue(h1.Equals(h2)); h1.DevMajor = 165; Assert.IsFalse(h1.Equals(h2)); h2.DevMajor = h1.DevMajor; Assert.IsTrue(h1.Equals(h2)); h1.DevMinor = 164; Assert.IsFalse(h1.Equals(h2)); h2.DevMinor = h1.DevMinor; Assert.IsTrue(h1.Equals(h2)); }
/// <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 == null) { throw new ArgumentNullException("entry"); } if (entry.TarHeader.Name.Length >= TarHeader.NAMELEN) { var longHeader = new TarHeader{TypeFlag = TarHeader.LF_GNU_LONGNAME}; longHeader.Name = longHeader.Name + "././@LongLink"; longHeader.UserId = 0; longHeader.GroupId = 0; longHeader.GroupName = String.Empty; longHeader.UserName = String.Empty; longHeader.LinkName = String.Empty; longHeader.Size = entry.TarHeader.Name.Length; longHeader.WriteHeader(blockBuffer); _buffer.WriteBlock(blockBuffer); // Add special long filename header block var nameCharIndex = 0; while (nameCharIndex < entry.TarHeader.Name.Length) { Array.Clear(blockBuffer, 0, blockBuffer.Length); TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, blockBuffer, 0, TarBuffer.BlockSize); nameCharIndex += TarBuffer.BlockSize; _buffer.WriteBlock(blockBuffer); } } entry.WriteEntryHeader(blockBuffer); _buffer.WriteBlock(blockBuffer); currBytes = 0; currSize = entry.IsDirectory ? 0 : entry.Size; }
/// <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) { if (header == null) { throw new ArgumentNullException("header"); } _header = (TarHeader) header.Clone(); }
/// <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 (hasHitEOF) { return null; } if (currentEntry != null) { SkipToNextEntry(); } var headerBuf = _buffer.ReadBlock(); if (headerBuf == null) { hasHitEOF = true; } else if (_buffer.IsEOFBlock(headerBuf)) { hasHitEOF = true; } if (hasHitEOF) { currentEntry = null; } else { try { var header = new TarHeader(); header.ParseBuffer(headerBuf); if (!header.IsChecksumValid) { throw new TarException("Header checksum is invalid"); } entryOffset = 0; entrySize = header.Size; StringBuilder longName = null; if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) { var nameBuffer = new byte[TarBuffer.BlockSize]; var numToRead = entrySize; longName = new StringBuilder(); while (numToRead > 0) { var numRead = 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 = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GHDR) { // POSIX global extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_XHDR) { // POSIX extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) { // TODO: could show volume name when verbose SkipToNextEntry(); headerBuf = _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 = _buffer.ReadBlock(); } if (entryFactory == null) { currentEntry = new TarEntry(headerBuf); if (longName != null) { currentEntry.Name = longName.ToString(); } } else { currentEntry = entryFactory.CreateEntry(headerBuf); } // Magic was checked here for 'ustar' but there are multiple valid possibilities // so this is not done anymore. entryOffset = 0; // TODO: Review How do we resolve this discrepancy?! entrySize = currentEntry.Size; } catch (InvalidHeaderException ex) { entrySize = 0; entryOffset = 0; currentEntry = null; var errorText = string.Format("Bad header in record {0} block {1} {2}", _buffer.CurrentRecord, _buffer.CurrentBlock, ex.Message); throw new InvalidHeaderException(errorText); } } return currentEntry; }
/// <summary> /// Construct an entry from an archive's _header bytes. File is set /// to null. /// </summary> /// <param name = "headerBuffer"> /// The _header bytes from a tar archive entry. /// </param> public TarEntry(byte[] headerBuffer) { _header = new TarHeader(); _header.ParseBuffer(headerBuffer); }
/// <summary> /// Initialise a default instance of <see cref="TarEntry"/>. /// </summary> private TarEntry() { _header = new TarHeader(); }
/// <summary> /// Fill in a TarHeader given only the entry's name. /// </summary> /// <param name="header"> /// The TarHeader to fill in. /// </param> /// <param name="name"> /// The tar entry name. /// </param> public static void NameTarHeader(TarHeader header, string name) { if (header == null) { throw new ArgumentNullException("header"); } if (name == null) { throw new ArgumentNullException("name"); } var isDir = name.EndsWith("/"); header.Name = name; header.Mode = isDir ? 1003 : 33216; header.UserId = 0; header.GroupId = 0; header.Size = 0; header.ModTime = DateTime.UtcNow; header.TypeFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; header.LinkName = String.Empty; header.UserName = String.Empty; header.GroupName = String.Empty; header.DevMajor = 0; header.DevMinor = 0; }
/// <summary> /// Fill in a TarHeader with information from a File. /// </summary> /// <param name="header"> /// The TarHeader to fill in. /// </param> /// <param name="file"> /// The _file from which to get the _header information. /// </param> public void GetFileTarHeader(TarHeader header, string file) { if (header == null) { throw new ArgumentNullException("header"); } if (file == null) { throw new ArgumentNullException("file"); } _file = file; // bugfix from torhovl from #D forum: var name = file; // 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); } /* 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); } header.LinkName = String.Empty; header.Name = name; if (Directory.Exists(file)) { header.Mode = 1003; // Magic number for security access for a UNIX filesystem header.TypeFlag = TarHeader.LF_DIR; if ((header.Name.Length == 0) || header.Name[header.Name.Length - 1] != '/') { header.Name = header.Name + "/"; } header.Size = 0; } else { header.Mode = 33216; // Magic number for security access for a UNIX filesystem header.TypeFlag = TarHeader.LF_NORMAL; header.Size = new FileInfo(file.Replace('/', Path.DirectorySeparatorChar)).Length; } header.ModTime = System.IO.File.GetLastWriteTime(file.Replace('/', Path.DirectorySeparatorChar)).ToUniversalTime(); header.DevMajor = 0; header.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 (hasHitEOF) { return(null); } if (currentEntry != null) { SkipToNextEntry(); } var headerBuf = _buffer.ReadBlock(); if (headerBuf == null) { hasHitEOF = true; } else if (_buffer.IsEOFBlock(headerBuf)) { hasHitEOF = true; } if (hasHitEOF) { currentEntry = null; } else { try { var header = new TarHeader(); header.ParseBuffer(headerBuf); if (!header.IsChecksumValid) { throw new TarException("Header checksum is invalid"); } entryOffset = 0; entrySize = header.Size; StringBuilder longName = null; if (header.TypeFlag == TarHeader.LF_GNU_LONGNAME) { var nameBuffer = new byte[TarBuffer.BlockSize]; var numToRead = entrySize; longName = new StringBuilder(); while (numToRead > 0) { var numRead = 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 = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GHDR) { // POSIX global extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_XHDR) { // POSIX extended header // Ignore things we dont understand completely for now SkipToNextEntry(); headerBuf = _buffer.ReadBlock(); } else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR) { // TODO: could show volume name when verbose SkipToNextEntry(); headerBuf = _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 = _buffer.ReadBlock(); } if (entryFactory == null) { currentEntry = new TarEntry(headerBuf); if (longName != null) { currentEntry.Name = longName.ToString(); } } else { currentEntry = entryFactory.CreateEntry(headerBuf); } // Magic was checked here for 'ustar' but there are multiple valid possibilities // so this is not done anymore. entryOffset = 0; // TODO: Review How do we resolve this discrepancy?! entrySize = currentEntry.Size; } catch (InvalidHeaderException ex) { entrySize = 0; entryOffset = 0; currentEntry = null; var errorText = string.Format("Bad header in record {0} block {1} {2}", _buffer.CurrentRecord, _buffer.CurrentBlock, ex.Message); throw new InvalidHeaderException(errorText); } } return(currentEntry); }