1 /* 2 ** Authored by Timothy Gerard Endres 3 ** <mailto:time@gjt.org> <http://www.trustice.com> 4 ** 5 ** This work has been placed into the public domain. 6 ** You may use this work in any way and for any purpose you wish. 7 ** 8 ** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, 9 ** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR 10 ** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY 11 ** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR 12 ** REDISTRIBUTION OF THIS SOFTWARE. 13 ** 14 */ 15 16 package com.ice.tar; 17 18 import java.io.*; 19 import java.util.Date; 20 21 22 /** 23 * 24 * This class represents an entry in a Tar archive. It consists 25 * of the entry's header, as well as the entry's File. Entries 26 * can be instantiated in one of three ways, depending on how 27 * they are to be used. 28 * <p> 29 * TarEntries that are created from the header bytes read from 30 * an archive are instantiated with the TarEntry( byte[] ) 31 * constructor. These entries will be used when extracting from 32 * or listing the contents of an archive. These entries have their 33 * header filled in using the header bytes. They also set the File 34 * to null, since they reference an archive entry not a file. 35 * <p> 36 * TarEntries that are created from Files that are to be written 37 * into an archive are instantiated with the TarEntry( File ) 38 * constructor. These entries have their header filled in using 39 * the File's information. They also keep a reference to the File 40 * for convenience when writing entries. 41 * <p> 42 * Finally, TarEntries can be constructed from nothing but a name. 43 * This allows the programmer to construct the entry by hand, for 44 * instance when only an InputStream is available for writing to 45 * the archive, and the header information is constructed from 46 * other information. In this case the header fields are set to 47 * defaults and the File is set to null. 48 * 49 * <pre> 50 * 51 * Original Unix Tar Header: 52 * 53 * Field Field Field 54 * Width Name Meaning 55 * ----- --------- --------------------------- 56 * 100 name name of file 57 * 8 mode file mode 58 * 8 uid owner user ID 59 * 8 gid owner group ID 60 * 12 size length of file in bytes 61 * 12 mtime modify time of file 62 * 8 chksum checksum for header 63 * 1 link indicator for links 64 * 100 linkname name of linked file 65 * 66 * </pre> 67 * 68 * <pre> 69 * 70 * POSIX "ustar" Style Tar Header: 71 * 72 * Field Field Field 73 * Width Name Meaning 74 * ----- --------- --------------------------- 75 * 100 name name of file 76 * 8 mode file mode 77 * 8 uid owner user ID 78 * 8 gid owner group ID 79 * 12 size length of file in bytes 80 * 12 mtime modify time of file 81 * 8 chksum checksum for header 82 * 1 typeflag type of file 83 * 100 linkname name of linked file 84 * 6 magic USTAR indicator 85 * 2 version USTAR version 86 * 32 uname owner user name 87 * 32 gname owner group name 88 * 8 devmajor device major number 89 * 8 devminor device minor number 90 * 155 prefix prefix for file name 91 * 92 * struct posix_header 93 * { byte offset 94 * char name[100]; 0 95 * char mode[8]; 100 96 * char uid[8]; 108 97 * char gid[8]; 116 98 * char size[12]; 124 99 * char mtime[12]; 136 100 * char chksum[8]; 148 101 * char typeflag; 156 102 * char linkname[100]; 157 103 * char magic[6]; 257 104 * char version[2]; 263 105 * char uname[32]; 265 106 * char gname[32]; 297 107 * char devmajor[8]; 329 108 * char devminor[8]; 337 109 * char prefix[155]; 345 110 * }; 500 111 * 112 * </pre> 113 * 114 * Note that while the class does recognize GNU formatted headers, 115 * it does not perform proper processing of GNU archives. I hope 116 * to add the GNU support someday. 117 * 118 * Directory "size" fix contributed by: 119 * Bert Becker <becker@informatik.hu-berlin.de> 120 * 121 * @see TarHeader 122 * @author Timothy Gerard Endres, <time@gjt.org> 123 */ 124 125 public 126 class TarEntry 127 extends Object 128 implements Cloneable 129 { 130 /** 131 * If this entry represents a File, this references it. 132 */ 133 protected File file; 134 135 /** 136 * This is the entry's header information. 137 */ 138 protected TarHeader header; 139 140 /** 141 * Set to true if this is a "old-unix" format entry. 142 */ 143 protected boolean unixFormat; 144 145 /** 146 * Set to true if this is a 'ustar' format entry. 147 */ 148 protected boolean ustarFormat; 149 150 /** 151 * Set to true if this is a GNU 'ustar' format entry. 152 */ 153 protected boolean gnuFormat; 154 155 156 /** 157 * The default constructor is protected for use only by subclasses. 158 */ 159 protected TarEntry()160 TarEntry() 161 { 162 } 163 164 /** 165 * Construct an entry with only a name. This allows the programmer 166 * to construct the entry's header "by hand". File is set to null. 167 */ 168 public TarEntry( String name )169 TarEntry( String name ) 170 { 171 this.initialize(); 172 this.nameTarHeader( this.header, name ); 173 } 174 175 /** 176 * Construct an entry for a file. File is set to file, and the 177 * header is constructed from information from the file. 178 * 179 * @param file The file that the entry represents. 180 */ 181 public TarEntry( File file )182 TarEntry( File file ) 183 throws InvalidHeaderException 184 { 185 this.initialize(); 186 this.getFileTarHeader( this.header, file ); 187 } 188 189 /** 190 * Construct an entry from an archive's header bytes. File is set 191 * to null. 192 * 193 * @param headerBuf The header bytes from a tar archive entry. 194 */ 195 public TarEntry( byte[] headerBuf )196 TarEntry( byte[] headerBuf ) 197 throws InvalidHeaderException 198 { 199 this.initialize(); 200 this.parseTarHeader( this.header, headerBuf ); 201 } 202 203 /** 204 * Initialization code common to all constructors. 205 */ 206 private void initialize()207 initialize() 208 { 209 this.file = null; 210 this.header = new TarHeader(); 211 212 this.gnuFormat = false; 213 this.ustarFormat = true; // REVIEW What we prefer to use... 214 this.unixFormat = false; 215 } 216 217 /** 218 * Clone the entry. 219 */ 220 public Object clone()221 clone() 222 { 223 TarEntry entry = null; 224 225 try { 226 entry = (TarEntry) super.clone(); 227 228 if ( this.header != null ) 229 { 230 entry.header = (TarHeader) this.header.clone(); 231 } 232 233 if ( this.file != null ) 234 { 235 entry.file = new File( this.file.getAbsolutePath() ); 236 } 237 } 238 catch ( CloneNotSupportedException ex ) 239 { 240 ex.printStackTrace( System.err ); 241 } 242 243 return entry; 244 } 245 246 /** 247 * Returns true if this entry's header is in "ustar" format. 248 * 249 * @return True if the entry's header is in "ustar" format. 250 */ 251 public boolean isUSTarFormat()252 isUSTarFormat() 253 { 254 return this.ustarFormat; 255 } 256 257 /** 258 * Sets this entry's header format to "ustar". 259 */ 260 public void setUSTarFormat()261 setUSTarFormat() 262 { 263 this.ustarFormat = true; 264 this.gnuFormat = false; 265 this.unixFormat = false; 266 } 267 268 /** 269 * Returns true if this entry's header is in the GNU 'ustar' format. 270 * 271 * @return True if the entry's header is in the GNU 'ustar' format. 272 */ 273 public boolean isGNUTarFormat()274 isGNUTarFormat() 275 { 276 return this.gnuFormat; 277 } 278 279 /** 280 * Sets this entry's header format to GNU "ustar". 281 */ 282 public void setGNUTarFormat()283 setGNUTarFormat() 284 { 285 this.gnuFormat = true; 286 this.ustarFormat = false; 287 this.unixFormat = false; 288 } 289 290 /** 291 * Returns true if this entry's header is in the old "unix-tar" format. 292 * 293 * @return True if the entry's header is in the old "unix-tar" format. 294 */ 295 public boolean isUnixTarFormat()296 isUnixTarFormat() 297 { 298 return this.unixFormat; 299 } 300 301 /** 302 * Sets this entry's header format to "unix-style". 303 */ 304 public void setUnixTarFormat()305 setUnixTarFormat() 306 { 307 this.unixFormat = true; 308 this.ustarFormat = false; 309 this.gnuFormat = false; 310 } 311 312 /** 313 * Determine if the two entries are equal. Equality is determined 314 * by the header names being equal. 315 * 316 * @return it Entry to be checked for equality. 317 * @return True if the entries are equal. 318 */ 319 public boolean equals( TarEntry it )320 equals( TarEntry it ) 321 { 322 return 323 this.header.name.toString().equals 324 ( it.header.name.toString() ); 325 } 326 327 /** 328 * Determine if the given entry is a descendant of this entry. 329 * Descendancy is determined by the name of the descendant 330 * starting with this entry's name. 331 * 332 * @param desc Entry to be checked as a descendent of this. 333 * @return True if entry is a descendant of this. 334 */ 335 public boolean isDescendent( TarEntry desc )336 isDescendent( TarEntry desc ) 337 { 338 return 339 desc.header.name.toString().startsWith 340 ( this.header.name.toString() ); 341 } 342 343 /** 344 * Get this entry's header. 345 * 346 * @return This entry's TarHeader. 347 */ 348 public TarHeader getHeader()349 getHeader() 350 { 351 return this.header; 352 } 353 354 /** 355 * Get this entry's name. 356 * 357 * @return This entry's name. 358 */ 359 public String getName()360 getName() 361 { 362 return this.header.name.toString(); 363 } 364 365 /** 366 * Set this entry's name. 367 * 368 * @param name This entry's new name. 369 */ 370 public void setName( String name )371 setName( String name ) 372 { 373 this.header.name = 374 new StringBuffer( name ); 375 } 376 377 /** 378 * Get this entry's user id. 379 * 380 * @return This entry's user id. 381 */ 382 public int getUserId()383 getUserId() 384 { 385 return this.header.userId; 386 } 387 388 /** 389 * Set this entry's user id. 390 * 391 * @param userId This entry's new user id. 392 */ 393 public void setUserId( int userId )394 setUserId( int userId ) 395 { 396 this.header.userId = userId; 397 } 398 399 /** 400 * Get this entry's group id. 401 * 402 * @return This entry's group id. 403 */ 404 public int getGroupId()405 getGroupId() 406 { 407 return this.header.groupId; 408 } 409 410 /** 411 * Set this entry's group id. 412 * 413 * @param groupId This entry's new group id. 414 */ 415 public void setGroupId( int groupId )416 setGroupId( int groupId ) 417 { 418 this.header.groupId = groupId; 419 } 420 421 /** 422 * Get this entry's user name. 423 * 424 * @return This entry's user name. 425 */ 426 public String getUserName()427 getUserName() 428 { 429 return this.header.userName.toString(); 430 } 431 432 /** 433 * Set this entry's user name. 434 * 435 * @param userName This entry's new user name. 436 */ 437 public void setUserName( String userName )438 setUserName( String userName ) 439 { 440 this.header.userName = 441 new StringBuffer( userName ); 442 } 443 444 /** 445 * Get this entry's group name. 446 * 447 * @return This entry's group name. 448 */ 449 public String getGroupName()450 getGroupName() 451 { 452 return this.header.groupName.toString(); 453 } 454 455 /** 456 * Set this entry's group name. 457 * 458 * @param groupName This entry's new group name. 459 */ 460 public void setGroupName( String groupName )461 setGroupName( String groupName ) 462 { 463 this.header.groupName = 464 new StringBuffer( groupName ); 465 } 466 467 /** 468 * Convenience method to set this entry's group and user ids. 469 * 470 * @param userId This entry's new user id. 471 * @param groupId This entry's new group id. 472 */ 473 public void setIds( int userId, int groupId )474 setIds( int userId, int groupId ) 475 { 476 this.setUserId( userId ); 477 this.setGroupId( groupId ); 478 } 479 480 /** 481 * Convenience method to set this entry's group and user names. 482 * 483 * @param userName This entry's new user name. 484 * @param groupName This entry's new group name. 485 */ 486 public void setNames( String userName, String groupName )487 setNames( String userName, String groupName ) 488 { 489 this.setUserName( userName ); 490 this.setGroupName( groupName ); 491 } 492 493 /** 494 * Set this entry's modification time. The parameter passed 495 * to this method is in "Java time". 496 * 497 * @param time This entry's new modification time. 498 */ 499 public void setModTime( long time )500 setModTime( long time ) 501 { 502 this.header.modTime = time / 1000; 503 } 504 505 /** 506 * Set this entry's modification time. 507 * 508 * @param time This entry's new modification time. 509 */ 510 public void setModTime( Date time )511 setModTime( Date time ) 512 { 513 this.header.modTime = time.getTime() / 1000; 514 } 515 516 /** 517 * Set this entry's modification time. 518 * 519 * @param time This entry's new modification time. 520 */ 521 public Date getModTime()522 getModTime() 523 { 524 return new Date( this.header.modTime * 1000 ); 525 } 526 527 /** 528 * Get this entry's file. 529 * 530 * @return This entry's file. 531 */ 532 public File getFile()533 getFile() 534 { 535 return this.file; 536 } 537 538 /** 539 * Get this entry's file size. 540 * 541 * @return This entry's file size. 542 */ 543 public long getSize()544 getSize() 545 { 546 return this.header.size; 547 } 548 549 /** 550 * Set this entry's file size. 551 * 552 * @param size This entry's new file size. 553 */ 554 public void setSize( long size )555 setSize( long size ) 556 { 557 this.header.size = size; 558 } 559 560 /** 561 * Return whether or not this entry represents a directory. 562 * 563 * @return True if this entry is a directory. 564 */ 565 public boolean isDirectory()566 isDirectory() 567 { 568 if ( this.file != null ) 569 return this.file.isDirectory(); 570 571 if ( this.header != null ) 572 { 573 if ( this.header.linkFlag == TarHeader.LF_DIR ) 574 return true; 575 576 if ( this.header.name.toString().endsWith( "/" ) ) 577 return true; 578 } 579 580 return false; 581 } 582 583 /** 584 * Fill in a TarHeader with information from a File. 585 * 586 * @param hdr The TarHeader to fill in. 587 * @param file The file from which to get the header information. 588 */ 589 public void getFileTarHeader( TarHeader hdr, File file )590 getFileTarHeader( TarHeader hdr, File file ) 591 throws InvalidHeaderException 592 { 593 this.file = file; 594 595 String name = file.getPath(); 596 String osname = System.getProperty( "os.name" ); 597 if ( osname != null ) 598 { 599 // Strip off drive letters! 600 // REVIEW Would a better check be "(File.separator == '\')"? 601 602 // String Win32Prefix = "Windows"; 603 // String prefix = osname.substring( 0, Win32Prefix.length() ); 604 // if ( prefix.equalsIgnoreCase( Win32Prefix ) ) 605 606 // if ( File.separatorChar == '\\' ) 607 608 // Windows OS check was contributed by 609 // Patrick Beard <beard@netscape.com> 610 String Win32Prefix = "windows"; 611 if ( osname.toLowerCase().startsWith( Win32Prefix ) ) 612 { 613 if ( name.length() > 2 ) 614 { 615 char ch1 = name.charAt(0); 616 char ch2 = name.charAt(1); 617 if ( ch2 == ':' 618 && ( (ch1 >= 'a' && ch1 <= 'z') 619 || (ch1 >= 'A' && ch1 <= 'Z') ) ) 620 { 621 name = name.substring( 2 ); 622 } 623 } 624 } 625 } 626 627 name = name.replace( File.separatorChar, '/' ); 628 629 // No absolute pathnames 630 // Windows (and Posix?) paths can start with "\\NetworkDrive\", 631 // so we loop on starting /'s. 632 633 for ( ; name.startsWith( "/" ) ; ) 634 name = name.substring( 1 ); 635 636 hdr.linkName = new StringBuffer( "" ); 637 638 hdr.name = new StringBuffer( name ); 639 640 if ( file.isDirectory() ) 641 { 642 hdr.size = 0; 643 hdr.mode = 040755; 644 hdr.linkFlag = TarHeader.LF_DIR; 645 if ( hdr.name.charAt( hdr.name.length() - 1 ) != '/' ) 646 hdr.name.append( "/" ); 647 } 648 else 649 { 650 hdr.size = file.length(); 651 hdr.mode = 0100644; 652 hdr.linkFlag = TarHeader.LF_NORMAL; 653 } 654 655 // UNDONE When File lets us get the userName, use it! 656 657 hdr.modTime = file.lastModified() / 1000; 658 hdr.checkSum = 0; 659 hdr.devMajor = 0; 660 hdr.devMinor = 0; 661 } 662 663 /** 664 * If this entry represents a file, and the file is a directory, return 665 * an array of TarEntries for this entry's children. 666 * 667 * @return An array of TarEntry's for this entry's children. 668 */ 669 public TarEntry[] getDirectoryEntries()670 getDirectoryEntries() 671 throws InvalidHeaderException 672 { 673 if ( this.file == null 674 || ! this.file.isDirectory() ) 675 { 676 return new TarEntry[0]; 677 } 678 679 String[] list = this.file.list(); 680 681 TarEntry[] result = new TarEntry[ list.length ]; 682 683 for ( int i = 0 ; i < list.length ; ++i ) 684 { 685 result[i] = 686 new TarEntry 687 ( new File( this.file, list[i] ) ); 688 } 689 690 return result; 691 } 692 693 /** 694 * Compute the checksum of a tar entry header. 695 * 696 * @param buf The tar entry's header buffer. 697 * @return The computed checksum. 698 */ 699 public long computeCheckSum( byte[] buf )700 computeCheckSum( byte[] buf ) 701 { 702 long sum = 0; 703 704 for ( int i = 0 ; i < buf.length ; ++i ) 705 { 706 sum += 255 & buf[ i ]; 707 } 708 709 return sum; 710 } 711 712 /** 713 * Write an entry's header information to a header buffer. 714 * This method can throw an InvalidHeaderException 715 * 716 * @param outbuf The tar entry header buffer to fill in. 717 * @throws InvalidHeaderException If the name will not fit in the header. 718 */ 719 public void writeEntryHeader( byte[] outbuf )720 writeEntryHeader( byte[] outbuf ) 721 throws InvalidHeaderException 722 { 723 int offset = 0; 724 725 if ( this.isUnixTarFormat() ) 726 { 727 if ( this.header.name.length() > 100 ) 728 throw new InvalidHeaderException 729 ( "file path is greater than 100 characters, " 730 + this.header.name ); 731 } 732 733 offset = TarHeader.getFileNameBytes( this.header.name.toString(), outbuf ); 734 735 offset = TarHeader.getOctalBytes 736 ( this.header.mode, outbuf, offset, TarHeader.MODELEN ); 737 738 offset = TarHeader.getOctalBytes 739 ( this.header.userId, outbuf, offset, TarHeader.UIDLEN ); 740 741 offset = TarHeader.getOctalBytes 742 ( this.header.groupId, outbuf, offset, TarHeader.GIDLEN ); 743 744 long size = this.header.size; 745 746 offset = TarHeader.getLongOctalBytes 747 ( size, outbuf, offset, TarHeader.SIZELEN ); 748 749 offset = TarHeader.getLongOctalBytes 750 ( this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN ); 751 752 int csOffset = offset; 753 for ( int c = 0 ; c < TarHeader.CHKSUMLEN ; ++c ) 754 outbuf[ offset++ ] = (byte) ' '; 755 756 outbuf[ offset++ ] = this.header.linkFlag; 757 758 offset = TarHeader.getNameBytes 759 ( this.header.linkName, outbuf, offset, TarHeader.NAMELEN ); 760 761 if ( this.unixFormat ) 762 { 763 for ( int i = 0 ; i < TarHeader.MAGICLEN ; ++i ) 764 outbuf[ offset++ ] = 0; 765 } 766 else 767 { 768 offset = TarHeader.getNameBytes 769 ( this.header.magic, outbuf, offset, TarHeader.MAGICLEN ); 770 } 771 772 offset = TarHeader.getNameBytes 773 ( this.header.userName, outbuf, offset, TarHeader.UNAMELEN ); 774 775 offset = TarHeader.getNameBytes 776 ( this.header.groupName, outbuf, offset, TarHeader.GNAMELEN ); 777 778 offset = TarHeader.getOctalBytes 779 ( this.header.devMajor, outbuf, offset, TarHeader.DEVLEN ); 780 781 offset = TarHeader.getOctalBytes 782 ( this.header.devMinor, outbuf, offset, TarHeader.DEVLEN ); 783 784 for ( ; offset < outbuf.length ; ) 785 outbuf[ offset++ ] = 0; 786 787 long checkSum = this.computeCheckSum( outbuf ); 788 789 TarHeader.getCheckSumOctalBytes 790 ( checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN ); 791 } 792 793 /** 794 * Parse an entry's TarHeader information from a header buffer. 795 * 796 * Old unix-style code contributed by David Mehringer <dmehring@astro.uiuc.edu>. 797 * 798 * @param hdr The TarHeader to fill in from the buffer information. 799 * @param header The tar entry header buffer to get information from. 800 */ 801 public void parseTarHeader( TarHeader hdr, byte[] headerBuf )802 parseTarHeader( TarHeader hdr, byte[] headerBuf ) 803 throws InvalidHeaderException 804 { 805 int offset = 0; 806 807 // 808 // NOTE Recognize archive header format. 809 // 810 if ( headerBuf[257] == 0 811 && headerBuf[258] == 0 812 && headerBuf[259] == 0 813 && headerBuf[260] == 0 814 && headerBuf[261] == 0 ) 815 { 816 this.unixFormat = true; 817 this.ustarFormat = false; 818 this.gnuFormat = false; 819 } 820 else if ( headerBuf[257] == 'u' 821 && headerBuf[258] == 's' 822 && headerBuf[259] == 't' 823 && headerBuf[260] == 'a' 824 && headerBuf[261] == 'r' 825 && headerBuf[262] == 0 ) 826 { 827 this.ustarFormat = true; 828 this.gnuFormat = false; 829 this.unixFormat = false; 830 } 831 else if ( headerBuf[257] == 'u' 832 && headerBuf[258] == 's' 833 && headerBuf[259] == 't' 834 && headerBuf[260] == 'a' 835 && headerBuf[261] == 'r' 836 && headerBuf[262] != 0 837 && headerBuf[263] != 0 ) 838 { 839 // REVIEW 840 this.gnuFormat = true; 841 this.unixFormat = false; 842 this.ustarFormat = false; 843 } 844 else 845 { 846 StringBuffer buf = new StringBuffer( 128 ); 847 848 buf.append( "header magic is not 'ustar' or unix-style zeros, it is '" ); 849 buf.append( headerBuf[257] ); 850 buf.append( headerBuf[258] ); 851 buf.append( headerBuf[259] ); 852 buf.append( headerBuf[260] ); 853 buf.append( headerBuf[261] ); 854 buf.append( headerBuf[262] ); 855 buf.append( headerBuf[263] ); 856 buf.append( "', or (dec) " ); 857 buf.append( (int)headerBuf[257] ); 858 buf.append( ", " ); 859 buf.append( (int)headerBuf[258] ); 860 buf.append( ", " ); 861 buf.append( (int)headerBuf[259] ); 862 buf.append( ", " ); 863 buf.append( (int)headerBuf[260] ); 864 buf.append( ", " ); 865 buf.append( (int)headerBuf[261] ); 866 buf.append( ", " ); 867 buf.append( (int)headerBuf[262] ); 868 buf.append( ", " ); 869 buf.append( (int)headerBuf[263] ); 870 871 throw new InvalidHeaderException( buf.toString() ); 872 } 873 874 hdr.name = TarHeader.parseFileName( headerBuf ); 875 876 offset = TarHeader.NAMELEN; 877 878 hdr.mode = (int) 879 TarHeader.parseOctal( headerBuf, offset, TarHeader.MODELEN ); 880 881 offset += TarHeader.MODELEN; 882 883 hdr.userId = (int) 884 TarHeader.parseOctal( headerBuf, offset, TarHeader.UIDLEN ); 885 886 offset += TarHeader.UIDLEN; 887 888 hdr.groupId = (int) 889 TarHeader.parseOctal( headerBuf, offset, TarHeader.GIDLEN ); 890 891 offset += TarHeader.GIDLEN; 892 893 hdr.size = 894 TarHeader.parseOctal( headerBuf, offset, TarHeader.SIZELEN ); 895 896 offset += TarHeader.SIZELEN; 897 898 hdr.modTime = 899 TarHeader.parseOctal( headerBuf, offset, TarHeader.MODTIMELEN ); 900 901 offset += TarHeader.MODTIMELEN; 902 903 hdr.checkSum = (int) 904 TarHeader.parseOctal( headerBuf, offset, TarHeader.CHKSUMLEN ); 905 906 offset += TarHeader.CHKSUMLEN; 907 908 hdr.linkFlag = headerBuf[ offset++ ]; 909 910 hdr.linkName = 911 TarHeader.parseName( headerBuf, offset, TarHeader.NAMELEN ); 912 913 offset += TarHeader.NAMELEN; 914 915 if ( this.ustarFormat ) 916 { 917 hdr.magic = 918 TarHeader.parseName( headerBuf, offset, TarHeader.MAGICLEN ); 919 920 offset += TarHeader.MAGICLEN; 921 922 hdr.userName = 923 TarHeader.parseName( headerBuf, offset, TarHeader.UNAMELEN ); 924 925 offset += TarHeader.UNAMELEN; 926 927 hdr.groupName = 928 TarHeader.parseName( headerBuf, offset, TarHeader.GNAMELEN ); 929 930 offset += TarHeader.GNAMELEN; 931 932 hdr.devMajor = (int) 933 TarHeader.parseOctal( headerBuf, offset, TarHeader.DEVLEN ); 934 935 offset += TarHeader.DEVLEN; 936 937 hdr.devMinor = (int) 938 TarHeader.parseOctal( headerBuf, offset, TarHeader.DEVLEN ); 939 } 940 else 941 { 942 hdr.devMajor = 0; 943 hdr.devMinor = 0; 944 hdr.magic = new StringBuffer( "" ); 945 hdr.userName = new StringBuffer( "" ); 946 hdr.groupName = new StringBuffer( "" ); 947 } 948 } 949 950 /** 951 * Fill in a TarHeader given only the entry's name. 952 * 953 * @param hdr The TarHeader to fill in. 954 * @param name The tar entry name. 955 */ 956 public void nameTarHeader( TarHeader hdr, String name )957 nameTarHeader( TarHeader hdr, String name ) 958 { 959 boolean isDir = name.endsWith( "/" ); 960 961 this.gnuFormat = false; 962 this.ustarFormat = true; 963 this.unixFormat = false; 964 965 hdr.checkSum = 0; 966 hdr.devMajor = 0; 967 hdr.devMinor = 0; 968 969 hdr.name = new StringBuffer( name ); 970 hdr.mode = isDir ? 040755 : 0100644; 971 hdr.userId = 0; 972 hdr.groupId = 0; 973 hdr.size = 0; 974 hdr.checkSum = 0; 975 976 hdr.modTime = 977 (new java.util.Date()).getTime() / 1000; 978 979 hdr.linkFlag = 980 isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL; 981 982 hdr.linkName = new StringBuffer( "" ); 983 hdr.userName = new StringBuffer( "" ); 984 hdr.groupName = new StringBuffer( "" ); 985 986 hdr.devMajor = 0; 987 hdr.devMinor = 0; 988 } 989 990 public String toString()991 toString() 992 { 993 StringBuffer result = new StringBuffer( 128 ); 994 return result. 995 append( "[TarEntry name=" ). 996 append( this.getName() ). 997 append( ", isDir=" ). 998 append( this.isDirectory() ). 999 append( ", size=" ). 1000 append( this.getSize() ). 1001 append( ", userId=" ). 1002 append( this.getUserId() ). 1003 append( ", user=" ). 1004 append( this.getUserName() ). 1005 append( ", groupId=" ). 1006 append( this.getGroupId() ). 1007 append( ", group=" ). 1008 append( this.getGroupName() ). 1009 append( "]" ). 1010 toString(); 1011 } 1012 1013 } 1014 1015