Beispiel #1
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();
            }

            byte[] headerBuf = tarBuffer.ReadBlock();

            if (headerBuf == null)
            {
                hasHitEOF = true;
            }
            else if (TarBuffer.IsEndOfArchiveBlock(headerBuf))
            {
                hasHitEOF = true;

                // Read the second zero-filled block
                tarBuffer.ReadBlock();
            }
            else
            {
                hasHitEOF = false;
            }

            if (hasHitEOF)
            {
                currentEntry = null;
            }
            else
            {
                try
                {
                    var header = new TarHeader();
                    header.ParseBuffer(headerBuf, encoding);
                    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, encoding).ToString());
                            numToRead -= numRead;
                        }

                        SkipToNextEntry();
                        headerBuf = this.tarBuffer.ReadBlock();
                    }
                    else if (header.TypeFlag == TarHeader.LF_GHDR)
                    {                      // POSIX global extended header
                                           // Ignore things we dont understand completely for now
                        SkipToNextEntry();
                        headerBuf = this.tarBuffer.ReadBlock();
                    }
                    else if (header.TypeFlag == TarHeader.LF_XHDR)
                    {                      // POSIX extended header
                        byte[] nameBuffer = new byte[TarBuffer.BlockSize];
                        long   numToRead  = this.entrySize;

                        var xhr = new TarExtendedHeaderReader();

                        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");
                            }

                            xhr.Read(nameBuffer, numRead);
                            numToRead -= numRead;
                        }

                        if (xhr.Headers.TryGetValue("path", out string name))
                        {
                            longName = new StringBuilder(name);
                        }

                        SkipToNextEntry();
                        headerBuf = this.tarBuffer.ReadBlock();
                    }
                    else if (header.TypeFlag == TarHeader.LF_GNU_VOLHDR)
                    {
                        // TODO: could show volume name when verbose
                        SkipToNextEntry();
                        headerBuf = this.tarBuffer.ReadBlock();
                    }
                    else if (header.TypeFlag != TarHeader.LF_NORMAL &&
                             header.TypeFlag != TarHeader.LF_OLDNORM &&
                             header.TypeFlag != TarHeader.LF_LINK &&
                             header.TypeFlag != TarHeader.LF_SYMLINK &&
                             header.TypeFlag != TarHeader.LF_DIR)
                    {
                        // Ignore things we dont understand completely for now
                        SkipToNextEntry();
                        headerBuf = tarBuffer.ReadBlock();
                    }

                    if (entryFactory == null)
                    {
                        currentEntry = new TarEntry(headerBuf, encoding);
                        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 = this.currentEntry.Size;
                }
                catch (InvalidHeaderException ex)
                {
                    entrySize    = 0;
                    entryOffset  = 0;
                    currentEntry = null;
                    string errorText = string.Format("Bad header in record {0} block {1} {2}",
                                                     tarBuffer.CurrentRecord, tarBuffer.CurrentBlock, ex.Message);
                    throw new InvalidHeaderException(errorText);
                }
            }
            return(currentEntry);
        }
Beispiel #2
0
        /// <summary>
        /// Parse TarHeader information from a header buffer.
        /// </summary>
        /// <param name = "header">
        /// The tar entry header buffer to get information from.
        /// </param>
        /// <param name = "nameEncoding">
        /// The <see cref="Encoding"/> used for the Name field, or null for ASCII only
        /// </param>
        public void ParseBuffer(byte[] header, Encoding nameEncoding)
        {
            if (header == null)
            {
                throw new ArgumentNullException(nameof(header));
            }

            int offset = 0;

            name    = ParseName(header, offset, NAMELEN, nameEncoding).ToString();
            offset += NAMELEN;

            mode    = (int)ParseOctal(header, offset, MODELEN);
            offset += MODELEN;

            UserId  = (int)ParseOctal(header, offset, UIDLEN);
            offset += UIDLEN;

            GroupId = (int)ParseOctal(header, offset, GIDLEN);
            offset += GIDLEN;

            Size    = ParseBinaryOrOctal(header, offset, SIZELEN);
            offset += SIZELEN;

            ModTime = GetDateTimeFromCTime(ParseOctal(header, offset, MODTIMELEN));
            offset += MODTIMELEN;

            checksum = (int)ParseOctal(header, offset, CHKSUMLEN);
            offset  += CHKSUMLEN;

            TypeFlag = header[offset++];

            LinkName = ParseName(header, offset, NAMELEN, nameEncoding).ToString();
            offset  += NAMELEN;

            Magic   = ParseName(header, offset, MAGICLEN, nameEncoding).ToString();
            offset += MAGICLEN;

            if (Magic == "ustar")
            {
                Version = ParseName(header, offset, VERSIONLEN, nameEncoding).ToString();
                offset += VERSIONLEN;

                UserName = ParseName(header, offset, UNAMELEN, nameEncoding).ToString();
                offset  += UNAMELEN;

                GroupName = ParseName(header, offset, GNAMELEN, nameEncoding).ToString();
                offset   += GNAMELEN;

                DevMajor = (int)ParseOctal(header, offset, DEVLEN);
                offset  += DEVLEN;

                DevMinor = (int)ParseOctal(header, offset, DEVLEN);
                offset  += DEVLEN;

                string prefix = ParseName(header, offset, PREFIXLEN, nameEncoding).ToString();
                if (!string.IsNullOrEmpty(prefix))
                {
                    Name = prefix + '/' + Name;
                }
            }

            isChecksumValid = Checksum == TarHeader.MakeCheckSum(header);
        }
Beispiel #3
0
 /// <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>
 /// <param name = "nameEncoding">
 /// The <see cref="Encoding"/> used for the Name fields, or null for ASCII only
 /// </param>
 public TarEntry(byte[] headerBuffer, Encoding nameEncoding)
 {
     header = new TarHeader();
     header.ParseBuffer(headerBuffer, nameEncoding);
 }
Beispiel #4
0
 /// <summary>
 /// Convenience method that will modify an entry's name directly
 /// in place in an entry header buffer byte array.
 /// </summary>
 /// <param name="buffer">
 /// The buffer containing the entry header to modify.
 /// </param>
 /// <param name="newName">
 /// The new name to place into the header buffer.
 /// </param>
 /// <param name="nameEncoding">
 /// The <see cref="Encoding"/> used for the Name fields, or null for ASCII only
 /// </param>
 static public void AdjustEntryName(byte[] buffer, string newName, Encoding nameEncoding)
 {
     TarHeader.GetNameBytes(newName, buffer, 0, TarHeader.NAMELEN, nameEncoding);
 }
Beispiel #5
0
 /// <summary>
 /// Initialise a default instance of <see cref="TarEntry"/>.
 /// </summary>
 private TarEntry()
 {
     header = new TarHeader();
 }
Beispiel #6
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(nameof(header));
            }

            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            this.file = file;

            // bugfix from torhovl from #D forum:
            string name = file;

            // 23-Jan-2004 GnuTar allows device names in path where the name is not local to the current directory
            if (name.IndexOf(Directory.GetCurrentDirectory(), StringComparison.Ordinal) == 0)
            {
                name = name.Substring(Directory.GetCurrentDirectory().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("/", StringComparison.Ordinal))
            {
                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>
 /// Add the checksum integer to header buffer.
 /// </summary>
 /// <param name = "value"></param>
 /// <param name = "buffer">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>
 static int GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length)
 {
     TarHeader.GetOctalBytes(value, buffer, offset, length - 1);
     return(offset + length);
 }