001/* 002** Authored by Timothy Gerard Endres 003** <mailto:time@gjt.org> <http://www.trustice.com> 004** 005** This work has been placed into the public domain. 006** You may use this work in any way and for any purpose you wish. 007** 008** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, 009** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR 010** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY 011** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR 012** REDISTRIBUTION OF THIS SOFTWARE. 013** 014*/ 015 016package com.ice.tar; 017 018/** 019 * This class encapsulates the Tar Entry Header used in Tar Archives. 020 * The class also holds a number of tar constants, used mostly in headers. 021 * 022 * @author Timothy Gerard Endres, <time@gjt.org> 023 */ 024 025public 026class TarHeader 027extends Object 028implements Cloneable 029 { 030 /** 031 * The length of the name field in a header buffer. 032 */ 033 public static final int NAMELEN = 100; 034 /** 035 * The offset of the name field in a header buffer. 036 */ 037 public static final int NAMEOFFSET = 0; 038 /** 039 * The length of the name prefix field in a header buffer. 040 */ 041 public static final int PREFIXLEN = 155; 042 /** 043 * The offset of the name prefix field in a header buffer. 044 */ 045 public static final int PREFIXOFFSET = 345; 046 /** 047 * The length of the mode field in a header buffer. 048 */ 049 public static final int MODELEN = 8; 050 /** 051 * The length of the user id field in a header buffer. 052 */ 053 public static final int UIDLEN = 8; 054 /** 055 * The length of the group id field in a header buffer. 056 */ 057 public static final int GIDLEN = 8; 058 /** 059 * The length of the checksum field in a header buffer. 060 */ 061 public static final int CHKSUMLEN = 8; 062 /** 063 * The length of the size field in a header buffer. 064 */ 065 public static final int SIZELEN = 12; 066 /** 067 * The length of the magic field in a header buffer. 068 */ 069 public static final int MAGICLEN = 8; 070 /** 071 * The length of the modification time field in a header buffer. 072 */ 073 public static final int MODTIMELEN = 12; 074 /** 075 * The length of the user name field in a header buffer. 076 */ 077 public static final int UNAMELEN = 32; 078 /** 079 * The length of the group name field in a header buffer. 080 */ 081 public static final int GNAMELEN = 32; 082 /** 083 * The length of the devices field in a header buffer. 084 */ 085 public static final int DEVLEN = 8; 086 087 /** 088 * LF_ constants represent the "link flag" of an entry, or more commonly, 089 * the "entry type". This is the "old way" of indicating a normal file. 090 */ 091 public static final byte LF_OLDNORM = 0; 092 /** 093 * Normal file type. 094 */ 095 public static final byte LF_NORMAL = (byte) '0'; 096 /** 097 * Link file type. 098 */ 099 public static final byte LF_LINK = (byte) '1'; 100 /** 101 * Symbolic link file type. 102 */ 103 public static final byte LF_SYMLINK = (byte) '2'; 104 /** 105 * Character device file type. 106 */ 107 public static final byte LF_CHR = (byte) '3'; 108 /** 109 * Block device file type. 110 */ 111 public static final byte LF_BLK = (byte) '4'; 112 /** 113 * Directory file type. 114 */ 115 public static final byte LF_DIR = (byte) '5'; 116 /** 117 * FIFO (pipe) file type. 118 */ 119 public static final byte LF_FIFO = (byte) '6'; 120 /** 121 * Contiguous file type. 122 */ 123 public static final byte LF_CONTIG = (byte) '7'; 124 125 /** 126 * The magic tag representing a POSIX tar archive. 127 */ 128 public static final String TMAGIC = "ustar"; 129 130 /** 131 * The magic tag representing a GNU tar archive. 132 */ 133 public static final String GNU_TMAGIC = "ustar "; 134 135 /** 136 * The entry's name. 137 */ 138 public StringBuffer name; 139 /** 140 * The entry's permission mode. 141 */ 142 public int mode; 143 /** 144 * The entry's user id. 145 */ 146 public int userId; 147 /** 148 * The entry's group id. 149 */ 150 public int groupId; 151 /** 152 * The entry's size. 153 */ 154 public long size; 155 /** 156 * The entry's modification time. 157 */ 158 public long modTime; 159 /** 160 * The entry's checksum. 161 */ 162 public int checkSum; 163 /** 164 * The entry's link flag. 165 */ 166 public byte linkFlag; 167 /** 168 * The entry's link name. 169 */ 170 public StringBuffer linkName; 171 /** 172 * The entry's magic tag. 173 */ 174 public StringBuffer magic; 175 /** 176 * The entry's user name. 177 */ 178 public StringBuffer userName; 179 /** 180 * The entry's group name. 181 */ 182 public StringBuffer groupName; 183 /** 184 * The entry's major device number. 185 */ 186 public int devMajor; 187 /** 188 * The entry's minor device number. 189 */ 190 public int devMinor; 191 192 193 public 194 TarHeader() 195 { 196 this.magic = new StringBuffer( TarHeader.TMAGIC ); 197 198 this.name = new StringBuffer(); 199 this.linkName = new StringBuffer(); 200 201 String user = 202 System.getProperty( "user.name", "" ); 203 204 if ( user.length() > 31 ) 205 user = user.substring( 0, 31 ); 206 207 this.userId = 0; 208 this.groupId = 0; 209 this.userName = new StringBuffer( user ); 210 this.groupName = new StringBuffer( "" ); 211 } 212 213 /** 214 * TarHeaders can be cloned. 215 */ 216 public Object 217 clone() 218 { 219 TarHeader hdr = null; 220 221 try { 222 hdr = (TarHeader) super.clone(); 223 224 hdr.name = 225 (this.name == null ) ? null 226 : new StringBuffer( this.name.toString() ); 227 hdr.mode = this.mode; 228 hdr.userId = this.userId; 229 hdr.groupId = this.groupId; 230 hdr.size = this.size; 231 hdr.modTime = this.modTime; 232 hdr.checkSum = this.checkSum; 233 hdr.linkFlag = this.linkFlag; 234 hdr.linkName = 235 (this.linkName == null ) ? null 236 : new StringBuffer( this.linkName.toString() ); 237 hdr.magic = 238 (this.magic == null ) ? null 239 : new StringBuffer( this.magic.toString() ); 240 hdr.userName = 241 (this.userName == null ) ? null 242 : new StringBuffer( this.userName.toString() ); 243 hdr.groupName = 244 (this.groupName == null ) ? null 245 : new StringBuffer( this.groupName.toString() ); 246 hdr.devMajor = this.devMajor; 247 hdr.devMinor = this.devMinor; 248 } 249 catch ( CloneNotSupportedException ex ) 250 { 251 ex.printStackTrace( System.err ); 252 } 253 254 return hdr; 255 } 256 257 /** 258 * Get the name of this entry. 259 * 260 * @return Teh entry's name. 261 */ 262 public String 263 getName() 264 { 265 return this.name.toString(); 266 } 267 268 /** 269 * Parse an octal string from a header buffer. This is used for the 270 * file permission mode value. 271 * 272 * @param header The header buffer from which to parse. 273 * @param offset The offset into the buffer from which to parse. 274 * @param length The number of header bytes to parse. 275 * @return The long value of the octal string. 276 */ 277 public static long 278 parseOctal( byte[] header, int offset, int length ) 279 throws InvalidHeaderException 280 { 281 long result = 0; 282 boolean stillPadding = true; 283 284 int end = offset + length; 285 for ( int i = offset ; i < end ; ++i ) 286 { 287 if ( header[i] == 0 ) 288 break; 289 290 if ( header[i] == (byte) ' ' || header[i] == '0' ) 291 { 292 if ( stillPadding ) 293 continue; 294 295 if ( header[i] == (byte) ' ' ) 296 break; 297 } 298 299 stillPadding = false; 300 301 result = 302 (result << 3) 303 + (header[i] - '0'); 304 } 305 306 return result; 307 } 308 309 /** 310 * Parse a file name from a header buffer. This is different from 311 * parseName() in that is recognizes 'ustar' names and will handle 312 * adding on the "prefix" field to the name. 313 * 314 * Contributed by Dmitri Tikhonov <dxt2431@yahoo.com> 315 * 316 * @param header The header buffer from which to parse. 317 * @param offset The offset into the buffer from which to parse. 318 * @param length The number of header bytes to parse. 319 * @return The header's entry name. 320 */ 321 public static StringBuffer 322 parseFileName( byte[] header ) 323 { 324 StringBuffer result = new StringBuffer( 256 ); 325 326 // If header[345] is not equal to zero, then it is the "prefix" 327 // that 'ustar' defines. It must be prepended to the "normal" 328 // name field. We are responsible for the separating '/'. 329 // 330 if ( header[345] != 0 ) 331 { 332 for ( int i = 345 ; i < 500 && header[i] != 0 ; ++i ) 333 { 334 result.append( (char)header[i] ); 335 } 336 337 result.append( "/" ); 338 } 339 340 for ( int i = 0 ; i < 100 && header[i] != 0 ; ++i ) 341 { 342 result.append( (char)header[i] ); 343 } 344 345 return result; 346 } 347 348 /** 349 * Parse an entry name from a header buffer. 350 * 351 * @param header The header buffer from which to parse. 352 * @param offset The offset into the buffer from which to parse. 353 * @param length The number of header bytes to parse. 354 * @return The header's entry name. 355 */ 356 public static StringBuffer 357 parseName( byte[] header, int offset, int length ) 358 throws InvalidHeaderException 359 { 360 StringBuffer result = new StringBuffer( length ); 361 362 int end = offset + length; 363 for ( int i = offset ; i < end ; ++i ) 364 { 365 if ( header[i] == 0 ) 366 break; 367 result.append( (char)header[i] ); 368 } 369 370 return result; 371 } 372 373 /** 374 * This method, like getNameBytes(), is intended to place a name 375 * into a TarHeader's buffer. However, this method is sophisticated 376 * enough to recognize long names (name.length() > NAMELEN). In these 377 * cases, the method will break the name into a prefix and suffix and 378 * place the name in the header in 'ustar' format. It is up to the 379 * TarEntry to manage the "entry header format". This method assumes 380 * the name is valid for the type of archive being generated. 381 * 382 * @param outbuf The buffer containing the entry header to modify. 383 * @param newName The new name to place into the header buffer. 384 * @return The current offset in the tar header (always TarHeader.NAMELEN). 385 * @throws InvalidHeaderException If the name will not fit in the header. 386 */ 387 public static int 388 getFileNameBytes( String newName, byte[] outbuf ) 389 throws InvalidHeaderException 390 { 391 if ( newName.length() > 100 ) 392 { 393 // Locate a pathname "break" prior to the maximum name length... 394 int index = newName.indexOf( "/", newName.length() - 100 ); 395 if ( index == -1 ) 396 throw new InvalidHeaderException 397 ( "file name is greater than 100 characters, " + newName ); 398 399 // Get the "suffix subpath" of the name. 400 String name = newName.substring( index + 1 ); 401 402 // Get the "prefix subpath", or "prefix", of the name. 403 String prefix = newName.substring( 0, index ); 404 if ( prefix.length() > TarHeader.PREFIXLEN ) 405 throw new InvalidHeaderException 406 ( "file prefix is greater than 155 characters" ); 407 408 TarHeader.getNameBytes 409 ( new StringBuffer( name ), outbuf, 410 TarHeader.NAMEOFFSET, TarHeader.NAMELEN ); 411 412 TarHeader.getNameBytes 413 ( new StringBuffer( prefix ), outbuf, 414 TarHeader.PREFIXOFFSET, TarHeader.PREFIXLEN ); 415 } 416 else 417 { 418 TarHeader.getNameBytes 419 ( new StringBuffer( newName ), outbuf, 420 TarHeader.NAMEOFFSET, TarHeader.NAMELEN ); 421 } 422 423 // The offset, regardless of the format, is now the end of the 424 // original name field. 425 // 426 return TarHeader.NAMELEN; 427 } 428 429 /** 430 * Move the bytes from the name StringBuffer into the header's buffer. 431 * 432 * @param header The header buffer into which to copy the name. 433 * @param offset The offset into the buffer at which to store. 434 * @param length The number of header bytes to store. 435 * @return The new offset (offset + length). 436 */ 437 public static int 438 getNameBytes( StringBuffer name, byte[] buf, int offset, int length ) 439 { 440 int i; 441 442 for ( i = 0 ; i < length && i < name.length() ; ++i ) 443 { 444 buf[ offset + i ] = (byte) name.charAt( i ); 445 } 446 447 for ( ; i < length ; ++i ) 448 { 449 buf[ offset + i ] = 0; 450 } 451 452 return offset + length; 453 } 454 455 /** 456 * Parse an octal integer from a header buffer. 457 * 458 * @param header The header buffer from which to parse. 459 * @param offset The offset into the buffer from which to parse. 460 * @param length The number of header bytes to parse. 461 * @return The integer value of the octal bytes. 462 */ 463 public static int 464 getOctalBytes( long value, byte[] buf, int offset, int length ) 465 { 466 byte[] result = new byte[ length ]; 467 468 int idx = length - 1; 469 470 buf[ offset + idx ] = 0; 471 --idx; 472 buf[ offset + idx ] = (byte) ' '; 473 --idx; 474 475 if ( value == 0 ) 476 { 477 buf[ offset + idx ] = (byte) '0'; 478 --idx; 479 } 480 else 481 { 482 for ( long val = value ; idx >= 0 && val > 0 ; --idx ) 483 { 484 buf[ offset + idx ] = (byte) 485 ( (byte) '0' + (byte) (val & 7) ); 486 val = val >> 3; 487 } 488 } 489 490 for ( ; idx >= 0 ; --idx ) 491 { 492 buf[ offset + idx ] = (byte) ' '; 493 } 494 495 return offset + length; 496 } 497 498 /** 499 * Parse an octal long integer from a header buffer. 500 * 501 * @param header The header buffer from which to parse. 502 * @param offset The offset into the buffer from which to parse. 503 * @param length The number of header bytes to parse. 504 * @return The long value of the octal bytes. 505 */ 506 public static int 507 getLongOctalBytes( long value, byte[] buf, int offset, int length ) 508 { 509 byte[] temp = new byte[ length + 1 ]; 510 TarHeader.getOctalBytes( value, temp, 0, length + 1 ); 511 System.arraycopy( temp, 0, buf, offset, length ); 512 return offset + length; 513 } 514 515 /** 516 * Parse the checksum octal integer from a header buffer. 517 * 518 * @param header The header buffer from which to parse. 519 * @param offset The offset into the buffer from which to parse. 520 * @param length The number of header bytes to parse. 521 * @return The integer value of the entry's checksum. 522 */ 523 public static int 524 getCheckSumOctalBytes( long value, byte[] buf, int offset, int length ) 525 { 526 TarHeader.getOctalBytes( value, buf, offset, length ); 527 buf[ offset + length - 1 ] = (byte) ' '; 528 buf[ offset + length - 2 ] = 0; 529 return offset + length; 530 } 531 532 } 533