| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753 |
- // 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 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"
- 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
- // 500 End
- 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
- {
- /// <summary>
- /// The length of the name field in a header buffer.
- /// </summary>
- public readonly static int NAMELEN = 100;
-
- /// <summary>
- /// The length of the mode field in a header buffer.
- /// </summary>
- public readonly static int MODELEN = 8;
-
- /// <summary>
- /// The length of the user id field in a header buffer.
- /// </summary>
- public readonly static int UIDLEN = 8;
-
- /// <summary>
- /// The length of the group id field in a header buffer.
- /// </summary>
- public readonly static int GIDLEN = 8;
-
- /// <summary>
- /// The length of the checksum field in a header buffer.
- /// </summary>
- public readonly static int CHKSUMLEN = 8;
-
- /// <summary>
- /// The length of the size field in a header buffer.
- /// </summary>
- public readonly static int SIZELEN = 12;
-
- /// <summary>
- /// The length of the magic field in a header buffer.
- /// </summary>
- public readonly static int MAGICLEN = 6;
-
- /// <summary>
- /// The length of the version field in a header buffer.
- /// </summary>
- public readonly static int VERSIONLEN = 2;
- /// <summary>
- /// The length of the modification time field in a header buffer.
- /// </summary>
- public readonly static int MODTIMELEN = 12;
-
- /// <summary>
- /// The length of the user name field in a header buffer.
- /// </summary>
- public readonly static int UNAMELEN = 32;
-
- /// <summary>
- /// The length of the group name field in a header buffer.
- /// </summary>
- public readonly static int GNAMELEN = 32;
-
- /// <summary>
- /// The length of the devices field in a header buffer.
- /// </summary>
- public readonly static int DEVLEN = 8;
-
- /// <summary>
- /// LF_ constants represents the "type" of an entry
- /// </summary>
- ///
- /// <summary>
- /// This is 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 readonly static byte LF_XHDR = (byte) 'x';
-
-
-
-
- // POSIX allows for upper case ascii type as extensions
-
- // Solaris access control list
- public const byte LF_ACL = (byte) 'A';
-
- // This is a dir entry that contains the names of files that were in the
- // dir at the time the dump was made
- public const byte LF_GNU_DUMPDIR = (byte) 'D';
-
- // Solaris Extended Attribute File
- public const byte LF_EXTATTR = (byte) 'E' ;
-
- // Inode (metadata only) no file content
- public const byte LF_META = (byte) 'I';
-
- // Identifies the next file on the tape as having a long link name
- public const byte LF_GNU_LONGLINK = (byte) 'K';
-
- // Identifies the next file on the tape as having a long name
- public const byte LF_GNU_LONGNAME = (byte) 'L';
-
- // Continuation of a file that began on another volume
- public const byte LF_GNU_MULTIVOL = (byte) 'M';
-
- // For storing filenames that dont fit in the main header (old GNU)
- public const byte LF_GNU_NAMES = (byte) 'N';
-
- // Sparse file
- public const byte LF_GNU_SPARSE = (byte) 'S';
-
- // Tape/volume header ignore on extraction
- public const byte LF_GNU_VOLHDR = (byte) 'V';
-
- /// <summary>
- /// The magic tag representing a POSIX tar archive. (includes trailing NULL)
- /// </summary>
- public readonly static string TMAGIC = "ustar ";
-
- /// <summary>
- /// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it
- /// </summary>
- public readonly static string GNU_TMAGIC = "ustar ";
-
- /// <summary>
- /// The entry's name.
- /// </summary>
- public StringBuilder name;
-
- /// <summary>
- /// The entry's permission mode.
- /// </summary>
- public int mode;
-
- /// <summary>
- /// The entry's user id.
- /// </summary>
- public int userId;
-
- /// <summary>
- /// The entry's group id.
- /// </summary>
- public int groupId;
-
- /// <summary>
- /// The entry's size.
- /// </summary>
- public long size;
-
- /// <summary>
- /// The entry's modification time.
- /// </summary>
- public DateTime modTime;
-
- /// <summary>
- /// The entry's checksum.
- /// </summary>
- public int checkSum;
-
- /// <summary>
- /// The entry's type flag.
- /// </summary>
- public byte typeFlag;
-
- /// <summary>
- /// The entry's link name.
- /// </summary>
- public StringBuilder linkName;
-
- /// <summary>
- /// The entry's magic tag.
- /// </summary>
- public StringBuilder magic;
-
- /// <summary>
- /// The entry's version.
- /// </summary>
- public StringBuilder version;
-
- /// <summary>
- /// The entry's user name.
- /// </summary>
- public StringBuilder userName;
-
- /// <summary>
- /// The entry's group name.
- /// </summary>
- public StringBuilder groupName;
-
- /// <summary>
- /// The entry's major device number.
- /// </summary>
- public int devMajor;
-
- /// <summary>
- /// The entry's minor device number.
- /// </summary>
- public int devMinor;
-
- public TarHeader()
- {
- this.magic = new StringBuilder(TarHeader.TMAGIC);
- this.version = new StringBuilder(" ");
-
- this.name = new StringBuilder();
- this.linkName = new StringBuilder();
-
- string user = Environment.UserName;
- // string user = "PocketPC";
- // string user = "Everyone";
-
- if (user.Length > 31) {
- user = user.Substring(0, 31);
- }
-
- this.userId = 1003; // -jr- was 0
- this.groupId = 513; // -jr- was 0
- this.userName = new StringBuilder(user);
- // -jr-
- // this.groupName = new StringBuilder(String.Empty);
- // this.groupName = new StringBuilder("Everyone"); Attempt2
- this.groupName = new StringBuilder("None"); // Gnu compatible
- this.size = 0;
- }
-
- /// <summary>
- /// TarHeaders can be cloned.
- /// </summary>
- public object Clone()
- {
- TarHeader hdr = new TarHeader();
-
- hdr.name = (this.name == null) ? null : new StringBuilder(this.name.ToString());
- hdr.mode = this.mode;
- hdr.userId = this.userId;
- hdr.groupId = this.groupId;
- hdr.size = this.size;
- hdr.modTime = this.modTime;
- hdr.checkSum = this.checkSum;
- hdr.typeFlag = this.typeFlag;
- hdr.linkName = (this.linkName == null) ? null : new StringBuilder(this.linkName.ToString());
- hdr.magic = (this.magic == null) ? null : new StringBuilder(this.magic.ToString());
- hdr.version = (this.version == null) ? null : new StringBuilder(this.version.ToString());
- hdr.userName = (this.userName == null) ? null : new StringBuilder(this.userName.ToString());
- hdr.groupName = (this.groupName == null) ? null : new StringBuilder(this.groupName.ToString());
- hdr.devMajor = this.devMajor;
- hdr.devMinor = this.devMinor;
-
- return hdr;
- }
-
- /// <summary>
- /// Get the name of this entry.
- /// </summary>
- /// <returns>
- /// The entry's name.
- /// </returns>
- public string GetName()
- {
- return this.name.ToString();
- }
-
- /// <summary>
- /// Parse an octal string from a header buffer. This is used for the
- /// file permission mode value.
- /// </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 value of the octal string.
- /// </returns>
- public static long ParseOctal(byte[] header, int offset, int length)
- {
- 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 an entry 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 header's entry name.
- /// </returns>
- public static StringBuilder ParseName(byte[] header, int offset, int 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;
- }
-
- public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buf, int bufferOffset, int length)
- {
- int i;
-
- for (i = 0 ; i < length && nameOffset + i < name.Length; ++i)
- {
- buf[bufferOffset + i] = (byte)name[nameOffset + i];
- }
-
- for (; i < length ; ++i)
- {
- buf[bufferOffset + i] = 0;
- }
-
- return bufferOffset + length;
- }
- /// <summary>
- /// Determine the number of bytes in an entry name.
- /// </summary>
- /// <param name="name">
- /// </param>
- /// <param name="buf">
- /// 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 number of bytes in a header's entry name.
- /// </returns>
- public static int GetNameBytes(StringBuilder name, byte[] buf, int offset, int length)
- {
- return GetNameBytes(name, 0, buf, offset, length);
- }
-
- /// <summary>
- /// Parse an octal integer from a header buffer.
- /// </summary>
- /// <param name = "val">
- /// </param>
- /// <param name = "buf">
- /// 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 integer value of the octal bytes.
- /// </returns>
- public static int GetOctalBytes(long val, byte[] buf, int offset, int length)
- {
- // TODO check for values too large...
- int idx = length - 1;
- // Either a space or null is valid here. We use NULL as per GNUTar
- buf[offset + idx] = 0;
- --idx;
- if (val > 0)
- {
- for (long v = val; idx >= 0 && v > 0; --idx)
- {
- buf[offset + idx] = (byte)((byte)'0' + (byte)(v & 7));
- v >>= 3;
- }
- }
-
- for (; idx >= 0; --idx)
- {
- buf[offset + idx] = (byte)'0';
- }
-
- return offset + length;
- }
-
- /// <summary>
- /// Parse an octal long integer from a header buffer.
- /// </summary>
- /// <param name = "val">
- /// </param>
- /// <param name = "buf">
- /// 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 value of the octal bytes.
- /// </returns>
- public static int GetLongOctalBytes(long val, byte[] buf, int offset, int length)
- {
- return GetOctalBytes(val, buf, offset, length);
- }
-
- /// <summary>
- /// Add the checksum octal integer to header buffer.
- /// </summary>
- /// <param name = "val">
- /// </param>
- /// <param name = "buf">
- /// 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>
- private static int GetCheckSumOctalBytes(long val, byte[] buf, int offset, int length)
- {
- TarHeader.GetOctalBytes(val, buf, offset, length - 1);
- // buf[offset + length - 1] = (byte)' '; -jr- 23-Jan-2004 this causes failure!!!
- // buf[offset + length - 2] = 0;
- 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 = "buf">
- /// The tar entry's header buffer.
- /// </param>
- /// <returns>
- /// The computed checksum.
- /// </returns>
- private static long ComputeCheckSum(byte[] buf)
- {
- long sum = 0;
- for (int i = 0; i < buf.Length; ++i)
- {
- sum += buf[i];
- }
- return sum;
- }
- readonly static long timeConversionFactor = 10000000L; // -jr- 1 tick == 100 nanoseconds
- readonly static DateTime datetTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
- // readonly static DateTime datetTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0).ToUniversalTime(); // -jr- Should be UTC? doesnt match Gnutar if this is so though, why?
-
- static int GetCTime(System.DateTime dateTime)
- {
- return (int)((dateTime.Ticks - datetTime1970.Ticks) / timeConversionFactor);
- }
-
- static DateTime GetDateTimeFromCTime(long ticks)
- {
- return new DateTime(datetTime1970.Ticks + ticks * timeConversionFactor);
- }
- /// <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)
- {
- int offset = 0;
-
- name = TarHeader.ParseName(header, offset, TarHeader.NAMELEN);
- 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);
- offset += TarHeader.NAMELEN;
-
- magic = TarHeader.ParseName(header, offset, TarHeader.MAGICLEN);
- offset += TarHeader.MAGICLEN;
- version = TarHeader.ParseName(header, offset, TarHeader.VERSIONLEN);
- offset += TarHeader.VERSIONLEN;
-
- userName = TarHeader.ParseName(header, offset, TarHeader.UNAMELEN);
- offset += TarHeader.UNAMELEN;
-
- groupName = TarHeader.ParseName(header, offset, TarHeader.GNAMELEN);
- 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...
- }
- /// <summary>
- /// 'Write' header information to buffer provided
- /// </summary>
- /// <param name="outbuf">output buffer for header information</param>
- public void WriteHeader(byte[] outbuf)
- {
- int offset = 0;
-
- offset = GetNameBytes(this.name, outbuf, offset, TarHeader.NAMELEN);
- offset = GetOctalBytes(this.mode, outbuf, offset, TarHeader.MODELEN);
- offset = GetOctalBytes(this.userId, outbuf, offset, TarHeader.UIDLEN);
- offset = GetOctalBytes(this.groupId, outbuf, offset, TarHeader.GIDLEN);
-
- long size = this.size;
-
- offset = GetLongOctalBytes(size, outbuf, offset, TarHeader.SIZELEN);
- offset = GetLongOctalBytes(GetCTime(this.modTime), outbuf, offset, TarHeader.MODTIMELEN);
-
- int csOffset = offset;
- for (int c = 0; c < TarHeader.CHKSUMLEN; ++c)
- {
- outbuf[offset++] = (byte)' ';
- }
-
- outbuf[offset++] = this.typeFlag;
-
- offset = GetNameBytes(this.linkName, outbuf, offset, NAMELEN);
- offset = GetNameBytes(this.magic, outbuf, offset, MAGICLEN);
- offset = GetNameBytes(this.version, outbuf, offset, VERSIONLEN);
- offset = GetNameBytes(this.userName, outbuf, offset, UNAMELEN);
- offset = GetNameBytes(this.groupName, outbuf, offset, GNAMELEN);
- if (this.typeFlag == LF_CHR || this.typeFlag == LF_BLK)
- {
- offset = GetOctalBytes(this.devMajor, outbuf, offset, DEVLEN);
- offset = GetOctalBytes(this.devMinor, outbuf, offset, DEVLEN);
- }
-
- for ( ; offset < outbuf.Length; )
- {
- outbuf[offset++] = 0;
- }
-
- long checkSum = ComputeCheckSum(outbuf);
-
- GetCheckSumOctalBytes(checkSum, outbuf, csOffset, CHKSUMLEN);
- }
- }
- }
- /* The original Java file had this header:
- *
- ** Authored by Timothy Gerard Endres
- ** <mailto:[email protected]> <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.
- **
- */
|