Summary

Class:ICSharpCode.SharpZipLib.Tar.TarHeader
Assembly:ICSharpCode.SharpZipLib
File(s):C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Tar\TarHeader.cs
Covered lines:209
Uncovered lines:63
Coverable lines:272
Total lines:1077
Line coverage:76.8%
Branch coverage:66.9%

Metrics

MethodCyclomatic ComplexitySequence CoverageBranch Coverage
.ctor()1100100
GetName()100
Clone()1100100
ParseBuffer(...)463.1642.86
WriteHeader(...)689.6672.73
GetHashCode()100
Equals(...)1683.3366.67
SetValueDefaults(...)100
RestoreSetValues()100
ParseBinaryOrOctal(...)32540
ParseOctal(...)893.3373.33
ParseName(...)773.3369.23
GetNameBytes(...)300
GetNameBytes(...)683.3372.73
GetNameBytes(...)300
GetNameBytes(...)36060
GetAsciiBytes(...)577.7877.78
GetOctalBytes(...)693.3390.91
GetBinaryOrOctalBytes(...)322.2240
GetCheckSumOctalBytes(...)1100100
ComputeCheckSum(...)2100100
MakeCheckSum(...)4100100
GetCTime(...)1100100
GetDateTimeFromCTime(...)150100
.cctor()1100100

File(s)

C:\Users\Neil\Documents\Visual Studio 2015\Projects\icsharpcode\SZL_master\ICSharpCode.SharpZipLib\Tar\TarHeader.cs

#LineLine coverage
 1using System;
 2using System.Text;
 3
 4namespace ICSharpCode.SharpZipLib.Tar
 5{
 6  /// <summary>
 7  /// This class encapsulates the Tar Entry Header used in Tar Archives.
 8  /// The class also holds a number of tar constants, used mostly in headers.
 9  /// </summary>
 10  /// <remarks>
 11  ///    The tar format and its POSIX successor PAX have a long history which makes for compatability
 12  ///    issues when creating and reading files.
 13  ///
 14  ///    This is further complicated by a large number of programs with variations on formats
 15  ///    One common issue is the handling of names longer than 100 characters.
 16  ///    GNU style long names are currently supported.
 17  ///
 18  /// This is the ustar (Posix 1003.1) header.
 19  ///
 20  /// struct header
 21  /// {
 22  ///   char t_name[100];          //   0 Filename
 23  ///   char t_mode[8];            // 100 Permissions
 24  ///   char t_uid[8];             // 108 Numerical User ID
 25  ///   char t_gid[8];             // 116 Numerical Group ID
 26  ///   char t_size[12];           // 124 Filesize
 27  ///   char t_mtime[12];          // 136 st_mtime
 28  ///   char t_chksum[8];          // 148 Checksum
 29  ///   char t_typeflag;           // 156 Type of File
 30  ///   char t_linkname[100];      // 157 Target of Links
 31  ///   char t_magic[6];           // 257 "ustar" or other...
 32  ///   char t_version[2];         // 263 Version fixed to 00
 33  ///   char t_uname[32];          // 265 User Name
 34  ///   char t_gname[32];          // 297 Group Name
 35  ///   char t_devmajor[8];        // 329 Major for devices
 36  ///   char t_devminor[8];        // 337 Minor for devices
 37  ///   char t_prefix[155];        // 345 Prefix for t_name
 38  ///   char t_mfill[12];          // 500 Filler up to 512
 39  /// };
 40  /// </remarks>
 41  public class TarHeader : ICloneable
 42  {
 43    #region Constants
 44    /// <summary>
 45    /// The length of the name field in a header buffer.
 46    /// </summary>
 47    public const int NAMELEN = 100;
 48
 49    /// <summary>
 50    /// The length of the mode field in a header buffer.
 51    /// </summary>
 52    public const int MODELEN = 8;
 53
 54    /// <summary>
 55    /// The length of the user id field in a header buffer.
 56    /// </summary>
 57    public const int UIDLEN = 8;
 58
 59    /// <summary>
 60    /// The length of the group id field in a header buffer.
 61    /// </summary>
 62    public const int GIDLEN = 8;
 63
 64    /// <summary>
 65    /// The length of the checksum field in a header buffer.
 66    /// </summary>
 67    public const int CHKSUMLEN = 8;
 68
 69    /// <summary>
 70    /// Offset of checksum in a header buffer.
 71    /// </summary>
 72    public const int CHKSUMOFS = 148;
 73
 74    /// <summary>
 75    /// The length of the size field in a header buffer.
 76    /// </summary>
 77    public const int SIZELEN = 12;
 78
 79    /// <summary>
 80    /// The length of the magic field in a header buffer.
 81    /// </summary>
 82    public const int MAGICLEN = 6;
 83
 84    /// <summary>
 85    /// The length of the version field in a header buffer.
 86    /// </summary>
 87    public const int VERSIONLEN = 2;
 88
 89    /// <summary>
 90    /// The length of the modification time field in a header buffer.
 91    /// </summary>
 92    public const int MODTIMELEN = 12;
 93
 94    /// <summary>
 95    /// The length of the user name field in a header buffer.
 96    /// </summary>
 97    public const int UNAMELEN = 32;
 98
 99    /// <summary>
 100    /// The length of the group name field in a header buffer.
 101    /// </summary>
 102    public const int GNAMELEN = 32;
 103
 104    /// <summary>
 105    /// The length of the devices field in a header buffer.
 106    /// </summary>
 107    public const int DEVLEN = 8;
 108
 109    /// <summary>
 110    /// The length of the name prefix field in a header buffer.
 111    /// </summary>
 112    public const int PREFIXLEN = 155;
 113
 114    //
 115    // LF_ constants represent the "type" of an entry
 116    //
 117
 118    /// <summary>
 119    ///  The "old way" of indicating a normal file.
 120    /// </summary>
 121    public const byte LF_OLDNORM = 0;
 122
 123    /// <summary>
 124    /// Normal file type.
 125    /// </summary>
 126    public const byte LF_NORMAL = (byte)'0';
 127
 128    /// <summary>
 129    /// Link file type.
 130    /// </summary>
 131    public const byte LF_LINK = (byte)'1';
 132
 133    /// <summary>
 134    /// Symbolic link file type.
 135    /// </summary>
 136    public const byte LF_SYMLINK = (byte)'2';
 137
 138    /// <summary>
 139    /// Character device file type.
 140    /// </summary>
 141    public const byte LF_CHR = (byte)'3';
 142
 143    /// <summary>
 144    /// Block device file type.
 145    /// </summary>
 146    public const byte LF_BLK = (byte)'4';
 147
 148    /// <summary>
 149    /// Directory file type.
 150    /// </summary>
 151    public const byte LF_DIR = (byte)'5';
 152
 153    /// <summary>
 154    /// FIFO (pipe) file type.
 155    /// </summary>
 156    public const byte LF_FIFO = (byte)'6';
 157
 158    /// <summary>
 159    /// Contiguous file type.
 160    /// </summary>
 161    public const byte LF_CONTIG = (byte)'7';
 162
 163    /// <summary>
 164    /// Posix.1 2001 global extended header
 165    /// </summary>
 166    public const byte LF_GHDR = (byte)'g';
 167
 168    /// <summary>
 169    /// Posix.1 2001 extended header
 170    /// </summary>
 171    public const byte LF_XHDR = (byte)'x';
 172
 173    // POSIX allows for upper case ascii type as extensions
 174
 175    /// <summary>
 176    /// Solaris access control list file type
 177    /// </summary>
 178    public const byte LF_ACL = (byte)'A';
 179
 180    /// <summary>
 181    /// GNU dir dump file type
 182    /// This is a dir entry that contains the names of files that were in the
 183    /// dir at the time the dump was made
 184    /// </summary>
 185    public const byte LF_GNU_DUMPDIR = (byte)'D';
 186
 187    /// <summary>
 188    /// Solaris Extended Attribute File
 189    /// </summary>
 190    public const byte LF_EXTATTR = (byte)'E';
 191
 192    /// <summary>
 193    /// Inode (metadata only) no file content
 194    /// </summary>
 195    public const byte LF_META = (byte)'I';
 196
 197    /// <summary>
 198    /// Identifies the next file on the tape as having a long link name
 199    /// </summary>
 200    public const byte LF_GNU_LONGLINK = (byte)'K';
 201
 202    /// <summary>
 203    /// Identifies the next file on the tape as having a long name
 204    /// </summary>
 205    public const byte LF_GNU_LONGNAME = (byte)'L';
 206
 207    /// <summary>
 208    /// Continuation of a file that began on another volume
 209    /// </summary>
 210    public const byte LF_GNU_MULTIVOL = (byte)'M';
 211
 212    /// <summary>
 213    /// For storing filenames that dont fit in the main header (old GNU)
 214    /// </summary>
 215    public const byte LF_GNU_NAMES = (byte)'N';
 216
 217    /// <summary>
 218    /// GNU Sparse file
 219    /// </summary>
 220    public const byte LF_GNU_SPARSE = (byte)'S';
 221
 222    /// <summary>
 223    /// GNU Tape/volume header ignore on extraction
 224    /// </summary>
 225    public const byte LF_GNU_VOLHDR = (byte)'V';
 226
 227    /// <summary>
 228    /// The magic tag representing a POSIX tar archive.  (includes trailing NULL)
 229    /// </summary>
 230    public const string TMAGIC = "ustar ";
 231
 232    /// <summary>
 233    /// The magic tag representing an old GNU tar archive where version is included in magic and overwrites it
 234    /// </summary>
 235    public const string GNU_TMAGIC = "ustar  ";
 236
 237    const long timeConversionFactor = 10000000L;           // 1 tick == 100 nanoseconds
 1238    readonly static DateTime dateTime1970 = new DateTime(1970, 1, 1, 0, 0, 0, 0);
 239    #endregion
 240
 241    #region Constructors
 242
 243    /// <summary>
 244    /// Initialise a default TarHeader instance
 245    /// </summary>
 84246    public TarHeader()
 247    {
 84248      Magic = TMAGIC;
 84249      Version = " ";
 250
 84251      Name = "";
 84252      LinkName = "";
 253
 84254      UserId = defaultUserId;
 84255      GroupId = defaultGroupId;
 84256      UserName = defaultUser;
 84257      GroupName = defaultGroupName;
 84258      Size = 0;
 84259    }
 260
 261    #endregion
 262
 263    #region Properties
 264    /// <summary>
 265    /// Get/set the name for this tar entry.
 266    /// </summary>
 267    /// <exception cref="ArgumentNullException">Thrown when attempting to set the property to null.</exception>
 268    public string Name {
 214269      get { return name; }
 270      set {
 166271         if (value == null) {
 1272          throw new ArgumentNullException(nameof(value));
 273        }
 165274        name = value;
 165275      }
 276    }
 277
 278    /// <summary>
 279    /// Get the name of this entry.
 280    /// </summary>
 281    /// <returns>The entry's name.</returns>
 282    [Obsolete("Use the Name property instead", true)]
 283    public string GetName()
 284    {
 0285      return name;
 286    }
 287
 288    /// <summary>
 289    /// Get/set the entry's Unix style permission mode.
 290    /// </summary>
 291    public int Mode {
 1292      get { return mode; }
 162293      set { mode = value; }
 294    }
 295
 296
 297    /// <summary>
 298    /// The entry's user id.
 299    /// </summary>
 300    /// <remarks>
 301    /// This is only directly relevant to unix systems.
 302    /// The default is zero.
 303    /// </remarks>
 304    public int UserId {
 125305      get { return userId; }
 334306      set { userId = value; }
 307    }
 308
 309
 310    /// <summary>
 311    /// Get/set the entry's group id.
 312    /// </summary>
 313    /// <remarks>
 314    /// This is only directly relevant to linux/unix systems.
 315    /// The default value is zero.
 316    /// </remarks>
 317    public int GroupId {
 125318      get { return groupId; }
 336319      set { groupId = value; }
 320    }
 321
 322
 323    /// <summary>
 324    /// Get/set the entry's size.
 325    /// </summary>
 326    /// <exception cref="ArgumentOutOfRangeException">Thrown when setting the size to less than zero.</exception>
 327    public long Size {
 195328      get { return size; }
 329      set {
 237330         if (value < 0) {
 1331          throw new ArgumentOutOfRangeException(nameof(value), "Cannot be less than zero");
 332        }
 236333        size = value;
 236334      }
 335    }
 336
 337
 338    /// <summary>
 339    /// Get/set the entry's modification time.
 340    /// </summary>
 341    /// <remarks>
 342    /// The modification time is only accurate to within a second.
 343    /// </remarks>
 344    /// <exception cref="ArgumentOutOfRangeException">Thrown when setting the date time to less than 1/1/1970.</exceptio
 345    public DateTime ModTime {
 121346      get { return modTime; }
 347      set {
 85348         if (value < dateTime1970) {
 1349          throw new ArgumentOutOfRangeException(nameof(value), "ModTime cannot be before Jan 1st 1970");
 350        }
 84351        modTime = new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, value.Second);
 84352      }
 353    }
 354
 355
 356    /// <summary>
 357    /// Get the entry's checksum.  This is only valid/updated after writing or reading an entry.
 358    /// </summary>
 359    public int Checksum {
 51360      get { return checksum; }
 361    }
 362
 363
 364    /// <summary>
 365    /// Get value of true if the header checksum is valid, false otherwise.
 366    /// </summary>
 367    public bool IsChecksumValid {
 3368      get { return isChecksumValid; }
 369    }
 370
 371
 372    /// <summary>
 373    /// Get/set the entry's type flag.
 374    /// </summary>
 375    public byte TypeFlag {
 334376      get { return typeFlag; }
 166377      set { typeFlag = value; }
 378    }
 379
 380
 381    /// <summary>
 382    /// The entry's link name.
 383    /// </summary>
 384    /// <exception cref="ArgumentNullException">Thrown when attempting to set LinkName to null.</exception>
 385    public string LinkName {
 120386      get { return linkName; }
 387      set {
 170388         if (value == null) {
 1389          throw new ArgumentNullException(nameof(value));
 390        }
 169391        linkName = value;
 169392      }
 393    }
 394
 395
 396    /// <summary>
 397    /// Get/set the entry's magic tag.
 398    /// </summary>
 399    /// <exception cref="ArgumentNullException">Thrown when attempting to set Magic to null.</exception>
 400    public string Magic {
 116401      get { return magic; }
 402      set {
 90403         if (value == null) {
 1404          throw new ArgumentNullException(nameof(value));
 405        }
 89406        magic = value;
 89407      }
 408    }
 409
 410
 411    /// <summary>
 412    /// The entry's version.
 413    /// </summary>
 414    /// <exception cref="ArgumentNullException">Thrown when attempting to set Version to null.</exception>
 415    public string Version {
 416      get {
 111417        return version;
 418      }
 419
 420      set {
 87421         if (value == null) {
 1422          throw new ArgumentNullException(nameof(value));
 423        }
 86424        version = value;
 86425      }
 426    }
 427
 428
 429    /// <summary>
 430    /// The entry's user name.
 431    /// </summary>
 432    public string UserName {
 111433      get { return userName; }
 434      set {
 166435         if (value != null) {
 81436          userName = value.Substring(0, Math.Min(UNAMELEN, value.Length));
 81437        } else {
 85438          string currentUser = Environment.UserName;
 85439           if (currentUser.Length > UNAMELEN) {
 0440            currentUser = currentUser.Substring(0, UNAMELEN);
 441          }
 85442          userName = currentUser;
 443        }
 85444      }
 445    }
 446
 447
 448    /// <summary>
 449    /// Get/set the entry's group name.
 450    /// </summary>
 451    /// <remarks>
 452    /// This is only directly relevant to unix systems.
 453    /// </remarks>
 454    public string GroupName {
 110455      get { return groupName; }
 456      set {
 166457         if (value == null) {
 1458          groupName = "None";
 1459        } else {
 165460          groupName = value;
 461        }
 165462      }
 463    }
 464
 465
 466    /// <summary>
 467    /// Get/set the entry's major device number.
 468    /// </summary>
 469    public int DevMajor {
 36470      get { return devMajor; }
 162471      set { devMajor = value; }
 472    }
 473
 474
 475    /// <summary>
 476    /// Get/set the entry's minor device number.
 477    /// </summary>
 478    public int DevMinor {
 34479      get { return devMinor; }
 162480      set { devMinor = value; }
 481    }
 482
 483    #endregion
 484
 485    #region ICloneable Members
 486    /// <summary>
 487    /// Create a new <see cref="TarHeader"/> that is a copy of the current instance.
 488    /// </summary>
 489    /// <returns>A new <see cref="Object"/> that is a copy of the current instance.</returns>
 490    public object Clone()
 491    {
 2492      return MemberwiseClone();
 493    }
 494    #endregion
 495
 496    /// <summary>
 497    /// Parse TarHeader information from a header buffer.
 498    /// </summary>
 499    /// <param name = "header">
 500    /// The tar entry header buffer to get information from.
 501    /// </param>
 502    public void ParseBuffer(byte[] header)
 503    {
 3504       if (header == null) {
 0505        throw new ArgumentNullException(nameof(header));
 506      }
 507
 3508      int offset = 0;
 509
 3510      name = ParseName(header, offset, NAMELEN).ToString();
 3511      offset += NAMELEN;
 512
 3513      mode = (int)ParseOctal(header, offset, MODELEN);
 3514      offset += MODELEN;
 515
 3516      UserId = (int)ParseOctal(header, offset, UIDLEN);
 3517      offset += UIDLEN;
 518
 3519      GroupId = (int)ParseOctal(header, offset, GIDLEN);
 3520      offset += GIDLEN;
 521
 3522      Size = ParseBinaryOrOctal(header, offset, SIZELEN);
 3523      offset += SIZELEN;
 524
 3525      ModTime = GetDateTimeFromCTime(ParseOctal(header, offset, MODTIMELEN));
 3526      offset += MODTIMELEN;
 527
 3528      checksum = (int)ParseOctal(header, offset, CHKSUMLEN);
 3529      offset += CHKSUMLEN;
 530
 3531      TypeFlag = header[offset++];
 532
 3533      LinkName = ParseName(header, offset, NAMELEN).ToString();
 3534      offset += NAMELEN;
 535
 3536      Magic = ParseName(header, offset, MAGICLEN).ToString();
 3537      offset += MAGICLEN;
 538
 3539       if (Magic == "ustar")
 540      {
 0541        Version = ParseName(header, offset, VERSIONLEN).ToString();
 0542        offset += VERSIONLEN;
 543
 0544        UserName = ParseName(header, offset, UNAMELEN).ToString();
 0545        offset += UNAMELEN;
 546
 0547        GroupName = ParseName(header, offset, GNAMELEN).ToString();
 0548        offset += GNAMELEN;
 549
 0550        DevMajor = (int) ParseOctal(header, offset, DEVLEN);
 0551        offset += DEVLEN;
 552
 0553        DevMinor = (int) ParseOctal(header, offset, DEVLEN);
 0554        offset += DEVLEN;
 555
 0556        string prefix = ParseName(header, offset, PREFIXLEN).ToString();
 0557         if (!string.IsNullOrEmpty(prefix)) Name = prefix + '/' + Name;
 558      }
 559
 3560      isChecksumValid = Checksum == TarHeader.MakeCheckSum(header);
 3561    }
 562
 563    /// <summary>
 564    /// 'Write' header information to buffer provided, updating the <see cref="Checksum">check sum</see>.
 565    /// </summary>
 566    /// <param name="outBuffer">output buffer for header information</param>
 567    public void WriteHeader(byte[] outBuffer)
 568    {
 70569       if (outBuffer == null) {
 0570        throw new ArgumentNullException(nameof(outBuffer));
 571      }
 572
 70573      int offset = 0;
 574
 70575      offset = GetNameBytes(Name, outBuffer, offset, NAMELEN);
 70576      offset = GetOctalBytes(mode, outBuffer, offset, MODELEN);
 70577      offset = GetOctalBytes(UserId, outBuffer, offset, UIDLEN);
 70578      offset = GetOctalBytes(GroupId, outBuffer, offset, GIDLEN);
 579
 70580      offset = GetBinaryOrOctalBytes(Size, outBuffer, offset, SIZELEN);
 70581      offset = GetOctalBytes(GetCTime(ModTime), outBuffer, offset, MODTIMELEN);
 582
 70583      int csOffset = offset;
 1260584       for (int c = 0; c < CHKSUMLEN; ++c) {
 560585        outBuffer[offset++] = (byte)' ';
 586      }
 587
 70588      outBuffer[offset++] = TypeFlag;
 589
 70590      offset = GetNameBytes(LinkName, outBuffer, offset, NAMELEN);
 70591      offset = GetAsciiBytes(Magic, 0, outBuffer, offset, MAGICLEN);
 70592      offset = GetNameBytes(Version, outBuffer, offset, VERSIONLEN);
 70593      offset = GetNameBytes(UserName, outBuffer, offset, UNAMELEN);
 70594      offset = GetNameBytes(GroupName, outBuffer, offset, GNAMELEN);
 595
 70596       if ((TypeFlag == LF_CHR) || (TypeFlag == LF_BLK)) {
 0597        offset = GetOctalBytes(DevMajor, outBuffer, offset, DEVLEN);
 0598        offset = GetOctalBytes(DevMinor, outBuffer, offset, DEVLEN);
 599      }
 600
 12880601       for (; offset < outBuffer.Length;) {
 12810602        outBuffer[offset++] = 0;
 603      }
 604
 70605      checksum = ComputeCheckSum(outBuffer);
 606
 70607      GetCheckSumOctalBytes(checksum, outBuffer, csOffset, CHKSUMLEN);
 70608      isChecksumValid = true;
 70609    }
 610
 611    /// <summary>
 612    /// Get a hash code for the current object.
 613    /// </summary>
 614    /// <returns>A hash code for the current object.</returns>
 615    public override int GetHashCode()
 616    {
 0617      return Name.GetHashCode();
 618    }
 619
 620    /// <summary>
 621    /// Determines if this instance is equal to the specified object.
 622    /// </summary>
 623    /// <param name="obj">The object to compare with.</param>
 624    /// <returns>true if the objects are equal, false otherwise.</returns>
 625    public override bool Equals(object obj)
 626    {
 29627      var localHeader = obj as TarHeader;
 628
 629      bool result;
 29630       if (localHeader != null) {
 29631        result = (name == localHeader.name)
 29632          && (mode == localHeader.mode)
 29633          && (UserId == localHeader.UserId)
 29634          && (GroupId == localHeader.GroupId)
 29635          && (Size == localHeader.Size)
 29636          && (ModTime == localHeader.ModTime)
 29637          && (Checksum == localHeader.Checksum)
 29638          && (TypeFlag == localHeader.TypeFlag)
 29639          && (LinkName == localHeader.LinkName)
 29640          && (Magic == localHeader.Magic)
 29641          && (Version == localHeader.Version)
 29642          && (UserName == localHeader.UserName)
 29643          && (GroupName == localHeader.GroupName)
 29644          && (DevMajor == localHeader.DevMajor)
 29645          && (DevMinor == localHeader.DevMinor);
 29646      } else {
 0647        result = false;
 648      }
 29649      return result;
 650    }
 651
 652    /// <summary>
 653    /// Set defaults for values used when constructing a TarHeader instance.
 654    /// </summary>
 655    /// <param name="userId">Value to apply as a default for userId.</param>
 656    /// <param name="userName">Value to apply as a default for userName.</param>
 657    /// <param name="groupId">Value to apply as a default for groupId.</param>
 658    /// <param name="groupName">Value to apply as a default for groupName.</param>
 659    static internal void SetValueDefaults(int userId, string userName, int groupId, string groupName)
 660    {
 0661      defaultUserId = userIdAsSet = userId;
 0662      defaultUser = userNameAsSet = userName;
 0663      defaultGroupId = groupIdAsSet = groupId;
 0664      defaultGroupName = groupNameAsSet = groupName;
 0665    }
 666
 667    static internal void RestoreSetValues()
 668    {
 0669      defaultUserId = userIdAsSet;
 0670      defaultUser = userNameAsSet;
 0671      defaultGroupId = groupIdAsSet;
 0672      defaultGroupName = groupNameAsSet;
 0673    }
 674
 675    // Return value that may be stored in octal or binary. Length must exceed 8.
 676    //
 677    static private long ParseBinaryOrOctal(byte[] header, int offset, int length)
 678    {
 3679       if (header[offset] >= 0x80) {
 680        // File sizes over 8GB are stored in 8 right-justified bytes of binary indicated by setting the high-order bit o
 0681        long result = 0;
 0682         for (int pos = length - 8; pos < length; pos++) {
 0683          result = result << 8 | header[offset + pos];
 684        }
 0685        return result;
 686      }
 3687      return ParseOctal(header, offset, length);
 688    }
 689
 690    /// <summary>
 691    /// Parse an octal string from a header buffer.
 692    /// </summary>
 693    /// <param name = "header">The header buffer from which to parse.</param>
 694    /// <param name = "offset">The offset into the buffer from which to parse.</param>
 695    /// <param name = "length">The number of header bytes to parse.</param>
 696    /// <returns>The long equivalent of the octal string.</returns>
 697    static public long ParseOctal(byte[] header, int offset, int length)
 698    {
 18699       if (header == null) {
 0700        throw new ArgumentNullException(nameof(header));
 701      }
 702
 18703      long result = 0;
 18704      bool stillPadding = true;
 705
 18706      int end = offset + length;
 330707       for (int i = offset; i < end; ++i) {
 165708         if (header[i] == 0) {
 709          break;
 710        }
 711
 147712         if (header[i] == (byte)' ' || header[i] == '0') {
 102713           if (stillPadding) {
 714            continue;
 715          }
 716
 15717           if (header[i] == (byte)' ') {
 718            break;
 719          }
 720        }
 721
 60722        stillPadding = false;
 723
 60724        result = (result << 3) + (header[i] - '0');
 725      }
 726
 18727      return result;
 728    }
 729
 730    /// <summary>
 731    /// Parse a name from a header buffer.
 732    /// </summary>
 733    /// <param name="header">
 734    /// The header buffer from which to parse.
 735    /// </param>
 736    /// <param name="offset">
 737    /// The offset into the buffer from which to parse.
 738    /// </param>
 739    /// <param name="length">
 740    /// The number of header bytes to parse.
 741    /// </param>
 742    /// <returns>
 743    /// The name parsed.
 744    /// </returns>
 745    static public StringBuilder ParseName(byte[] header, int offset, int length)
 746    {
 9747       if (header == null) {
 0748        throw new ArgumentNullException(nameof(header));
 749      }
 750
 9751       if (offset < 0) {
 0752        throw new ArgumentOutOfRangeException(nameof(offset), "Cannot be less than zero");
 753      }
 754
 9755       if (length < 0) {
 0756        throw new ArgumentOutOfRangeException(nameof(length), "Cannot be less than zero");
 757      }
 758
 9759       if (offset + length > header.Length) {
 0760        throw new ArgumentException("Exceeds header size", nameof(length));
 761      }
 762
 9763      var result = new StringBuilder(length);
 764
 108765       for (int i = offset; i < offset + length; ++i) {
 51766         if (header[i] == 0) {
 767          break;
 768        }
 45769        result.Append((char)header[i]);
 770      }
 771
 9772      return result;
 773    }
 774
 775    /// <summary>
 776    /// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes
 777    /// </summary>
 778    /// <param name="name">The name to add</param>
 779    /// <param name="nameOffset">The offset of the first character</param>
 780    /// <param name="buffer">The buffer to add to</param>
 781    /// <param name="bufferOffset">The index of the first byte to add</param>
 782    /// <param name="length">The number of characters/bytes to add</param>
 783    /// <returns>The next free index in the <paramref name="buffer"/></returns>
 784    public static int GetNameBytes(StringBuilder name, int nameOffset, byte[] buffer, int bufferOffset, int length)
 785    {
 0786       if (name == null) {
 0787        throw new ArgumentNullException(nameof(name));
 788      }
 789
 0790       if (buffer == null) {
 0791        throw new ArgumentNullException(nameof(buffer));
 792      }
 793
 0794      return GetNameBytes(name.ToString(), nameOffset, buffer, bufferOffset, length);
 795    }
 796
 797    /// <summary>
 798    /// Add <paramref name="name">name</paramref> to the buffer as a collection of bytes
 799    /// </summary>
 800    /// <param name="name">The name to add</param>
 801    /// <param name="nameOffset">The offset of the first character</param>
 802    /// <param name="buffer">The buffer to add to</param>
 803    /// <param name="bufferOffset">The index of the first byte to add</param>
 804    /// <param name="length">The number of characters/bytes to add</param>
 805    /// <returns>The next free index in the <paramref name="buffer"/></returns>
 806    public static int GetNameBytes(string name, int nameOffset, byte[] buffer, int bufferOffset, int length)
 807    {
 350808       if (name == null) {
 0809        throw new ArgumentNullException(nameof(name));
 810      }
 811
 350812       if (buffer == null) {
 0813        throw new ArgumentNullException(nameof(buffer));
 814      }
 815
 816      int i;
 817
 2100818       for (i = 0 ; i < length && nameOffset + i < name.Length; ++i) {
 700819        buffer[bufferOffset + i] = (byte)name[nameOffset + i];
 820      }
 821
 36190822       for (; i < length; ++i) {
 17920823        buffer[bufferOffset + i] = 0;
 824      }
 825
 350826      return bufferOffset + length;
 827    }
 828
 829    /// <summary>
 830    /// Add an entry name to the buffer
 831    /// </summary>
 832    /// <param name="name">
 833    /// The name to add
 834    /// </param>
 835    /// <param name="buffer">
 836    /// The buffer to add to
 837    /// </param>
 838    /// <param name="offset">
 839    /// The offset into the buffer from which to start adding
 840    /// </param>
 841    /// <param name="length">
 842    /// The number of header bytes to add
 843    /// </param>
 844    /// <returns>
 845    /// The index of the next free byte in the buffer
 846    /// </returns>
 847    public static int GetNameBytes(StringBuilder name, byte[] buffer, int offset, int length)
 848    {
 849
 0850       if (name == null) {
 0851        throw new ArgumentNullException(nameof(name));
 852      }
 853
 0854       if (buffer == null) {
 0855        throw new ArgumentNullException(nameof(buffer));
 856      }
 857
 0858      return GetNameBytes(name.ToString(), 0, buffer, offset, length);
 859    }
 860
 861    /// <summary>
 862    /// Add an entry name to the buffer
 863    /// </summary>
 864    /// <param name="name">The name to add</param>
 865    /// <param name="buffer">The buffer to add to</param>
 866    /// <param name="offset">The offset into the buffer from which to start adding</param>
 867    /// <param name="length">The number of header bytes to add</param>
 868    /// <returns>The index of the next free byte in the buffer</returns>
 869    public static int GetNameBytes(string name, byte[] buffer, int offset, int length)
 870    {
 871
 350872       if (name == null) {
 0873        throw new ArgumentNullException(nameof(name));
 874      }
 875
 350876       if (buffer == null) {
 0877        throw new ArgumentNullException(nameof(buffer));
 878      }
 879
 350880      return GetNameBytes(name, 0, buffer, offset, length);
 881    }
 882
 883    /// <summary>
 884    /// Add a string to a buffer as a collection of ascii bytes.
 885    /// </summary>
 886    /// <param name="toAdd">The string to add</param>
 887    /// <param name="nameOffset">The offset of the first character to add.</param>
 888    /// <param name="buffer">The buffer to add to.</param>
 889    /// <param name="bufferOffset">The offset to start adding at.</param>
 890    /// <param name="length">The number of ascii characters to add.</param>
 891    /// <returns>The next free index in the buffer.</returns>
 892    public static int GetAsciiBytes(string toAdd, int nameOffset, byte[] buffer, int bufferOffset, int length)
 893    {
 70894       if (toAdd == null) {
 0895        throw new ArgumentNullException(nameof(toAdd));
 896      }
 897
 70898       if (buffer == null) {
 0899        throw new ArgumentNullException(nameof(buffer));
 900      }
 901
 980902       for (int i = 0; i < length && nameOffset + i < toAdd.Length; ++i) {
 420903        buffer[bufferOffset + i] = (byte)toAdd[nameOffset + i];
 904      }
 70905      return bufferOffset + length;
 906    }
 907
 908    /// <summary>
 909    /// Put an octal representation of a value into a buffer
 910    /// </summary>
 911    /// <param name = "value">
 912    /// the value to be converted to octal
 913    /// </param>
 914    /// <param name = "buffer">
 915    /// buffer to store the octal string
 916    /// </param>
 917    /// <param name = "offset">
 918    /// The offset into the buffer where the value starts
 919    /// </param>
 920    /// <param name = "length">
 921    /// The length of the octal string to create
 922    /// </param>
 923    /// <returns>
 924    /// The offset of the character next byte after the octal string
 925    /// </returns>
 926    public static int GetOctalBytes(long value, byte[] buffer, int offset, int length)
 927    {
 420928       if (buffer == null) {
 0929        throw new ArgumentNullException(nameof(buffer));
 930      }
 931
 420932      int localIndex = length - 1;
 933
 934      // Either a space or null is valid here.  We use NULL as per GNUTar
 420935      buffer[offset + localIndex] = 0;
 420936      --localIndex;
 937
 420938       if (value > 0) {
 4218939         for (long v = value; (localIndex >= 0) && (v > 0); --localIndex) {
 1831940          buffer[offset + localIndex] = (byte)((byte)'0' + (byte)(v & 7));
 1831941          v >>= 3;
 942        }
 943      }
 944
 3618945       for (; localIndex >= 0; --localIndex) {
 1599946        buffer[offset + localIndex] = (byte)'0';
 947      }
 948
 420949      return offset + length;
 950    }
 951
 952    /// <summary>
 953    /// Put an octal or binary representation of a value into a buffer
 954    /// </summary>
 955    /// <param name = "value">Value to be convert to octal</param>
 956    /// <param name = "buffer">The buffer to update</param>
 957    /// <param name = "offset">The offset into the buffer to store the value</param>
 958    /// <param name = "length">The length of the octal string. Must be 12.</param>
 959    /// <returns>Index of next byte</returns>
 960    private static int GetBinaryOrOctalBytes(long value, byte[] buffer, int offset, int length)
 961    {
 70962       if (value > 0x1FFFFFFFF) {  // Octal 77777777777 (11 digits)
 963                    // Put value as binary, right-justified into the buffer. Set high order bit of left-most byte.
 0964         for (int pos = length - 1; pos > 0; pos--) {
 0965          buffer[offset + pos] = (byte)value;
 0966          value = value >> 8;
 967        }
 0968        buffer[offset] = 0x80;
 0969        return offset + length;
 970      }
 70971      return GetOctalBytes(value, buffer, offset, length);
 972    }
 973
 974    /// <summary>
 975    /// Add the checksum integer to header buffer.
 976    /// </summary>
 977    /// <param name = "value"></param>
 978    /// <param name = "buffer">The header buffer to set the checksum for</param>
 979    /// <param name = "offset">The offset into the buffer for the checksum</param>
 980    /// <param name = "length">The number of header bytes to update.
 981    /// It's formatted differently from the other fields: it has 6 digits, a
 982    /// null, then a space -- rather than digits, a space, then a null.
 983    /// The final space is already there, from checksumming
 984    /// </param>
 985    /// <returns>The modified buffer offset</returns>
 986    static void GetCheckSumOctalBytes(long value, byte[] buffer, int offset, int length)
 987    {
 70988      GetOctalBytes(value, buffer, offset, length - 1);
 70989    }
 990
 991    /// <summary>
 992    /// Compute the checksum for a tar entry header.
 993    /// The checksum field must be all spaces prior to this happening
 994    /// </summary>
 995    /// <param name = "buffer">The tar entry's header buffer.</param>
 996    /// <returns>The computed checksum.</returns>
 997    static int ComputeCheckSum(byte[] buffer)
 998    {
 70999      int sum = 0;
 718201000       for (int i = 0; i < buffer.Length; ++i) {
 358401001        sum += buffer[i];
 1002      }
 701003      return sum;
 1004    }
 1005
 1006    /// <summary>
 1007    /// Make a checksum for a tar entry ignoring the checksum contents.
 1008    /// </summary>
 1009    /// <param name = "buffer">The tar entry's header buffer.</param>
 1010    /// <returns>The checksum for the buffer</returns>
 1011    static int MakeCheckSum(byte[] buffer)
 1012    {
 31013      int sum = 0;
 8941014       for (int i = 0; i < CHKSUMOFS; ++i) {
 4441015        sum += buffer[i];
 1016      }
 1017
 541018       for (int i = 0; i < CHKSUMLEN; ++i) {
 241019        sum += (byte)' ';
 1020      }
 1021
 21421022       for (int i = CHKSUMOFS + CHKSUMLEN; i < buffer.Length; ++i) {
 10681023        sum += buffer[i];
 1024      }
 31025      return sum;
 1026    }
 1027
 1028    static int GetCTime(DateTime dateTime)
 1029    {
 701030      return unchecked((int)((dateTime.Ticks - dateTime1970.Ticks) / timeConversionFactor));
 1031    }
 1032
 1033    static DateTime GetDateTimeFromCTime(long ticks)
 1034    {
 1035      DateTime result;
 1036
 1037      try {
 31038        result = new DateTime(dateTime1970.Ticks + ticks * timeConversionFactor);
 31039      } catch (ArgumentOutOfRangeException) {
 01040        result = dateTime1970;
 01041      }
 31042      return result;
 1043    }
 1044
 1045    #region Instance Fields
 1046    string name;
 1047    int mode;
 1048    int userId;
 1049    int groupId;
 1050    long size;
 1051    DateTime modTime;
 1052    int checksum;
 1053    bool isChecksumValid;
 1054    byte typeFlag;
 1055    string linkName;
 1056    string magic;
 1057    string version;
 1058    string userName;
 1059    string groupName;
 1060    int devMajor;
 1061    int devMinor;
 1062    #endregion
 1063
 1064    #region Class Fields
 1065    // Values used during recursive operations.
 1066    static internal int userIdAsSet;
 1067    static internal int groupIdAsSet;
 1068    static internal string userNameAsSet;
 11069    static internal string groupNameAsSet = "None";
 1070
 1071    static internal int defaultUserId;
 1072    static internal int defaultGroupId;
 11073    static internal string defaultGroupName = "None";
 1074    static internal string defaultUser;
 1075    #endregion
 1076  }
 1077}