diff Tar/TarHeader.cs @ 1:94e25b786321

Re #311: can't read ZIP file packed by Linux app Archive Manager/File Roller Initial commit of clean SharpZipLib 0860 source. Only change is build paths.
author IBBoard <dev@ibboard.co.uk>
date Sat, 30 Oct 2010 14:03:17 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Tar/TarHeader.cs	Sat Oct 30 14:03:17 2010 +0000
@@ -0,0 +1,1156 @@
+// TarHeader.cs
+//
+// Copyright (C) 2001 Mike Krueger
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// Linking this library statically or dynamically with other modules is
+// making a combined work based on this library.  Thus, the terms and
+// conditions of the GNU General Public License cover the whole
+// combination.
+// 
+// As a special exception, the copyright holders of this library give you
+// permission to link this library with independent modules to produce an
+// executable, regardless of the license terms of these independent
+// modules, and to copy and distribute the resulting executable under
+// terms of your choice, provided that you also meet, for each linked
+// independent module, the terms and conditions of the license of that
+// module.  An independent module is a module which is not derived from
+// or based on this library.  If you modify this library, you may extend
+// this exception to your version of the library, but you are not
+// obligated to do so.  If you do not wish to do so, delete this
+// exception statement from your version.
+
+
+/* The tar format and its POSIX successor PAX have a long history which makes for compatability
+   issues when creating and reading files.
+
+   This is further complicated by a large number of programs with variations on formats
+   One common issue is the handling of names longer than 100 characters.
+   GNU style long names are currently supported.
+
+This is the ustar (Posix 1003.1) header.
+
+struct header 
+{
+	char t_name[100];          //   0 Filename
+	char t_mode[8];            // 100 Permissions
+	char t_uid[8];             // 108 Numerical User ID
+	char t_gid[8];             // 116 Numerical Group ID
+	char t_size[12];           // 124 Filesize
+	char t_mtime[12];          // 136 st_mtime
+	char t_chksum[8];          // 148 Checksum
+	char t_typeflag;           // 156 Type of File
+	char t_linkname[100];      // 157 Target of Links
+	char t_magic[6];           // 257 "ustar" or other...
+	char t_version[2];         // 263 Version fixed to 00
+	char t_uname[32];          // 265 User Name
+	char t_gname[32];          // 297 Group Name
+	char t_devmajor[8];        // 329 Major for devices
+	char t_devminor[8];        // 337 Minor for devices
+	char t_prefix[155];        // 345 Prefix for t_name
+	char t_mfill[12];          // 500 Filler up to 512
+};
+
+*/
+
+using System;
+using System.Text;
+
+namespace ICSharpCode.SharpZipLib.Tar 
+{
+	
+	
+	/// <summary>
+	/// This class encapsulates the Tar Entry Header used in Tar Archives.
+	/// The class also holds a number of tar constants, used mostly in headers.
+	/// </summary>
+	public class TarHeader : ICloneable
+	{
+		#region Constants
+		/// <summary>
+		/// The length of the name field in a header buffer.
+		/// </summary>
+		public const int NAMELEN = 100;
+		
+		/// <summary>
+		/// The length of the mode field in a header buffer.
+		/// </summary>
+		public const int MODELEN = 8;
+		
+		/// <summary>
+		/// The length of the user id field in a header buffer.
+		/// </summary>
+		public const int UIDLEN = 8;
+		
+		/// <summary>
+		/// The length of the group id field in a header buffer.
+		/// </summary>
+		public const int GIDLEN = 8;
+		
+		/// <summary>
+		/// The length of the checksum field in a header buffer.
+		/// </summary>
+		public const int CHKSUMLEN = 8;
+
+		/// <summary>
+		/// Offset of checksum in a header buffer.
+		/// </summary>
+		public const int CHKSUMOFS = 148;
+		
+		/// <summary>
+		/// The length of the size field in a header buffer.
+		/// </summary>
+		public const int SIZELEN = 12;
+		
+		/// <summary>
+		/// The length of the magic field in a header buffer.
+		/// </summary>
+		public const int MAGICLEN = 6;
+		
+		/// <summary>
+		/// The length of the version field in a header buffer.
+		/// </summary>
+		public const int VERSIONLEN = 2;
+
+		/// <summary>
+		/// The length of the modification time field in a header buffer.
+		/// </summary>
+		public const int MODTIMELEN = 12;
+		
+		/// <summary>
+		/// The length of the user name field in a header buffer.
+		/// </summary>
+		public const int UNAMELEN = 32;
+		
+		/// <summary>
+		/// The length of the group name field in a header buffer.
+		/// </summary>
+		public const int GNAMELEN = 32;
+		
+		/// <summary>
+		/// The length of the devices field in a header buffer.
+		/// </summary>
+		public const int DEVLEN = 8;
+		
+		//
+		// LF_ constants represent the "type" of an entry
+		//
+
+		/// <summary>
+		///  The "old way" of indicating a normal file.
+		/// </summary>
+		public const byte	LF_OLDNORM	= 0;
+		
+		/// <summary>
+		/// Normal file type.
+		/// </summary>
+		public const byte	LF_NORMAL	= (byte) '0';
+		
+		/// <summary>
+		/// Link file type.
+		/// </summary>
+		public const byte	LF_LINK		= (byte) '1';
+		
+		/// <summary>
+		/// Symbolic link file type.
+		/// </summary>
+		public const byte	LF_SYMLINK	= (byte) '2';
+		
+		/// <summary>
+		/// Character device file type.
+		/// </summary>
+		public const byte	LF_CHR		= (byte) '3';
+		
+		/// <summary>
+		/// Block device file type.
+		/// </summary>
+		public const byte	LF_BLK		= (byte) '4';
+		
+		/// <summary>
+		/// Directory file type.
+		/// </summary>
+		public const byte	LF_DIR		= (byte) '5';
+		
+		/// <summary>
+		/// FIFO (pipe) file type.
+		/// </summary>
+		public const byte	LF_FIFO		= (byte) '6';
+		
+		/// <summary>
+		/// Contiguous file type.
+		/// </summary>
+		public const byte	LF_CONTIG	= (byte) '7';
+		
+		/// <summary>
+		/// Posix.1 2001 global extended header
+		/// </summary>
+		public const byte   LF_GHDR    = (byte) 'g';
+		
+		/// <summary>
+		/// Posix.1 2001 extended header
+		/// </summary>
+		public const byte   LF_XHDR    = (byte) 'x';
+		
+		// POSIX allows for upper case ascii type as extensions
+		
+		/// <summary>
+		/// Solaris access control list file type
+		/// </summary>
+		public const byte   LF_ACL            = (byte) 'A';
+		
+		/// <summary>
+		/// GNU dir dump file type
+		/// This is a dir entry that contains the names of files that were in the
+		/// dir at the time the dump was made
+		/// </summary>
+		public const byte   LF_GNU_DUMPDIR    = (byte) 'D';
+		
+		/// <summary>
+		/// Solaris Extended Attribute File
+		/// </summary>
+		public const byte   LF_EXTATTR        = (byte) 'E' ;
+		
+		/// <summary>
+		/// Inode (metadata only) no file content
+		/// </summary>
+		public const byte   LF_META           = (byte) 'I';
+		
+		/// <summary>
+		/// Identifies the next file on the tape as having a long link name
+		/// </summary>
+		public const byte   LF_GNU_LONGLINK   = (byte) 'K';
+		
+		/// <summary>
+		/// Identifies the next file on the tape as having a long name
+		/// </summary>
+		public const byte   LF_GNU_LONGNAME   = (byte) 'L';
+		
+		/// <summary>
+		/// Continuation of a file that began on another volume
+		/// </summary>
+		public const byte   LF_GNU_MULTIVOL   = (byte) 'M';
+		
+		/// <summary>
+		/// For storing filenames that dont fit in the main header (old GNU)
+		/// </summary>
+		public const byte   LF_GNU_NAMES      = (byte) 'N';
+		
+		/// <summary>
+		/// GNU Sparse file
+		/// </summary>
+		public const byte   LF_GNU_SPARSE     = (byte) 'S';
+		
+		/// <summary>
+		/// GNU Tape/volume header ignore on extraction
+		/// </summary>
+		public const byte   LF_GNU_VOLHDR     = (byte) 'V';
+		
+		/// <summary>
+		/// The magic tag representing a POSIX tar archive.  (includes trailing NULL)
+		/// </summary>
+		public const string	TMAGIC		= "ustar ";
+		
+		/// <summary>
+		/// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it
+		/// </summary>
+		public const string	GNU_TMAGIC	= "ustar  ";
+
+		const long     timeConversionFactor = 10000000L;           // 1 tick == 100 nanoseconds
+		readonly static DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0); 
+		#endregion
+
+		#region Constructors
+
+		/// <summary>
+		/// Initialise a default TarHeader instance
+		/// </summary>
+		public TarHeader()
+		{
+			Magic = TMAGIC;
+			Version = " ";
+			
+			Name     = "";
+			LinkName = "";
+			
+			UserId    = defaultUserId;
+			GroupId   = defaultGroupId;
+			UserName  = defaultUser;
+			GroupName = defaultGroupName;
+			Size      = 0;
+		}
+
+		#endregion
+
+		#region Properties
+		/// <summary>
+		/// Get/set the name for this tar entry.
+		/// </summary>
+		/// <exception cref="ArgumentNullException">Thrown when attempting to set the property to null.</exception>
+		public string Name
+		{
+			get { return name; }
+			set { 
+				if ( value == null ) {
+					throw new ArgumentNullException("value");
+				}
+				name = value;	
+			}
+		}
+
+		/// <summary>
+		/// Get the name of this entry.
+		/// </summary>
+		/// <returns>The entry's name.</returns>
+		[Obsolete("Use the Name property instead", true)]
+		public string GetName()
+		{
+			return name;
+		}
+		
+		/// <summary>
+		/// Get/set the entry's Unix style permission mode.
+		/// </summary>
+		public int Mode
+		{
+			get { return mode; }
+			set { mode = value; }
+		}
+		
+		
+		/// <summary>
+		/// The entry's user id.
+		/// </summary>
+		/// <remarks>
+		/// This is only directly relevant to unix systems.
+		/// The default is zero.
+		/// </remarks>
+		public int UserId
+		{
+			get { return userId; }
+			set { userId = value; }
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's group id.
+		/// </summary>
+		/// <remarks>
+		/// This is only directly relevant to linux/unix systems.
+		/// The default value is zero.
+		/// </remarks>
+		public int GroupId
+		{
+			get { return groupId; }
+			set { groupId = value; }
+		}
+		
+
+		/// <summary>
+		/// Get/set the entry's size.
+		/// </summary>
+		/// <exception cref="ArgumentOutOfRangeException">Thrown when setting the size to less than zero.</exception>
+		public long Size
+		{
+			get { return size; }
+			set { 
+				if ( value < 0 ) {
+#if NETCF_1_0
+					throw new ArgumentOutOfRangeException("value");
+#else
+					throw new ArgumentOutOfRangeException("value", "Cannot be less than zero");
+#endif					
+				}
+				size = value; 
+			}
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's modification time.
+		/// </summary>
+		/// <remarks>
+		/// The modification time is only accurate to within a second.
+		/// </remarks>
+		/// <exception cref="ArgumentOutOfRangeException">Thrown when setting the date time to less than 1/1/1970.</exception>
+		public DateTime ModTime
+		{
+			get { return modTime; }
+			set {
+				if ( value < dateTime1970 )
+				{
+#if NETCF_1_0
+					throw new ArgumentOutOfRangeException("value");
+#else
+					throw new ArgumentOutOfRangeException("value", "ModTime cannot be before Jan 1st 1970");
+#endif					
+				}
+				modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second);
+			}
+		}
+		
+		
+		/// <summary>
+		/// Get the entry's checksum.  This is only valid/updated after writing or reading an entry.
+		/// </summary>
+		public int Checksum
+		{
+			get { return checksum; }
+		}
+		
+		
+		/// <summary>
+		/// Get value of true if the header checksum is valid, false otherwise.
+		/// </summary>
+		public bool IsChecksumValid
+		{
+			get { return isChecksumValid; }
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's type flag.
+		/// </summary>
+		public byte TypeFlag
+		{
+			get { return typeFlag; }
+			set { typeFlag = value; }
+		}
+
+		
+		/// <summary>
+		/// The entry's link name.
+		/// </summary>
+		/// <exception cref="ArgumentNullException">Thrown when attempting to set LinkName to null.</exception>
+		public string LinkName
+		{
+			get { return linkName; }
+			set {
+				if ( value == null ) {
+					throw new ArgumentNullException("value");
+				}
+				linkName = value; 
+			}
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's magic tag.
+		/// </summary>
+		/// <exception cref="ArgumentNullException">Thrown when attempting to set Magic to null.</exception>
+		public string Magic
+		{
+			get { return magic; }
+			set { 
+				if ( value == null ) {
+					throw new ArgumentNullException("value");
+				}
+				magic = value; 
+			}
+		}
+		
+		
+		/// <summary>
+		/// The entry's version.
+		/// </summary>
+		/// <exception cref="ArgumentNullException">Thrown when attempting to set Version to null.</exception>
+		public string Version
+		{
+			get {
+				return version;
+			}
+
+			set { 
+				if ( value == null ) {
+					throw new ArgumentNullException("value");
+				}
+				version = value; 
+			}
+		}
+		
+		
+		/// <summary>
+		/// The entry's user name.
+		/// </summary>
+		public string UserName
+		{
+			get { return userName; }
+			set {
+				if (value != null) {
+					userName = value.Substring(0, Math.Min(UNAMELEN, value.Length));
+				}
+				else {
+#if NETCF_1_0 || NETCF_2_0
+					string currentUser = "PocketPC";
+#else
+					string currentUser = Environment.UserName;
+#endif
+					if (currentUser.Length > UNAMELEN) {
+						currentUser = currentUser.Substring(0, UNAMELEN);
+					}
+					userName = currentUser;
+				}
+			}
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's group name.
+		/// </summary>
+		/// <remarks>
+		/// This is only directly relevant to unix systems.
+		/// </remarks>
+		public string GroupName
+		{
+			get { return groupName; }
+			set { 
+				if ( value == null ) {
+					groupName = "None";
+				}
+				else {
+					groupName = value; 
+				}
+			}
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's major device number.
+		/// </summary>
+		public int DevMajor
+		{
+			get { return devMajor; }
+			set { devMajor = value; }
+		}
+		
+		
+		/// <summary>
+		/// Get/set the entry's minor device number.
+		/// </summary>
+		public int DevMinor
+		{
+			get { return devMinor; }
+			set { devMinor = value; }
+		}
+		
+		#endregion
+
+		#region ICloneable Members
+		/// <summary>
+		/// Create a new <see cref="TarHeader"/> that is a copy of the current instance.
+		/// </summary>
+		/// <returns>A new <see cref="Object"/> that is a copy of the current instance.</returns>
+		public object Clone()
+		{
+			return MemberwiseClone();
+		}
+		#endregion
+
+		/// <summary>
+		/// Parse TarHeader information from a header buffer.
+		/// </summary>
+		/// <param name = "header">
+		/// The tar entry header buffer to get information from.
+		/// </param>
+		public void ParseBuffer(byte[] header)
+		{
+			if ( header == null ) 
+			{
+				throw new ArgumentNullException("header");
+			}
+
+			int offset = 0;
+			
+			name = TarHeader.ParseName(header, offset, TarHeader.NAMELEN).ToString();
+			offset += TarHeader.NAMELEN;
+			
+			mode = (int)TarHeader.ParseOctal(header, offset, TarHeader.MODELEN);
+			offset += TarHeader.MODELEN;
+			
+			UserId = (int)TarHeader.ParseOctal(header, offset, TarHeader.UIDLEN);
+			offset += TarHeader.UIDLEN;
+			
+			GroupId = (int)TarHeader.ParseOctal(header, offset, TarHeader.GIDLEN);
+			offset += TarHeader.GIDLEN;
+			
+			Size = TarHeader.ParseOctal(header, offset, TarHeader.SIZELEN);
+			offset += TarHeader.SIZELEN;
+			
+			ModTime = GetDateTimeFromCTime(TarHeader.ParseOctal(header, offset, TarHeader.MODTIMELEN));
+			offset += TarHeader.MODTIMELEN;
+			
+			checksum = (int)TarHeader.ParseOctal(header, offset, TarHeader.CHKSUMLEN);
+			offset += TarHeader.CHKSUMLEN;
+			
+			TypeFlag = header[ offset++ ];
+
+			LinkName = TarHeader.ParseName(header, offset, TarHeader.NAMELEN).ToString();
+			offset += TarHeader.NAMELEN;
+			
+			Magic = TarHeader.ParseName(header, offset, TarHeader.MAGICLEN).ToString();
+			offset += TarHeader.MAGICLEN;
+			
+			Version = TarHeader.ParseName(header, offset, TarHeader.VERSIONLEN).ToString();
+			offset += TarHeader.VERSIONLEN;
+			
+			UserName = TarHeader.ParseName(header, offset, TarHeader.UNAMELEN).ToString();
+			offset += TarHeader.UNAMELEN;
+			
+			GroupName = TarHeader.ParseName(header, offset, TarHeader.GNAMELEN).ToString();
+			offset += TarHeader.GNAMELEN;
+			
+			DevMajor = (int)TarHeader.ParseOctal(header, offset, TarHeader.DEVLEN);
+			offset += TarHeader.DEVLEN;
+			
+			DevMinor = (int)TarHeader.ParseOctal(header, offset, TarHeader.DEVLEN);
+			
+			// Fields past this point not currently parsed or used...
+			
+			isChecksumValid = Checksum == TarHeader.MakeCheckSum(header);
+		}
+
+		/// <summary>
+		/// 'Write' header information to buffer provided, updating the <see cref="Checksum">check sum</see>.
+		/// </summary>
+		/// <param name="outBuffer">output buffer for header information</param>
+		public void WriteHeader(byte[] outBuffer)
+		{
+			if ( outBuffer == null ) 
+			{
+				throw new ArgumentNullException("outBuffer");
+			}
+
+			int offset = 0;
+			
+			offset = GetNameBytes(Name, outBuffer, offset, NAMELEN);
+			offset = GetOctalBytes(mode, outBuffer, offset, MODELEN);
+			offset = GetOctalBytes(UserId, outBuffer, offset, UIDLEN);
+			offset = GetOctalBytes(GroupId, outBuffer, offset, GIDLEN);
+			
+			offset = GetLongOctalBytes(Size, outBuffer, offset, SIZELEN);
+			offset = GetLongOctalBytes(GetCTime(ModTime), outBuffer, offset, MODTIMELEN);
+			
+			int csOffset = offset;
+			for (int c = 0; c < CHKSUMLEN; ++c) 
+			{
+				outBuffer[offset++] = (byte)' ';
+			}
+			
+			outBuffer[offset++] = TypeFlag;
+			
+			offset = GetNameBytes(LinkName, outBuffer, offset, NAMELEN);
+			offset = GetAsciiBytes(Magic, 0, outBuffer, offset, MAGICLEN);
+			offset = GetNameBytes(Version, outBuffer, offset, VERSIONLEN);
+			offset = GetNameBytes(UserName, outBuffer, offset, UNAMELEN);
+			offset = GetNameBytes(GroupName, outBuffer, offset, GNAMELEN);
+			
+			if ((TypeFlag == LF_CHR) || (TypeFlag == LF_BLK)) 
+			{
+				offset = GetOctalBytes(DevMajor, outBuffer, offset, DEVLEN);
+				offset = GetOctalBytes(DevMinor, outBuffer, offset, DEVLEN);
+			}
+			
+			for ( ; offset < outBuffer.Length; ) 
+			{
+				outBuffer[offset++] = 0;
+			}
+			
+			checksum = ComputeCheckSum(outBuffer);
+			
+			GetCheckSumOctalBytes(checksum, outBuffer, csOffset, CHKSUMLEN);
+			isChecksumValid = true;
+		}
+
+		/// <summary>
+		/// Get a hash code for the current object.
+		/// </summary>
+		/// <returns>A hash code for the current object.</returns>
+		public override int GetHashCode()
+		{
+			return Name.GetHashCode();
+		}
+		
+		/// <summary>
+		/// Determines if this instance is equal to the specified object.
+		/// </summary>
+		/// <param name="obj">The object to compare with.</param>
+		/// <returns>true if the objects are equal, false otherwise.</returns>
+		public override bool Equals(object obj)
+		{
+			TarHeader localHeader = obj as TarHeader;
+
+		    bool result;
+			if ( localHeader != null ) 
+			{
+				result = (name == localHeader.name)
+					&& (mode == localHeader.mode)
+					&& (UserId == localHeader.UserId)
+					&& (GroupId == localHeader.GroupId)
+					&& (Size == localHeader.Size)
+					&& (ModTime == localHeader.ModTime)
+					&& (Checksum == localHeader.Checksum)
+					&& (TypeFlag == localHeader.TypeFlag)
+					&& (LinkName == localHeader.LinkName)
+					&& (Magic == localHeader.Magic)
+					&& (Version == localHeader.Version)
+					&& (UserName == localHeader.UserName)
+					&& (GroupName == localHeader.GroupName)
+					&& (DevMajor == localHeader.DevMajor)
+					&& (DevMinor == localHeader.DevMinor);
+			}
+			else 
+			{
+				result = false;
+			}
+		    return result;
+		}
+		
+		/// <summary>
+		/// Set defaults for values used when constructing a TarHeader instance.
+		/// </summary>
+		/// <param name="userId">Value to apply as a default for userId.</param>
+		/// <param name="userName">Value to apply as a default for userName.</param>
+		/// <param name="groupId">Value to apply as a default for groupId.</param>
+		/// <param name="groupName">Value to apply as a default for groupName.</param>
+		static internal void SetValueDefaults(int userId, string userName, int groupId, string groupName)
+		{
+			defaultUserId = userIdAsSet = userId;
+			defaultUser = userNameAsSet = userName;
+			defaultGroupId = groupIdAsSet = groupId;
+			defaultGroupName = groupNameAsSet = groupName;
+		}
+
+		static internal void RestoreSetValues()
+		{
+			defaultUserId = userIdAsSet;
+			defaultUser = userNameAsSet;
+			defaultGroupId = groupIdAsSet;
+			defaultGroupName = groupNameAsSet;
+		}
+
+		/// <summary>
+		/// Parse an octal string from a header buffer.
+		/// </summary>
+		/// <param name = "header">The header buffer from which to parse.</param>
+		/// <param name = "offset">The offset into the buffer from which to parse.</param>
+		/// <param name = "length">The number of header bytes to parse.</param>
+		/// <returns>The long equivalent of the octal string.</returns>
+		static public long ParseOctal(byte[] header, int offset, int length)
+		{
+			if ( header == null ) {
+				throw new ArgumentNullException("header");
+			}
+
+			long result = 0;
+			bool stillPadding = true;
+			
+			int end = offset + length;
+			for (int i = offset; i < end ; ++i) {
+				if (header[i] == 0) {
+					break;
+				}
+				
+				if (header[i] == (byte)' ' || header[i] == '0') {
+					if (stillPadding) {
+						continue;
+					}
+					
+					if (header[i] == (byte)' ') {
+						break;
+					}
+				}
+				
+				stillPadding = false;
+				
+				result = (result << 3) + (header[i] - '0');
+			}
+			
+			return result;
+		}
+		
+		/// <summary>
+		/// Parse a name from a header buffer.
+		/// </summary>
+		/// <param name="header">
+		/// The header buffer from which to parse.
+		/// </param>
+		/// <param name="offset">
+		/// The offset into the buffer from which to parse.
+		/// </param>
+		/// <param name="length">
+		/// The number of header bytes to parse.
+		/// </param>
+		/// <returns>
+		/// The name parsed.
+		/// </returns>
+		static public StringBuilder ParseName(byte[] header, int offset, int length)
+		{
+			if ( header == null ) {
+				throw new ArgumentNullException("header");
+			}
+
+			if ( offset < 0 ) {
+#if NETCF_1_0
+				throw new ArgumentOutOfRangeException("offset");
+#else
+				throw new ArgumentOutOfRangeException("offset", "Cannot be less than zero");
+#endif				
+			}
+
+			if ( length < 0 )
+			{
+#if NETCF_1_0
+				throw new ArgumentOutOfRangeException("length");
+#else
+				throw new ArgumentOutOfRangeException("length", "Cannot be less than zero");
+#endif				
+			}
+
+			if ( offset + length > header.Length )
+			{
+				throw new ArgumentException("Exceeds header size", "length");
+			}
+
+			StringBuilder result = new StringBuilder(length);
+			
+			for (int i = offset; i < offset + length; ++i) {
+				if (header[i] == 0) {
+					break;
+				}
+				result.Append((char)header[i]);
+			}
+			
+			return result;
+		}
+		
+		/// <summary>
+		/// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes
+		/// </summary>
+		/// <param name="name">The name to add</param>
+		/// <param name="nameOffset">The offset of the first character</param>
+		/// <param name="buffer">The buffer to add to</param>
+		/// <param name="bufferOffset">The index of the first byte to add</param>
+		/// <param name="length">The number of characters/bytes to add</param>
+		/// <returns>The next free index in the <paramref name="buffer"/></returns>
+		public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length)
+		{
+			if ( name == null ) {
+				throw new ArgumentNullException("name");
+			}
+
+			if ( buffer == null ) {
+				throw new ArgumentNullException("buffer");
+			}
+
+			return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length);
+		}
+		
+		/// <summary>
+		/// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes
+		/// </summary>
+		/// <param name="name">The name to add</param>
+		/// <param name="nameOffset">The offset of the first character</param>
+		/// <param name="buffer">The buffer to add to</param>
+		/// <param name="bufferOffset">The index of the first byte to add</param>
+		/// <param name="length">The number of characters/bytes to add</param>
+		/// <returns>The next free index in the <paramref name="buffer"/></returns>
+		public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length)
+		{
+			if ( name == null ) 
+			{
+				throw new ArgumentNullException("name");
+			}
+
+			if ( buffer == null )
+			{
+				throw new ArgumentNullException("buffer");
+			}
+
+			int i;
+			
+			for (i = 0 ; i < length - 1 && nameOffset + i < name.Length; ++i) {
+				buffer[bufferOffset + i] = (byte)name[nameOffset + i];
+			}
+			
+			for (; i < length ; ++i) {
+				buffer[bufferOffset + i] = 0;
+			}
+			
+			return bufferOffset + length;
+		}
+
+		/// <summary>
+		/// Add an entry name to the buffer
+		/// </summary>
+		/// <param name="name">
+		/// The name to add
+		/// </param>
+		/// <param name="buffer">
+		/// The buffer to add to
+		/// </param>
+		/// <param name="offset">
+		/// The offset into the buffer from which to start adding
+		/// </param>
+		/// <param name="length">
+		/// The number of header bytes to add
+		/// </param>
+		/// <returns>
+		/// The index of the next free byte in the buffer
+		/// </returns>
+		public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length)
+		{
+
+			if ( name == null ) {
+				throw new ArgumentNullException("name");
+			}
+
+			if ( buffer == null ) {
+				throw new ArgumentNullException("buffer");
+			}
+
+			return GetNameBytes(name.ToString(), 0, buffer, offset, length);
+		}
+		
+		/// <summary>
+		/// Add an entry name to the buffer
+		/// </summary>
+		/// <param name="name">The name to add</param>
+		/// <param name="buffer">The buffer to add to</param>
+		/// <param name="offset">The offset into the buffer from which to start adding</param>
+		/// <param name="length">The number of header bytes to add</param>
+		/// <returns>The index of the next free byte in the buffer</returns>
+		public static int GetNameBytes(string name, byte[] buffer, int offset, int length)
+		{
+
+			if ( name == null ) {
+				throw new ArgumentNullException("name");
+			}
+
+			if ( buffer == null ) 
+			{
+				throw new ArgumentNullException("buffer");
+			}
+
+			return GetNameBytes(name, 0, buffer, offset, length);
+		}
+		
+		/// <summary>
+		/// Add a string to a buffer as a collection of ascii bytes.
+		/// </summary>
+		/// <param name="toAdd">The string to add</param>
+		/// <param name="nameOffset">The offset of the first character to add.</param>
+		/// <param name="buffer">The buffer to add to.</param>
+		/// <param name="bufferOffset">The offset to start adding at.</param>
+		/// <param name="length">The number of ascii characters to add.</param>
+		/// <returns>The next free index in the buffer.</returns>
+		public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length )
+		{
+			if ( toAdd == null ) {
+				throw new ArgumentNullException("toAdd");
+			}
+
+			if ( buffer == null ) {
+				throw new ArgumentNullException("buffer");
+			}
+
+			for (int i = 0 ; i < length && nameOffset + i < toAdd.Length; ++i) 
+			{
+				buffer[bufferOffset + i] = (byte)toAdd[nameOffset + i];
+			}
+			return bufferOffset + length;
+		}
+
+		/// <summary>
+		/// Put an octal representation of a value into a buffer
+		/// </summary>
+		/// <param name = "value">
+		/// the value to be converted to octal
+		/// </param>
+		/// <param name = "buffer">
+		/// buffer to store the octal string
+		/// </param>
+		/// <param name = "offset">
+		/// The offset into the buffer where the value starts
+		/// </param>
+		/// <param name = "length">
+		/// The length of the octal string to create
+		/// </param>
+		/// <returns>
+		/// The offset of the character next byte after the octal string
+		/// </returns>
+		public static int GetOctalBytes(long value, byte[] buffer, int offset, int length)
+		{
+			if ( buffer == null ) {
+				throw new ArgumentNullException("buffer");
+			}
+
+			int localIndex = length - 1;
+
+			// Either a space or null is valid here.  We use NULL as per GNUTar
+			buffer[offset + localIndex] = 0;
+			--localIndex;
+
+			if (value > 0) {
+				for ( long v = value; (localIndex >= 0) && (v > 0); --localIndex ) {
+					buffer[offset + localIndex] = (byte)((byte)'0' + (byte)(v & 7));
+					v >>= 3;
+				}
+			}
+				
+			for ( ; localIndex >= 0; --localIndex ) {
+				buffer[offset + localIndex] = (byte)'0';
+			}
+			
+			return offset + length;
+		}
+		
+		/// <summary>
+		/// Put an octal representation of a value into a buffer
+		/// </summary>
+		/// <param name = "value">Value to be convert to octal</param>
+		/// <param name = "buffer">The buffer to update</param>
+		/// <param name = "offset">The offset into the buffer to store the value</param>
+		/// <param name = "length">The length of the octal string</param>
+		/// <returns>Index of next byte</returns>
+		public static int GetLongOctalBytes(long value, byte[] buffer, int offset, int length)
+		{
+			return GetOctalBytes(value, buffer, offset, length);
+		}
+		
+		/// <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;
+		}
+		
+		/// <summary>
+		/// Compute the checksum for a tar entry header.  
+		/// The checksum field must be all spaces prior to this happening
+		/// </summary>
+		/// <param name = "buffer">The tar entry's header buffer.</param>
+		/// <returns>The computed checksum.</returns>
+		static int ComputeCheckSum(byte[] buffer)
+		{
+			int sum = 0;
+			for (int i = 0; i < buffer.Length; ++i) {
+				sum += buffer[i];
+			}
+			return sum;
+		}
+		
+		/// <summary>
+		/// Make a checksum for a tar entry ignoring the checksum contents.
+		/// </summary>
+		/// <param name = "buffer">The tar entry's header buffer.</param>
+		/// <returns>The checksum for the buffer</returns>
+		static int MakeCheckSum(byte[] buffer)
+		{
+			int sum = 0;
+			for ( int i = 0; i < CHKSUMOFS; ++i )
+			{
+				sum += buffer[i];
+			}
+		
+			for ( int i = 0; i < TarHeader.CHKSUMLEN; ++i)
+			{
+				sum += (byte)' ';
+			}
+		
+			for (int i = CHKSUMOFS + CHKSUMLEN; i < buffer.Length; ++i) 
+			{
+				sum += buffer[i];
+			}
+			return sum;
+		}
+		
+		static int GetCTime(System.DateTime dateTime)
+		{
+			return unchecked((int)((dateTime.Ticks - dateTime1970.Ticks) / timeConversionFactor));
+		}
+		
+		static DateTime GetDateTimeFromCTime(long ticks)
+		{
+			DateTime result;
+			
+			try {
+				result = new DateTime(dateTime1970.Ticks + ticks * timeConversionFactor);
+			}
+			catch(ArgumentOutOfRangeException) {
+				result = dateTime1970;
+			}
+			return result;
+		}
+
+		#region Instance Fields
+		string name;
+		int mode;
+		int userId;
+		int groupId;
+		long size;
+		DateTime modTime;
+		int checksum;
+		bool isChecksumValid;
+		byte typeFlag;
+		string linkName;
+		string magic;
+		string version;
+		string userName;
+		string groupName;
+		int devMajor;
+		int devMinor;
+		#endregion
+
+		#region Class Fields
+		// Values used during recursive operations.
+		static internal int userIdAsSet;
+		static internal int groupIdAsSet;
+		static internal string userNameAsSet;
+		static internal string groupNameAsSet = "None";
+		
+		static internal int defaultUserId;
+		static internal int defaultGroupId;
+		static internal string defaultGroupName = "None";
+		static internal string defaultUser;
+		#endregion
+	}
+}
+
+/* The original Java file had this header:
+ * 
+** Authored by Timothy Gerard Endres
+** <mailto:time@gjt.org>  <http://www.trustice.com>
+** 
+** This work has been placed into the public domain.
+** You may use this work in any way and for any purpose you wish.
+**
+** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
+** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
+** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
+** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
+** REDISTRIBUTION OF THIS SOFTWARE. 
+** 
+*/