001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.commons.compress.archivers.tar; 020 021import java.io.File; 022import java.io.IOException; 023import java.util.Date; 024import java.util.Locale; 025import java.util.Map; 026 027import org.apache.commons.compress.archivers.ArchiveEntry; 028import org.apache.commons.compress.archivers.zip.ZipEncoding; 029import org.apache.commons.compress.utils.ArchiveUtils; 030 031/** 032 * This class represents an entry in a Tar archive. It consists 033 * of the entry's header, as well as the entry's File. Entries 034 * can be instantiated in one of three ways, depending on how 035 * they are to be used. 036 * <p> 037 * TarEntries that are created from the header bytes read from 038 * an archive are instantiated with the TarEntry( byte[] ) 039 * constructor. These entries will be used when extracting from 040 * or listing the contents of an archive. These entries have their 041 * header filled in using the header bytes. They also set the File 042 * to null, since they reference an archive entry not a file. 043 * <p> 044 * TarEntries that are created from Files that are to be written 045 * into an archive are instantiated with the TarEntry( File ) 046 * constructor. These entries have their header filled in using 047 * the File's information. They also keep a reference to the File 048 * for convenience when writing entries. 049 * <p> 050 * Finally, TarEntries can be constructed from nothing but a name. 051 * This allows the programmer to construct the entry by hand, for 052 * instance when only an InputStream is available for writing to 053 * the archive, and the header information is constructed from 054 * other information. In this case the header fields are set to 055 * defaults and the File is set to null. 056 * 057 * <p> 058 * The C structure for a Tar Entry's header is: 059 * <pre> 060 * struct header { 061 * char name[100]; // TarConstants.NAMELEN - offset 0 062 * char mode[8]; // TarConstants.MODELEN - offset 100 063 * char uid[8]; // TarConstants.UIDLEN - offset 108 064 * char gid[8]; // TarConstants.GIDLEN - offset 116 065 * char size[12]; // TarConstants.SIZELEN - offset 124 066 * char mtime[12]; // TarConstants.MODTIMELEN - offset 136 067 * char chksum[8]; // TarConstants.CHKSUMLEN - offset 148 068 * char linkflag[1]; // - offset 156 069 * char linkname[100]; // TarConstants.NAMELEN - offset 157 070 * The following fields are only present in new-style POSIX tar archives: 071 * char magic[6]; // TarConstants.MAGICLEN - offset 257 072 * char version[2]; // TarConstants.VERSIONLEN - offset 263 073 * char uname[32]; // TarConstants.UNAMELEN - offset 265 074 * char gname[32]; // TarConstants.GNAMELEN - offset 297 075 * char devmajor[8]; // TarConstants.DEVLEN - offset 329 076 * char devminor[8]; // TarConstants.DEVLEN - offset 337 077 * char prefix[155]; // TarConstants.PREFIXLEN - offset 345 078 * // Used if "name" field is not long enough to hold the path 079 * char pad[12]; // NULs - offset 500 080 * } header; 081 * All unused bytes are set to null. 082 * New-style GNU tar files are slightly different from the above. 083 * For values of size larger than 077777777777L (11 7s) 084 * or uid and gid larger than 07777777L (7 7s) 085 * the sign bit of the first byte is set, and the rest of the 086 * field is the binary representation of the number. 087 * See TarUtils.parseOctalOrBinary. 088 * </pre> 089 * 090 * <p> 091 * The C structure for a old GNU Tar Entry's header is: 092 * <pre> 093 * struct oldgnu_header { 094 * char unused_pad1[345]; // TarConstants.PAD1LEN_GNU - offset 0 095 * char atime[12]; // TarConstants.ATIMELEN_GNU - offset 345 096 * char ctime[12]; // TarConstants.CTIMELEN_GNU - offset 357 097 * char offset[12]; // TarConstants.OFFSETLEN_GNU - offset 369 098 * char longnames[4]; // TarConstants.LONGNAMESLEN_GNU - offset 381 099 * char unused_pad2; // TarConstants.PAD2LEN_GNU - offset 385 100 * struct sparse sp[4]; // TarConstants.SPARSELEN_GNU - offset 386 101 * char isextended; // TarConstants.ISEXTENDEDLEN_GNU - offset 482 102 * char realsize[12]; // TarConstants.REALSIZELEN_GNU - offset 483 103 * char unused_pad[17]; // TarConstants.PAD3LEN_GNU - offset 495 104 * }; 105 * </pre> 106 * Whereas, "struct sparse" is: 107 * <pre> 108 * struct sparse { 109 * char offset[12]; // offset 0 110 * char numbytes[12]; // offset 12 111 * }; 112 * </pre> 113 * 114 * <p> 115 * The C structure for a xstar (Jörg Schilling star) Tar Entry's header is: 116 * <pre> 117 * struct star_header { 118 * char name[100]; // offset 0 119 * char mode[8]; // offset 100 120 * char uid[8]; // offset 108 121 * char gid[8]; // offset 116 122 * char size[12]; // offset 124 123 * char mtime[12]; // offset 136 124 * char chksum[8]; // offset 148 125 * char typeflag; // offset 156 126 * char linkname[100]; // offset 157 127 * char magic[6]; // offset 257 128 * char version[2]; // offset 263 129 * char uname[32]; // offset 265 130 * char gname[32]; // offset 297 131 * char devmajor[8]; // offset 329 132 * char devminor[8]; // offset 337 133 * char prefix[131]; // offset 345 134 * char atime[12]; // offset 476 135 * char ctime[12]; // offset 488 136 * char mfill[8]; // offset 500 137 * char xmagic[4]; // offset 508 "tar" 138 * }; 139 * </pre> 140 * <p>which is identical to new-style POSIX up to the first 130 bytes of the prefix.</p> 141 * 142 * @NotThreadSafe 143 */ 144 145public class TarArchiveEntry implements TarConstants, ArchiveEntry { 146 /** The entry's name. */ 147 private String name = ""; 148 149 /** Whether to enforce leading slashes on the name */ 150 private boolean preserveLeadingSlashes; 151 152 /** The entry's permission mode. */ 153 private int mode; 154 155 /** The entry's user id. */ 156 private long userId = 0; 157 158 /** The entry's group id. */ 159 private long groupId = 0; 160 161 /** The entry's size. */ 162 private long size = 0; 163 164 /** The entry's modification time. */ 165 private long modTime; 166 167 /** If the header checksum is reasonably correct. */ 168 private boolean checkSumOK; 169 170 /** The entry's link flag. */ 171 private byte linkFlag; 172 173 /** The entry's link name. */ 174 private String linkName = ""; 175 176 /** The entry's magic tag. */ 177 private String magic = MAGIC_POSIX; 178 /** The version of the format */ 179 private String version = VERSION_POSIX; 180 181 /** The entry's user name. */ 182 private String userName; 183 184 /** The entry's group name. */ 185 private String groupName = ""; 186 187 /** The entry's major device number. */ 188 private int devMajor = 0; 189 190 /** The entry's minor device number. */ 191 private int devMinor = 0; 192 193 /** If an extension sparse header follows. */ 194 private boolean isExtended; 195 196 /** The entry's real size in case of a sparse file. */ 197 private long realSize; 198 199 /** is this entry a GNU sparse entry using one of the PAX formats? */ 200 private boolean paxGNUSparse; 201 202 /** is this entry a star sparse entry using the PAX header? */ 203 private boolean starSparse; 204 205 /** The entry's file reference */ 206 private final File file; 207 208 /** Maximum length of a user's name in the tar file */ 209 public static final int MAX_NAMELEN = 31; 210 211 /** Default permissions bits for directories */ 212 public static final int DEFAULT_DIR_MODE = 040755; 213 214 /** Default permissions bits for files */ 215 public static final int DEFAULT_FILE_MODE = 0100644; 216 217 /** Convert millis to seconds */ 218 public static final int MILLIS_PER_SECOND = 1000; 219 220 /** 221 * Construct an empty entry and prepares the header values. 222 */ 223 private TarArchiveEntry() { 224 String user = System.getProperty("user.name", ""); 225 226 if (user.length() > MAX_NAMELEN) { 227 user = user.substring(0, MAX_NAMELEN); 228 } 229 230 this.userName = user; 231 this.file = null; 232 } 233 234 /** 235 * Construct an entry with only a name. This allows the programmer 236 * to construct the entry's header "by hand". File is set to null. 237 * 238 * @param name the entry name 239 */ 240 public TarArchiveEntry(String name) { 241 this(name, false); 242 } 243 244 /** 245 * Construct an entry with only a name. This allows the programmer 246 * to construct the entry's header "by hand". File is set to null. 247 * 248 * @param name the entry name 249 * @param preserveLeadingSlashes whether to allow leading slashes 250 * in the name. 251 * 252 * @since 1.1 253 */ 254 public TarArchiveEntry(String name, boolean preserveLeadingSlashes) { 255 this(); 256 257 this.preserveLeadingSlashes = preserveLeadingSlashes; 258 259 name = normalizeFileName(name, preserveLeadingSlashes); 260 boolean isDir = name.endsWith("/"); 261 262 this.name = name; 263 this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE; 264 this.linkFlag = isDir ? LF_DIR : LF_NORMAL; 265 this.modTime = new Date().getTime() / MILLIS_PER_SECOND; 266 this.userName = ""; 267 } 268 269 /** 270 * Construct an entry with a name and a link flag. 271 * 272 * @param name the entry name 273 * @param linkFlag the entry link flag. 274 */ 275 public TarArchiveEntry(String name, byte linkFlag) { 276 this(name, linkFlag, false); 277 } 278 279 /** 280 * Construct an entry with a name and a link flag. 281 * 282 * @param name the entry name 283 * @param linkFlag the entry link flag. 284 * @param preserveLeadingSlashes whether to allow leading slashes 285 * in the name. 286 * 287 * @since 1.5 288 */ 289 public TarArchiveEntry(String name, byte linkFlag, boolean preserveLeadingSlashes) { 290 this(name, preserveLeadingSlashes); 291 this.linkFlag = linkFlag; 292 if (linkFlag == LF_GNUTYPE_LONGNAME) { 293 magic = MAGIC_GNU; 294 version = VERSION_GNU_SPACE; 295 } 296 } 297 298 /** 299 * Construct an entry for a file. File is set to file, and the 300 * header is constructed from information from the file. 301 * The name is set from the normalized file path. 302 * 303 * @param file The file that the entry represents. 304 */ 305 public TarArchiveEntry(File file) { 306 this(file, file.getPath()); 307 } 308 309 /** 310 * Construct an entry for a file. File is set to file, and the 311 * header is constructed from information from the file. 312 * 313 * @param file The file that the entry represents. 314 * @param fileName the name to be used for the entry. 315 */ 316 public TarArchiveEntry(File file, String fileName) { 317 String normalizedName = normalizeFileName(fileName, false); 318 this.file = file; 319 320 if (file.isDirectory()) { 321 this.mode = DEFAULT_DIR_MODE; 322 this.linkFlag = LF_DIR; 323 324 int nameLength = normalizedName.length(); 325 if (nameLength == 0 || normalizedName.charAt(nameLength - 1) != '/') { 326 this.name = normalizedName + "/"; 327 } else { 328 this.name = normalizedName; 329 } 330 } else { 331 this.mode = DEFAULT_FILE_MODE; 332 this.linkFlag = LF_NORMAL; 333 this.size = file.length(); 334 this.name = normalizedName; 335 } 336 337 this.modTime = file.lastModified() / MILLIS_PER_SECOND; 338 this.userName = ""; 339 } 340 341 /** 342 * Construct an entry from an archive's header bytes. File is set 343 * to null. 344 * 345 * @param headerBuf The header bytes from a tar archive entry. 346 * @throws IllegalArgumentException if any of the numeric fields have an invalid format 347 */ 348 public TarArchiveEntry(byte[] headerBuf) { 349 this(); 350 parseTarHeader(headerBuf); 351 } 352 353 /** 354 * Construct an entry from an archive's header bytes. File is set 355 * to null. 356 * 357 * @param headerBuf The header bytes from a tar archive entry. 358 * @param encoding encoding to use for file names 359 * @since 1.4 360 * @throws IllegalArgumentException if any of the numeric fields have an invalid format 361 * @throws IOException on error 362 */ 363 public TarArchiveEntry(byte[] headerBuf, ZipEncoding encoding) 364 throws IOException { 365 this(); 366 parseTarHeader(headerBuf, encoding); 367 } 368 369 /** 370 * Determine if the two entries are equal. Equality is determined 371 * by the header names being equal. 372 * 373 * @param it Entry to be checked for equality. 374 * @return True if the entries are equal. 375 */ 376 public boolean equals(TarArchiveEntry it) { 377 return getName().equals(it.getName()); 378 } 379 380 /** 381 * Determine if the two entries are equal. Equality is determined 382 * by the header names being equal. 383 * 384 * @param it Entry to be checked for equality. 385 * @return True if the entries are equal. 386 */ 387 @Override 388 public boolean equals(Object it) { 389 if (it == null || getClass() != it.getClass()) { 390 return false; 391 } 392 return equals((TarArchiveEntry) it); 393 } 394 395 /** 396 * Hashcodes are based on entry names. 397 * 398 * @return the entry hashcode 399 */ 400 @Override 401 public int hashCode() { 402 return getName().hashCode(); 403 } 404 405 /** 406 * Determine if the given entry is a descendant of this entry. 407 * Descendancy is determined by the name of the descendant 408 * starting with this entry's name. 409 * 410 * @param desc Entry to be checked as a descendent of this. 411 * @return True if entry is a descendant of this. 412 */ 413 public boolean isDescendent(TarArchiveEntry desc) { 414 return desc.getName().startsWith(getName()); 415 } 416 417 /** 418 * Get this entry's name. 419 * 420 * @return This entry's name. 421 */ 422 public String getName() { 423 return name; 424 } 425 426 /** 427 * Set this entry's name. 428 * 429 * @param name This entry's new name. 430 */ 431 public void setName(String name) { 432 this.name = normalizeFileName(name, this.preserveLeadingSlashes); 433 } 434 435 /** 436 * Set the mode for this entry 437 * 438 * @param mode the mode for this entry 439 */ 440 public void setMode(int mode) { 441 this.mode = mode; 442 } 443 444 /** 445 * Get this entry's link name. 446 * 447 * @return This entry's link name. 448 */ 449 public String getLinkName() { 450 return linkName; 451 } 452 453 /** 454 * Set this entry's link name. 455 * 456 * @param link the link name to use. 457 * 458 * @since 1.1 459 */ 460 public void setLinkName(String link) { 461 this.linkName = link; 462 } 463 464 /** 465 * Get this entry's user id. 466 * 467 * @return This entry's user id. 468 * @deprecated use #getLongUserId instead as user ids can be 469 * bigger than {@link Integer#MAX_VALUE} 470 */ 471 @Deprecated 472 public int getUserId() { 473 return (int) (userId & 0xffffffff); 474 } 475 476 /** 477 * Set this entry's user id. 478 * 479 * @param userId This entry's new user id. 480 */ 481 public void setUserId(int userId) { 482 setUserId((long) userId); 483 } 484 485 /** 486 * Get this entry's user id. 487 * 488 * @return This entry's user id. 489 * @since 1.10 490 */ 491 public long getLongUserId() { 492 return userId; 493 } 494 495 /** 496 * Set this entry's user id. 497 * 498 * @param userId This entry's new user id. 499 * @since 1.10 500 */ 501 public void setUserId(long userId) { 502 this.userId = userId; 503 } 504 505 /** 506 * Get this entry's group id. 507 * 508 * @return This entry's group id. 509 * @deprecated use #getLongGroupId instead as group ids can be 510 * bigger than {@link Integer#MAX_VALUE} 511 */ 512 @Deprecated 513 public int getGroupId() { 514 return (int) (groupId & 0xffffffff); 515 } 516 517 /** 518 * Set this entry's group id. 519 * 520 * @param groupId This entry's new group id. 521 */ 522 public void setGroupId(int groupId) { 523 setGroupId((long) groupId); 524 } 525 526 /** 527 * Get this entry's group id. 528 * 529 * @since 1.10 530 * @return This entry's group id. 531 */ 532 public long getLongGroupId() { 533 return groupId; 534 } 535 536 /** 537 * Set this entry's group id. 538 * 539 * @since 1.10 540 * @param groupId This entry's new group id. 541 */ 542 public void setGroupId(long groupId) { 543 this.groupId = groupId; 544 } 545 546 /** 547 * Get this entry's user name. 548 * 549 * @return This entry's user name. 550 */ 551 public String getUserName() { 552 return userName; 553 } 554 555 /** 556 * Set this entry's user name. 557 * 558 * @param userName This entry's new user name. 559 */ 560 public void setUserName(String userName) { 561 this.userName = userName; 562 } 563 564 /** 565 * Get this entry's group name. 566 * 567 * @return This entry's group name. 568 */ 569 public String getGroupName() { 570 return groupName; 571 } 572 573 /** 574 * Set this entry's group name. 575 * 576 * @param groupName This entry's new group name. 577 */ 578 public void setGroupName(String groupName) { 579 this.groupName = groupName; 580 } 581 582 /** 583 * Convenience method to set this entry's group and user ids. 584 * 585 * @param userId This entry's new user id. 586 * @param groupId This entry's new group id. 587 */ 588 public void setIds(int userId, int groupId) { 589 setUserId(userId); 590 setGroupId(groupId); 591 } 592 593 /** 594 * Convenience method to set this entry's group and user names. 595 * 596 * @param userName This entry's new user name. 597 * @param groupName This entry's new group name. 598 */ 599 public void setNames(String userName, String groupName) { 600 setUserName(userName); 601 setGroupName(groupName); 602 } 603 604 /** 605 * Set this entry's modification time. The parameter passed 606 * to this method is in "Java time". 607 * 608 * @param time This entry's new modification time. 609 */ 610 public void setModTime(long time) { 611 modTime = time / MILLIS_PER_SECOND; 612 } 613 614 /** 615 * Set this entry's modification time. 616 * 617 * @param time This entry's new modification time. 618 */ 619 public void setModTime(Date time) { 620 modTime = time.getTime() / MILLIS_PER_SECOND; 621 } 622 623 /** 624 * Set this entry's modification time. 625 * 626 * @return time This entry's new modification time. 627 */ 628 public Date getModTime() { 629 return new Date(modTime * MILLIS_PER_SECOND); 630 } 631 632 public Date getLastModifiedDate() { 633 return getModTime(); 634 } 635 636 /** 637 * Get this entry's checksum status. 638 * 639 * @return if the header checksum is reasonably correct 640 * @see TarUtils#verifyCheckSum(byte[]) 641 * @since 1.5 642 */ 643 public boolean isCheckSumOK() { 644 return checkSumOK; 645 } 646 647 /** 648 * Get this entry's file. 649 * 650 * @return This entry's file. 651 */ 652 public File getFile() { 653 return file; 654 } 655 656 /** 657 * Get this entry's mode. 658 * 659 * @return This entry's mode. 660 */ 661 public int getMode() { 662 return mode; 663 } 664 665 /** 666 * Get this entry's file size. 667 * 668 * @return This entry's file size. 669 */ 670 public long getSize() { 671 return size; 672 } 673 674 /** 675 * Set this entry's file size. 676 * 677 * @param size This entry's new file size. 678 * @throws IllegalArgumentException if the size is < 0. 679 */ 680 public void setSize(long size) { 681 if (size < 0){ 682 throw new IllegalArgumentException("Size is out of range: "+size); 683 } 684 this.size = size; 685 } 686 687 /** 688 * Get this entry's major device number. 689 * 690 * @return This entry's major device number. 691 * @since 1.4 692 */ 693 public int getDevMajor() { 694 return devMajor; 695 } 696 697 /** 698 * Set this entry's major device number. 699 * 700 * @param devNo This entry's major device number. 701 * @throws IllegalArgumentException if the devNo is < 0. 702 * @since 1.4 703 */ 704 public void setDevMajor(int devNo) { 705 if (devNo < 0){ 706 throw new IllegalArgumentException("Major device number is out of " 707 + "range: " + devNo); 708 } 709 this.devMajor = devNo; 710 } 711 712 /** 713 * Get this entry's minor device number. 714 * 715 * @return This entry's minor device number. 716 * @since 1.4 717 */ 718 public int getDevMinor() { 719 return devMinor; 720 } 721 722 /** 723 * Set this entry's minor device number. 724 * 725 * @param devNo This entry's minor device number. 726 * @throws IllegalArgumentException if the devNo is < 0. 727 * @since 1.4 728 */ 729 public void setDevMinor(int devNo) { 730 if (devNo < 0){ 731 throw new IllegalArgumentException("Minor device number is out of " 732 + "range: " + devNo); 733 } 734 this.devMinor = devNo; 735 } 736 737 /** 738 * Indicates in case of an oldgnu sparse file if an extension 739 * sparse header follows. 740 * 741 * @return true if an extension oldgnu sparse header follows. 742 */ 743 public boolean isExtended() { 744 return isExtended; 745 } 746 747 /** 748 * Get this entry's real file size in case of a sparse file. 749 * 750 * @return This entry's real file size. 751 */ 752 public long getRealSize() { 753 return realSize; 754 } 755 756 /** 757 * Indicate if this entry is a GNU sparse block. 758 * 759 * @return true if this is a sparse extension provided by GNU tar 760 */ 761 public boolean isGNUSparse() { 762 return isOldGNUSparse() || isPaxGNUSparse(); 763 } 764 765 /** 766 * Indicate if this entry is a GNU or star sparse block using the 767 * oldgnu format. 768 * 769 * @return true if this is a sparse extension provided by GNU tar or star 770 * @since 1.11 771 */ 772 public boolean isOldGNUSparse() { 773 return linkFlag == LF_GNUTYPE_SPARSE; 774 } 775 776 /** 777 * Indicate if this entry is a GNU sparse block using one of the 778 * PAX formats. 779 * 780 * @return true if this is a sparse extension provided by GNU tar 781 * @since 1.11 782 */ 783 public boolean isPaxGNUSparse() { 784 return paxGNUSparse; 785 } 786 787 /** 788 * Indicate if this entry is a star sparse block using PAX headers. 789 * 790 * @return true if this is a sparse extension provided by star 791 * @since 1.11 792 */ 793 public boolean isStarSparse() { 794 return starSparse; 795 } 796 797 /** 798 * Indicate if this entry is a GNU long linkname block 799 * 800 * @return true if this is a long name extension provided by GNU tar 801 */ 802 public boolean isGNULongLinkEntry() { 803 return linkFlag == LF_GNUTYPE_LONGLINK; 804 } 805 806 /** 807 * Indicate if this entry is a GNU long name block 808 * 809 * @return true if this is a long name extension provided by GNU tar 810 */ 811 public boolean isGNULongNameEntry() { 812 return linkFlag == LF_GNUTYPE_LONGNAME; 813 } 814 815 /** 816 * Check if this is a Pax header. 817 * 818 * @return {@code true} if this is a Pax header. 819 * 820 * @since 1.1 821 * 822 */ 823 public boolean isPaxHeader(){ 824 return linkFlag == LF_PAX_EXTENDED_HEADER_LC 825 || linkFlag == LF_PAX_EXTENDED_HEADER_UC; 826 } 827 828 /** 829 * Check if this is a Pax header. 830 * 831 * @return {@code true} if this is a Pax header. 832 * 833 * @since 1.1 834 */ 835 public boolean isGlobalPaxHeader(){ 836 return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER; 837 } 838 839 /** 840 * Return whether or not this entry represents a directory. 841 * 842 * @return True if this entry is a directory. 843 */ 844 public boolean isDirectory() { 845 if (file != null) { 846 return file.isDirectory(); 847 } 848 849 if (linkFlag == LF_DIR) { 850 return true; 851 } 852 853 if (getName().endsWith("/")) { 854 return true; 855 } 856 857 return false; 858 } 859 860 /** 861 * Check if this is a "normal file" 862 * 863 * @since 1.2 864 * @return whether this is a "normal file" 865 */ 866 public boolean isFile() { 867 if (file != null) { 868 return file.isFile(); 869 } 870 if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) { 871 return true; 872 } 873 return !getName().endsWith("/"); 874 } 875 876 /** 877 * Check if this is a symbolic link entry. 878 * 879 * @since 1.2 880 * @return whether this is a symbolic link 881 */ 882 public boolean isSymbolicLink() { 883 return linkFlag == LF_SYMLINK; 884 } 885 886 /** 887 * Check if this is a link entry. 888 * 889 * @since 1.2 890 * @return whether this is a link entry 891 */ 892 public boolean isLink() { 893 return linkFlag == LF_LINK; 894 } 895 896 /** 897 * Check if this is a character device entry. 898 * 899 * @since 1.2 900 * @return whether this is a character device 901 */ 902 public boolean isCharacterDevice() { 903 return linkFlag == LF_CHR; 904 } 905 906 /** 907 * Check if this is a block device entry. 908 * 909 * @since 1.2 910 * @return whether this is a block device 911 */ 912 public boolean isBlockDevice() { 913 return linkFlag == LF_BLK; 914 } 915 916 /** 917 * Check if this is a FIFO (pipe) entry. 918 * 919 * @since 1.2 920 * @return whether this is a FIFO entry 921 */ 922 public boolean isFIFO() { 923 return linkFlag == LF_FIFO; 924 } 925 926 /** 927 * Check whether this is a sparse entry. 928 * 929 * @return whether this is a sparse entry 930 * @since 1.11 931 */ 932 public boolean isSparse() { 933 return isGNUSparse() || isStarSparse(); 934 } 935 936 /** 937 * If this entry represents a file, and the file is a directory, return 938 * an array of TarEntries for this entry's children. 939 * 940 * @return An array of TarEntry's for this entry's children. 941 */ 942 public TarArchiveEntry[] getDirectoryEntries() { 943 if (file == null || !file.isDirectory()) { 944 return new TarArchiveEntry[0]; 945 } 946 947 String[] list = file.list(); 948 TarArchiveEntry[] result = new TarArchiveEntry[list == null ? 0 : list.length]; 949 950 for (int i = 0; i < result.length; ++i) { 951 result[i] = new TarArchiveEntry(new File(file, list[i])); 952 } 953 954 return result; 955 } 956 957 /** 958 * Write an entry's header information to a header buffer. 959 * 960 * <p>This method does not use the star/GNU tar/BSD tar extensions.</p> 961 * 962 * @param outbuf The tar entry header buffer to fill in. 963 */ 964 public void writeEntryHeader(byte[] outbuf) { 965 try { 966 writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false); 967 } catch (IOException ex) { 968 try { 969 writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false); 970 } catch (IOException ex2) { 971 // impossible 972 throw new RuntimeException(ex2); 973 } 974 } 975 } 976 977 /** 978 * Write an entry's header information to a header buffer. 979 * 980 * @param outbuf The tar entry header buffer to fill in. 981 * @param encoding encoding to use when writing the file name. 982 * @param starMode whether to use the star/GNU tar/BSD tar 983 * extension for numeric fields if their value doesn't fit in the 984 * maximum size of standard tar archives 985 * @since 1.4 986 * @throws IOException on error 987 */ 988 public void writeEntryHeader(byte[] outbuf, ZipEncoding encoding, 989 boolean starMode) throws IOException { 990 int offset = 0; 991 992 offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN, 993 encoding); 994 offset = writeEntryHeaderField(mode, outbuf, offset, MODELEN, starMode); 995 offset = writeEntryHeaderField(userId, outbuf, offset, UIDLEN, 996 starMode); 997 offset = writeEntryHeaderField(groupId, outbuf, offset, GIDLEN, 998 starMode); 999 offset = writeEntryHeaderField(size, outbuf, offset, SIZELEN, starMode); 1000 offset = writeEntryHeaderField(modTime, outbuf, offset, MODTIMELEN, 1001 starMode); 1002 1003 int csOffset = offset; 1004 1005 for (int c = 0; c < CHKSUMLEN; ++c) { 1006 outbuf[offset++] = (byte) ' '; 1007 } 1008 1009 outbuf[offset++] = linkFlag; 1010 offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN, 1011 encoding); 1012 offset = TarUtils.formatNameBytes(magic, outbuf, offset, MAGICLEN); 1013 offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN); 1014 offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN, 1015 encoding); 1016 offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN, 1017 encoding); 1018 offset = writeEntryHeaderField(devMajor, outbuf, offset, DEVLEN, 1019 starMode); 1020 offset = writeEntryHeaderField(devMinor, outbuf, offset, DEVLEN, 1021 starMode); 1022 1023 while (offset < outbuf.length) { 1024 outbuf[offset++] = 0; 1025 } 1026 1027 long chk = TarUtils.computeCheckSum(outbuf); 1028 1029 TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); 1030 } 1031 1032 private int writeEntryHeaderField(long value, byte[] outbuf, int offset, 1033 int length, boolean starMode) { 1034 if (!starMode && (value < 0 1035 || value >= 1l << 3 * (length - 1))) { 1036 // value doesn't fit into field when written as octal 1037 // number, will be written to PAX header or causes an 1038 // error 1039 return TarUtils.formatLongOctalBytes(0, outbuf, offset, length); 1040 } 1041 return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset, 1042 length); 1043 } 1044 1045 /** 1046 * Parse an entry's header information from a header buffer. 1047 * 1048 * @param header The tar entry header buffer to get information from. 1049 * @throws IllegalArgumentException if any of the numeric fields have an invalid format 1050 */ 1051 public void parseTarHeader(byte[] header) { 1052 try { 1053 parseTarHeader(header, TarUtils.DEFAULT_ENCODING); 1054 } catch (IOException ex) { 1055 try { 1056 parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true); 1057 } catch (IOException ex2) { 1058 // not really possible 1059 throw new RuntimeException(ex2); 1060 } 1061 } 1062 } 1063 1064 /** 1065 * Parse an entry's header information from a header buffer. 1066 * 1067 * @param header The tar entry header buffer to get information from. 1068 * @param encoding encoding to use for file names 1069 * @since 1.4 1070 * @throws IllegalArgumentException if any of the numeric fields 1071 * have an invalid format 1072 * @throws IOException on error 1073 */ 1074 public void parseTarHeader(byte[] header, ZipEncoding encoding) 1075 throws IOException { 1076 parseTarHeader(header, encoding, false); 1077 } 1078 1079 private void parseTarHeader(byte[] header, ZipEncoding encoding, 1080 final boolean oldStyle) 1081 throws IOException { 1082 int offset = 0; 1083 1084 name = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) 1085 : TarUtils.parseName(header, offset, NAMELEN, encoding); 1086 offset += NAMELEN; 1087 mode = (int) TarUtils.parseOctalOrBinary(header, offset, MODELEN); 1088 offset += MODELEN; 1089 userId = (int) TarUtils.parseOctalOrBinary(header, offset, UIDLEN); 1090 offset += UIDLEN; 1091 groupId = (int) TarUtils.parseOctalOrBinary(header, offset, GIDLEN); 1092 offset += GIDLEN; 1093 size = TarUtils.parseOctalOrBinary(header, offset, SIZELEN); 1094 offset += SIZELEN; 1095 modTime = TarUtils.parseOctalOrBinary(header, offset, MODTIMELEN); 1096 offset += MODTIMELEN; 1097 checkSumOK = TarUtils.verifyCheckSum(header); 1098 offset += CHKSUMLEN; 1099 linkFlag = header[offset++]; 1100 linkName = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) 1101 : TarUtils.parseName(header, offset, NAMELEN, encoding); 1102 offset += NAMELEN; 1103 magic = TarUtils.parseName(header, offset, MAGICLEN); 1104 offset += MAGICLEN; 1105 version = TarUtils.parseName(header, offset, VERSIONLEN); 1106 offset += VERSIONLEN; 1107 userName = oldStyle ? TarUtils.parseName(header, offset, UNAMELEN) 1108 : TarUtils.parseName(header, offset, UNAMELEN, encoding); 1109 offset += UNAMELEN; 1110 groupName = oldStyle ? TarUtils.parseName(header, offset, GNAMELEN) 1111 : TarUtils.parseName(header, offset, GNAMELEN, encoding); 1112 offset += GNAMELEN; 1113 devMajor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN); 1114 offset += DEVLEN; 1115 devMinor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN); 1116 offset += DEVLEN; 1117 1118 int type = evaluateType(header); 1119 switch (type) { 1120 case FORMAT_OLDGNU: { 1121 offset += ATIMELEN_GNU; 1122 offset += CTIMELEN_GNU; 1123 offset += OFFSETLEN_GNU; 1124 offset += LONGNAMESLEN_GNU; 1125 offset += PAD2LEN_GNU; 1126 offset += SPARSELEN_GNU; 1127 isExtended = TarUtils.parseBoolean(header, offset); 1128 offset += ISEXTENDEDLEN_GNU; 1129 realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU); 1130 offset += REALSIZELEN_GNU; 1131 break; 1132 } 1133 case FORMAT_XSTAR: { 1134 String xstarPrefix = oldStyle 1135 ? TarUtils.parseName(header, offset, PREFIXLEN_XSTAR) 1136 : TarUtils.parseName(header, offset, PREFIXLEN_XSTAR, encoding); 1137 if (xstarPrefix.length() > 0) { 1138 name = xstarPrefix + "/" + name; 1139 } 1140 break; 1141 } 1142 case FORMAT_POSIX: 1143 default: { 1144 String prefix = oldStyle 1145 ? TarUtils.parseName(header, offset, PREFIXLEN) 1146 : TarUtils.parseName(header, offset, PREFIXLEN, encoding); 1147 // SunOS tar -E does not add / to directory names, so fix 1148 // up to be consistent 1149 if (isDirectory() && !name.endsWith("/")){ 1150 name = name + "/"; 1151 } 1152 if (prefix.length() > 0){ 1153 name = prefix + "/" + name; 1154 } 1155 } 1156 } 1157 } 1158 1159 /** 1160 * Strips Windows' drive letter as well as any leading slashes, 1161 * turns path separators into forward slahes. 1162 */ 1163 private static String normalizeFileName(String fileName, 1164 boolean preserveLeadingSlashes) { 1165 String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); 1166 1167 if (osname != null) { 1168 1169 // Strip off drive letters! 1170 // REVIEW Would a better check be "(File.separator == '\')"? 1171 1172 if (osname.startsWith("windows")) { 1173 if (fileName.length() > 2) { 1174 char ch1 = fileName.charAt(0); 1175 char ch2 = fileName.charAt(1); 1176 1177 if (ch2 == ':' 1178 && (ch1 >= 'a' && ch1 <= 'z' 1179 || ch1 >= 'A' && ch1 <= 'Z')) { 1180 fileName = fileName.substring(2); 1181 } 1182 } 1183 } else if (osname.contains("netware")) { 1184 int colon = fileName.indexOf(':'); 1185 if (colon != -1) { 1186 fileName = fileName.substring(colon + 1); 1187 } 1188 } 1189 } 1190 1191 fileName = fileName.replace(File.separatorChar, '/'); 1192 1193 // No absolute pathnames 1194 // Windows (and Posix?) paths can start with "\\NetworkDrive\", 1195 // so we loop on starting /'s. 1196 while (!preserveLeadingSlashes && fileName.startsWith("/")) { 1197 fileName = fileName.substring(1); 1198 } 1199 return fileName; 1200 } 1201 1202 /** 1203 * Evaluate an entry's header format from a header buffer. 1204 * 1205 * @param header The tar entry header buffer to evaluate the format for. 1206 * @return format type 1207 */ 1208 private int evaluateType(byte[] header) { 1209 if (ArchiveUtils.matchAsciiBuffer(MAGIC_GNU, header, MAGIC_OFFSET, MAGICLEN)) { 1210 return FORMAT_OLDGNU; 1211 } 1212 if (ArchiveUtils.matchAsciiBuffer(MAGIC_POSIX, header, MAGIC_OFFSET, MAGICLEN)) { 1213 if (ArchiveUtils.matchAsciiBuffer(MAGIC_XSTAR, header, XSTAR_MAGIC_OFFSET, 1214 XSTAR_MAGIC_LEN)) { 1215 return FORMAT_XSTAR; 1216 } 1217 return FORMAT_POSIX; 1218 } 1219 return 0; 1220 } 1221 1222 void fillGNUSparse0xData(Map<String, String> headers) { 1223 paxGNUSparse = true; 1224 realSize = Integer.parseInt(headers.get("GNU.sparse.size")); 1225 if (headers.containsKey("GNU.sparse.name")) { 1226 // version 0.1 1227 name = headers.get("GNU.sparse.name"); 1228 } 1229 } 1230 1231 void fillGNUSparse1xData(Map<String, String> headers) { 1232 paxGNUSparse = true; 1233 realSize = Integer.parseInt(headers.get("GNU.sparse.realsize")); 1234 name = headers.get("GNU.sparse.name"); 1235 } 1236 1237 void fillStarSparseData(Map<String, String> headers) { 1238 starSparse = true; 1239 if (headers.containsKey("SCHILY.realsize")) { 1240 realSize = Long.parseLong(headers.get("SCHILY.realsize")); 1241 } 1242 } 1243} 1244