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