protected override byte[] GetBuffer() { byte[] buffer = new byte[TarHeader.Length]; TarHeader header = new TarHeader(); header.FileName = _fileName; header.FileLength = _fileLength; header.WriteTo(buffer, 0); return buffer; }
/// <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> static public void NameTarHeader(TarHeader header, string name) { if ( header == null ) { throw new ArgumentNullException("header"); } if ( name == null ) { throw new ArgumentNullException("name"); } bool 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"); } 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); } 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; }
public override int Read(byte[] buffer, int offset, int count) { int origCount = count; while (count > 0) { if (currentEntryLength > 0 && currentEntryPosition >= currentEntryLength) // Next entry { currentEntry++; currentEntryPosition = 0; } if (currentEntry >= entries.Count) // End of archive, write zeros { if (currentFile != null) { if (currentFileOwned) { currentFile.Dispose(); } currentFile = null; currentFileOwned = false; } long l = Math.Min(count, totalSize - position); var dummy = new byte[l]; Array.Copy(dummy, 0, buffer, offset, l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; break; } if (currentEntryPosition == 0) // New entry { currentEntryLength = 512; var header = new TarHeader(); var fileName = entries[currentEntry].FileName ?? entries[currentEntry].ExtInfo.FilePath; if (fileName != LongLinkFlag) { header.FileName = fileName.Substring(rootDirectory.Length + 1).Replace(@"\", "/"); } if (currentFile != null) { if (currentFileOwned) { currentFile.Dispose(); } currentFile = null; currentFileOwned = false; } if (fileName == LongLinkFlag) { header.FileName = fileName; header.FileMode = "0000000"; fileName = (entries[currentEntry + 1].FileName ?? entries[currentEntry + 1].ExtInfo.FilePath).Substring(rootDirectory.Length + 1).Replace(@"\", "/"); var nameBuff = Encoding.UTF8.GetBytes(fileName); currentFile = new MemoryStream(nameBuff.Length + 1); currentFile.Write(nameBuff, 0, nameBuff.Length); currentFile.WriteByte(0); currentFile.Seek(0, SeekOrigin.Begin); currentEntryLength += currentFile.Length; if (currentFile.Length % 512 != 0) { currentEntryLength += 512 - (currentFile.Length % 512); } header.FileSize = Convert.ToString(currentFile.Length, 8).PadLeft(11, '0'); header.LastModificationTime = "0".PadLeft(11, '0'); header.FileType = 'L'; } else if (!header.FileName.EndsWith("/")) // It's a file! { string localFilePath = entries[currentEntry].FileName ?? entries[currentEntry].ExtInfo.LocalFilePath; DateTime lastWriteTimeUtc; if (localFilePath != null) // Standard file { currentFile = new FileStream(localFilePath, FileMode.Open); currentFileOwned = true; lastWriteTimeUtc = new FileInfo(localFilePath).LastWriteTimeUtc; } else // file link { currentFile = entries[currentEntry].ExtInfo.FileStream ?? new MemoryStream(); try { currentFile.Position = 0; } catch { } currentFileOwned = entries[currentEntry].ExtInfo.FileStream == null; lastWriteTimeUtc = entries[currentEntry].ExtInfo.ModifiedTime; } header.FileMode = "0100644"; currentEntryLength += currentFile.Length; if (currentFile.Length % 512 != 0) { currentEntryLength += 512 - (currentFile.Length % 512); } header.FileSize = Convert.ToString(currentFile.Length, 8).PadLeft(11, '0'); header.LastModificationTime = Convert.ToString( (long)lastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds , 8).PadLeft(11, '0'); header.FileType = '0'; this.currentFileName = header.FileName; // keep filename for advanced progress report } else if (header.FileName.EndsWith("/")) // It's a directory... { DateTime lastWriteTimeUtc = entries[currentEntry].FileName != null ? new DirectoryInfo(entries[currentEntry].FileName).LastWriteTimeUtc : DateTime.UtcNow; header.FileMode = "0040755"; header.FileSize = "".PadLeft(11, '0'); header.LastModificationTime = Convert.ToString( (long)lastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds , 8).PadLeft(11, '0'); header.FileType = '5'; } header.OwnerID = "0000000"; header.GroupID = "0000000"; header.UstarMagic = "ustar "; //header.UstarVersion = new char[] {'0', '0'}; header.CalcChecksum(); currentHeader = header.GetBytes(); } if (currentEntryPosition < 512) // Header { long l = Math.Min(count, 512 - currentEntryPosition); Array.Copy(currentHeader, currentEntryPosition, buffer, offset, l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; } else // Data { long l = Math.Min(count, currentEntryLength - currentEntryPosition); currentFile.Read(buffer, offset, (int)l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; } } OnReadProgress(Position, Length); OnAdvancedReadProgress(Position, Length, this.currentFileName); return(origCount - count); }
// Constructor called when reading a TarEntry from a TarReader. internal UstarTarEntry(TarHeader header, TarReader readerOfOrigin) : base(header, readerOfOrigin, TarEntryFormat.Ustar) { }
internal TarFilePart(TarHeader header, Stream seekableStream) { this.seekableStream = seekableStream; this.Header = header; }
public override bool AddFile(string filename, string tar_filename = null) { /* No tar within tar. */ Debug.Assert(tar_filename == null); /* The TAR-header, repeated for every file */ /* Check if we already seen this file */ if (FileIO._tar_list[(int)this.subdir].TryGetValue(filename, out var it) == false) { return(false); } var links = new Dictionary <string, string>(); try { using (var f = new FileInfo(filename).OpenRead()) { FileIO._tar_list[(int)this.subdir][filename].filename = filename; FileIO._tar_list[(int)this.subdir][filename].dirname = null; var dupped_filename = filename; /// Temporary list to collect links string name = null; //char[sizeof(th.prefix) + 1 + sizeof(th.name) + 1]; string link = null; //char link[sizeof(th.linkname) + 1]; string dest = null; //char dest[sizeof(th.prefix) + 1 + sizeof(th.name) + 1 + 1 + sizeof(th.linkname) + 1]; var num = 0; var pos = 0; var buffer = new byte[TarHeader.HeaderSize]; for (;;) { // Note: feof() always returns 'false' after 'fseek()'. Cool, isn't it? var num_bytes_read = f.Read(buffer, 1, TarHeader.HeaderSize); if (num_bytes_read != TarHeader.HeaderSize) { break; } pos += num_bytes_read; var th = new TarHeader(buffer); /* Check if we have the new tar-format (ustar) or the old one (a lot of zeros after 'link' field) */ if (th.magic != "ustar" && th.magic != "") { /* If we have only zeros in the block, it can be an end-of-file indicator */ if (buffer.Any(b => b != 0)) { continue; } Log.Debug($"The file '{filename}' isn't a valid tar-file"); f.Close(); return(false); } name = null; /* The prefix contains the directory-name */ if (th.prefix != "") { name = th.prefix + Path.PathSeparator; } /* Copy the name of the file in a safe way at the end of 'name' */ name += th.name; switch (th.typeflag) { case '\0': case '0': { // regular file /* Ignore empty files */ if (th.size == 0) { break; } if (name.Length == 0) { break; } /* Store this entry in the list */ var entry = new TarFileListEntry() { tar_filename = dupped_filename, size = th.size, position = pos }; /* Convert to lowercase and our PATHSEPCHAR */ name = FileIO.SimplifyFileName(name); Log.Debug($"Found file in tar: ${name} ({th.size} bytes, {pos} offset)"); if (FileIO._tar_filelist[(int)this.subdir].ContainsKey(name) == false) { FileIO._tar_filelist[(int)this.subdir].Add(name, entry); num++; } break; } case '1': // hard links case '2': { // symbolic links /* Copy the destination of the link in a safe way at the end of 'linkname' */ link = th.linkname; if (name.Length == 0 || link.Length == 0) { break; } /* Convert to lowercase and our PATHSEPCHAR */ name = FileIO.SimplifyFileName(name); link = FileIO.SimplifyFileName(link); /* Only allow relative links */ if (link[0] == Path.PathSeparator) { Log.Debug($"Ignoring absolute link in tar: {name} . {link}"); break; } /* Process relative path. * Note: The destination of links must not contain any directory-links. */ dest = name; //var destIndex = dest.LastIndexOf(Path.PathSeparator); var destpos = dest; //if (destIndex >= 0) //{ // destpos = dest.Substring(destIndex + 1); //} //TODO THIS MAKES NO SENSE var linkParts = link.Split(Path.PathSeparator); foreach (var linkPart in linkParts) { if (linkPart == ".") { /* Skip '.' (current dir) */ } else if (linkPart == "..") { /* level up */ if (dest == "") { Log.Debug( $"Ignoring link pointing outside of data directory: {name} . {link}"); break; } /* Truncate 'dest' after last PATHSEPCHAR. * This assumes that the truncated part is a real directory and not a link. */ destpos = linkParts.Last(); break; } else { /* Append at end of 'dest' */ if (destpos.Any()) { destpos += Path.PathSeparator; } destpos = dest; } //if (destpos >= lastof(dest)) { // Log.Debug("The length of a link in tar-file '{filename}' is too large (malformed?)"); // f.Close(); // return false; //} } /* Store links in temporary list */ Log.Debug($"Found link in tar: {name} . {dest}"); links.Add(name, dest); break; } case '5': // directory /* Convert to lowercase and our PATHSEPCHAR */ name = FileIO.SimplifyFileName(name); /* Store the first directory name we detect */ Log.Debug($"Found dir in tar: {name}"); if (FileIO._tar_list[(int)this.subdir][filename].dirname == null) { FileIO._tar_list[(int)this.subdir][filename].dirname = name; } break; default: /* Ignore other types */ break; } /* Skip to the next block.. */ //var skip = Align(th.size, 512); if (f.Seek(th.size, SeekOrigin.Current) < 0) { Log.Debug($"The file '{filename}' can't be read as a valid tar-file"); f.Close(); return(false); } pos += th.size; } Log.Debug($"Found tar '{filename}' with {num} new files"); f.Close(); } } catch (IOException ex) { /* Although the file has been found there can be * a number of reasons we cannot open the file. * Most common case is when we simply have not * been given read access. */ Log.Error(ex); return(false); } /* Resolve file links and store directory links. * We restrict usage of links to two cases: * 1) Links to directories: * Both the source path and the destination path must NOT contain any further links. * When resolving files at most one directory link is resolved. * 2) Links to files: * The destination path must NOT contain any links. * The source path may contain one directory link. */ foreach (var link in links) { var src = link.Key; var dest = link.Value; FileIO.TarAddLink(src, dest, this.subdir); } return(true); }
internal TarFilePart(TarHeader header, Stream seekableStream) : base(header.ArchiveEncoding) { _seekableStream = seekableStream; Header = header; }
public override int Read(byte[] buffer, int offset, int count) { int origCount = count; while (count > 0) { if (currentEntryLength > 0 && currentEntryPosition >= currentEntryLength) // Next entry { currentEntry++; currentEntryPosition = 0; } if (currentEntry >= entries.Count) // end of archive, write zeros { if (currentFile != null) { currentFile.Dispose(); currentFile = null; } long l = Math.Min(count, totalSize - position); var dummy = new byte[l]; Array.Copy(dummy, 0, buffer, offset, l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; break; } if (currentEntryPosition == 0) // New entry { currentEntryLength = 512; var header = new TarHeader(); if (entries[currentEntry] != LongLinkFlag) { header.FileName = entries[currentEntry].Substring(rootDirectory.Length + 1).Replace(@"\", "/"); } if (currentFile != null) { currentFile.Dispose(); currentFile = null; } if (entries[currentEntry] == LongLinkFlag) { header.FileName = entries[currentEntry]; header.FileMode = "0000000"; var name = entries[currentEntry + 1].Substring(rootDirectory.Length + 1).Replace(@"\", "/"); var nameBuff = Encoding.UTF8.GetBytes(name); currentFile = new MemoryStream(nameBuff.Length + 1); currentFile.Write(nameBuff, 0, nameBuff.Length); currentFile.WriteByte(0); currentFile.Seek(0, SeekOrigin.Begin); currentEntryLength += currentFile.Length; if (currentFile.Length % 512 != 0) { currentEntryLength += 512 - (currentFile.Length % 512); } header.FileSize = Convert.ToString(currentFile.Length, 8).PadLeft(11, '0'); header.LastModificationTime = "0".PadLeft(11, '0'); header.FileType = 'L'; } else if (!header.FileName.EndsWith("/")) // It's a file! { currentFile = new FileStream(entries[currentEntry], FileMode.Open); header.FileMode = "0100644"; currentEntryLength += currentFile.Length; if (currentFile.Length % 512 != 0) { currentEntryLength += 512 - (currentFile.Length % 512); } header.FileSize = Convert.ToString(currentFile.Length, 8).PadLeft(11, '0'); header.LastModificationTime = Convert.ToString( (long)new FileInfo(entries[currentEntry]).LastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds , 8).PadLeft(11, '0'); header.FileType = '0'; } else if (header.FileName.EndsWith("/")) // It's a directory... { header.FileMode = "0040755"; header.FileSize = "".PadLeft(11, '0'); header.LastModificationTime = Convert.ToString( (long)new DirectoryInfo(entries[currentEntry]).LastWriteTimeUtc.Subtract(new DateTime(1970, 1, 1)).TotalSeconds , 8).PadLeft(11, '0'); header.FileType = '5'; } header.OwnerID = "0000000"; header.GroupID = "0000000"; header.UstarMagic = "ustar "; //header.UstarVersion = new char[] {'0', '0'}; header.CalcChecksum(); currentHeader = header.getBytes(); } if (currentEntryPosition < 512) // Header { long l = Math.Min(count, 512 - currentEntryPosition); Array.Copy(currentHeader, currentEntryPosition, buffer, offset, l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; } else // Data { long l = Math.Min(count, currentEntryLength - currentEntryPosition); currentFile.Read(buffer, offset, (int)l); count -= (int)l; position += l; currentEntryPosition += l; offset += (int)l; } } OnReadProgress(Position, Length); return(origCount - count); }
/// <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) { 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.blockBuffer); this.buffer.WriteBlock(this.blockBuffer); // Add special long filename header block int nameCharIndex = 0; while (nameCharIndex < entry.TarHeader.Name.Length) { Array.Clear(blockBuffer, 0, blockBuffer.Length); TarHeader.GetAsciiBytes(entry.TarHeader.Name, nameCharIndex, this.blockBuffer, 0, TarBuffer.BlockSize); nameCharIndex += TarBuffer.BlockSize; buffer.WriteBlock(blockBuffer); } } entry.WriteEntryHeader(blockBuffer); buffer.WriteBlock(blockBuffer); currBytes = 0; currSize = entry.IsDirectory ? 0 : entry.Size; }
// Constructor used when reading an existing archive. internal V7TarEntry(TarHeader header, TarReader readerOfOrigin) : base(header, readerOfOrigin) { }
/// <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.currentEntry != null) { SkipToNextEntry(); } byte[] headerBuf = this.buffer.ReadBlock(); if (headerBuf == null) { this.hasHitEOF = true; } else if (buffer.IsEOFBlock(headerBuf)) { this.hasHitEOF = true; } if (this.hasHitEOF) { this.currentEntry = 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.entryFactory == null) { this.currentEntry = new TarEntry(headerBuf); if (longName != null) { currentEntry.Name = longName.ToString(); } } else { this.currentEntry = this.entryFactory.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.currentEntry.Size; } catch (InvalidHeaderException ex) { this.entrySize = 0; this.entryOffset = 0; this.currentEntry = null; string errorText = string.Format("Bad header in record {0} block {1} {2}", buffer.CurrentRecord, buffer.CurrentBlock, ex.Message); throw new InvalidHeaderException(errorText); } } return this.currentEntry; }
internal TarFilePart(TarHeader header) { this.Header = header; }
/// <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"); } this.header = (TarHeader)header.Clone(); }
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)); }
// Constructor called when reading a TarEntry from a TarReader. internal GnuTarEntry(TarHeader header, TarReader readerOfOrigin) : base(header, readerOfOrigin, TarEntryFormat.Gnu) { }