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 it != null && 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 * <p>This method is only useful for entries created from a {@code 655 * File} but not for entries read from an archive.</p> 656 * 657 * @return This entry's file. 658 */ 659 public File getFile() { 660 return file; 661 } 662 663 /** 664 * Get this entry's mode. 665 * 666 * @return This entry's mode. 667 */ 668 public int getMode() { 669 return mode; 670 } 671 672 /** 673 * Get this entry's file size. 674 * 675 * @return This entry's file size. 676 */ 677 @Override 678 public long getSize() { 679 return size; 680 } 681 682 /** 683 * Set this entry's file size. 684 * 685 * @param size This entry's new file size. 686 * @throws IllegalArgumentException if the size is < 0. 687 */ 688 public void setSize(final long size) { 689 if (size < 0){ 690 throw new IllegalArgumentException("Size is out of range: "+size); 691 } 692 this.size = size; 693 } 694 695 /** 696 * Get this entry's major device number. 697 * 698 * @return This entry's major device number. 699 * @since 1.4 700 */ 701 public int getDevMajor() { 702 return devMajor; 703 } 704 705 /** 706 * Set this entry's major device number. 707 * 708 * @param devNo This entry's major device number. 709 * @throws IllegalArgumentException if the devNo is < 0. 710 * @since 1.4 711 */ 712 public void setDevMajor(final int devNo) { 713 if (devNo < 0){ 714 throw new IllegalArgumentException("Major device number is out of " 715 + "range: " + devNo); 716 } 717 this.devMajor = devNo; 718 } 719 720 /** 721 * Get this entry's minor device number. 722 * 723 * @return This entry's minor device number. 724 * @since 1.4 725 */ 726 public int getDevMinor() { 727 return devMinor; 728 } 729 730 /** 731 * Set this entry's minor device number. 732 * 733 * @param devNo This entry's minor device number. 734 * @throws IllegalArgumentException if the devNo is < 0. 735 * @since 1.4 736 */ 737 public void setDevMinor(final int devNo) { 738 if (devNo < 0){ 739 throw new IllegalArgumentException("Minor device number is out of " 740 + "range: " + devNo); 741 } 742 this.devMinor = devNo; 743 } 744 745 /** 746 * Indicates in case of an oldgnu sparse file if an extension 747 * sparse header follows. 748 * 749 * @return true if an extension oldgnu sparse header follows. 750 */ 751 public boolean isExtended() { 752 return isExtended; 753 } 754 755 /** 756 * Get this entry's real file size in case of a sparse file. 757 * 758 * @return This entry's real file size. 759 */ 760 public long getRealSize() { 761 return realSize; 762 } 763 764 /** 765 * Indicate if this entry is a GNU sparse block. 766 * 767 * @return true if this is a sparse extension provided by GNU tar 768 */ 769 public boolean isGNUSparse() { 770 return isOldGNUSparse() || isPaxGNUSparse(); 771 } 772 773 /** 774 * Indicate if this entry is a GNU or star sparse block using the 775 * oldgnu format. 776 * 777 * @return true if this is a sparse extension provided by GNU tar or star 778 * @since 1.11 779 */ 780 public boolean isOldGNUSparse() { 781 return linkFlag == LF_GNUTYPE_SPARSE; 782 } 783 784 /** 785 * Indicate if this entry is a GNU sparse block using one of the 786 * PAX formats. 787 * 788 * @return true if this is a sparse extension provided by GNU tar 789 * @since 1.11 790 */ 791 public boolean isPaxGNUSparse() { 792 return paxGNUSparse; 793 } 794 795 /** 796 * Indicate if this entry is a star sparse block using PAX headers. 797 * 798 * @return true if this is a sparse extension provided by star 799 * @since 1.11 800 */ 801 public boolean isStarSparse() { 802 return starSparse; 803 } 804 805 /** 806 * Indicate if this entry is a GNU long linkname block 807 * 808 * @return true if this is a long name extension provided by GNU tar 809 */ 810 public boolean isGNULongLinkEntry() { 811 return linkFlag == LF_GNUTYPE_LONGLINK; 812 } 813 814 /** 815 * Indicate if this entry is a GNU long name block 816 * 817 * @return true if this is a long name extension provided by GNU tar 818 */ 819 public boolean isGNULongNameEntry() { 820 return linkFlag == LF_GNUTYPE_LONGNAME; 821 } 822 823 /** 824 * Check if this is a Pax header. 825 * 826 * @return {@code true} if this is a Pax header. 827 * 828 * @since 1.1 829 * 830 */ 831 public boolean isPaxHeader() { 832 return linkFlag == LF_PAX_EXTENDED_HEADER_LC 833 || linkFlag == LF_PAX_EXTENDED_HEADER_UC; 834 } 835 836 /** 837 * Check if this is a Pax header. 838 * 839 * @return {@code true} if this is a Pax header. 840 * 841 * @since 1.1 842 */ 843 public boolean isGlobalPaxHeader() { 844 return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER; 845 } 846 847 /** 848 * Return whether or not this entry represents a directory. 849 * 850 * @return True if this entry is a directory. 851 */ 852 @Override 853 public boolean isDirectory() { 854 if (file != null) { 855 return file.isDirectory(); 856 } 857 858 if (linkFlag == LF_DIR) { 859 return true; 860 } 861 862 if (!isPaxHeader() && !isGlobalPaxHeader() && getName().endsWith("/")) { 863 return true; 864 } 865 866 return false; 867 } 868 869 /** 870 * Check if this is a "normal file" 871 * 872 * @since 1.2 873 * @return whether this is a "normal file" 874 */ 875 public boolean isFile() { 876 if (file != null) { 877 return file.isFile(); 878 } 879 if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) { 880 return true; 881 } 882 return !getName().endsWith("/"); 883 } 884 885 /** 886 * Check if this is a symbolic link entry. 887 * 888 * @since 1.2 889 * @return whether this is a symbolic link 890 */ 891 public boolean isSymbolicLink() { 892 return linkFlag == LF_SYMLINK; 893 } 894 895 /** 896 * Check if this is a link entry. 897 * 898 * @since 1.2 899 * @return whether this is a link entry 900 */ 901 public boolean isLink() { 902 return linkFlag == LF_LINK; 903 } 904 905 /** 906 * Check if this is a character device entry. 907 * 908 * @since 1.2 909 * @return whether this is a character device 910 */ 911 public boolean isCharacterDevice() { 912 return linkFlag == LF_CHR; 913 } 914 915 /** 916 * Check if this is a block device entry. 917 * 918 * @since 1.2 919 * @return whether this is a block device 920 */ 921 public boolean isBlockDevice() { 922 return linkFlag == LF_BLK; 923 } 924 925 /** 926 * Check if this is a FIFO (pipe) entry. 927 * 928 * @since 1.2 929 * @return whether this is a FIFO entry 930 */ 931 public boolean isFIFO() { 932 return linkFlag == LF_FIFO; 933 } 934 935 /** 936 * Check whether this is a sparse entry. 937 * 938 * @return whether this is a sparse entry 939 * @since 1.11 940 */ 941 public boolean isSparse() { 942 return isGNUSparse() || isStarSparse(); 943 } 944 945 /** 946 * If this entry represents a file, and the file is a directory, return 947 * an array of TarEntries for this entry's children. 948 * 949 * <p>This method is only useful for entries created from a {@code 950 * File} but not for entries read from an archive.</p> 951 * 952 * @return An array of TarEntry's for this entry's children. 953 */ 954 public TarArchiveEntry[] getDirectoryEntries() { 955 if (file == null || !file.isDirectory()) { 956 return EMPTY_TAR_ARCHIVE_ENTRIES; 957 } 958 959 final String[] list = file.list(); 960 if (list == null) { 961 return EMPTY_TAR_ARCHIVE_ENTRIES; 962 } 963 final TarArchiveEntry[] result = new TarArchiveEntry[list.length]; 964 965 for (int i = 0; i < result.length; ++i) { 966 result[i] = new TarArchiveEntry(new File(file, list[i])); 967 } 968 969 return result; 970 } 971 972 /** 973 * Write an entry's header information to a header buffer. 974 * 975 * <p>This method does not use the star/GNU tar/BSD tar extensions.</p> 976 * 977 * @param outbuf The tar entry header buffer to fill in. 978 */ 979 public void writeEntryHeader(final byte[] outbuf) { 980 try { 981 writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false); 982 } catch (final IOException ex) { 983 try { 984 writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false); 985 } catch (final IOException ex2) { 986 // impossible 987 throw new RuntimeException(ex2); //NOSONAR 988 } 989 } 990 } 991 992 /** 993 * Write an entry's header information to a header buffer. 994 * 995 * @param outbuf The tar entry header buffer to fill in. 996 * @param encoding encoding to use when writing the file name. 997 * @param starMode whether to use the star/GNU tar/BSD tar 998 * extension for numeric fields if their value doesn't fit in the 999 * maximum size of standard tar archives 1000 * @since 1.4 1001 * @throws IOException on error 1002 */ 1003 public void writeEntryHeader(final byte[] outbuf, final ZipEncoding encoding, 1004 final boolean starMode) throws IOException { 1005 int offset = 0; 1006 1007 offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN, 1008 encoding); 1009 offset = writeEntryHeaderField(mode, outbuf, offset, MODELEN, starMode); 1010 offset = writeEntryHeaderField(userId, outbuf, offset, UIDLEN, 1011 starMode); 1012 offset = writeEntryHeaderField(groupId, outbuf, offset, GIDLEN, 1013 starMode); 1014 offset = writeEntryHeaderField(size, outbuf, offset, SIZELEN, starMode); 1015 offset = writeEntryHeaderField(modTime, outbuf, offset, MODTIMELEN, 1016 starMode); 1017 1018 final int csOffset = offset; 1019 1020 for (int c = 0; c < CHKSUMLEN; ++c) { 1021 outbuf[offset++] = (byte) ' '; 1022 } 1023 1024 outbuf[offset++] = linkFlag; 1025 offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN, 1026 encoding); 1027 offset = TarUtils.formatNameBytes(magic, outbuf, offset, MAGICLEN); 1028 offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN); 1029 offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN, 1030 encoding); 1031 offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN, 1032 encoding); 1033 offset = writeEntryHeaderField(devMajor, outbuf, offset, DEVLEN, 1034 starMode); 1035 offset = writeEntryHeaderField(devMinor, outbuf, offset, DEVLEN, 1036 starMode); 1037 1038 while (offset < outbuf.length) { 1039 outbuf[offset++] = 0; 1040 } 1041 1042 final long chk = TarUtils.computeCheckSum(outbuf); 1043 1044 TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); 1045 } 1046 1047 private int writeEntryHeaderField(final long value, final byte[] outbuf, final int offset, 1048 final int length, final boolean starMode) { 1049 if (!starMode && (value < 0 1050 || value >= 1l << 3 * (length - 1))) { 1051 // value doesn't fit into field when written as octal 1052 // number, will be written to PAX header or causes an 1053 // error 1054 return TarUtils.formatLongOctalBytes(0, outbuf, offset, length); 1055 } 1056 return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset, 1057 length); 1058 } 1059 1060 /** 1061 * Parse an entry's header information from a header buffer. 1062 * 1063 * @param header The tar entry header buffer to get information from. 1064 * @throws IllegalArgumentException if any of the numeric fields have an invalid format 1065 */ 1066 public void parseTarHeader(final byte[] header) { 1067 try { 1068 parseTarHeader(header, TarUtils.DEFAULT_ENCODING); 1069 } catch (final IOException ex) { 1070 try { 1071 parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true); 1072 } catch (final IOException ex2) { 1073 // not really possible 1074 throw new RuntimeException(ex2); //NOSONAR 1075 } 1076 } 1077 } 1078 1079 /** 1080 * Parse an entry's header information from a header buffer. 1081 * 1082 * @param header The tar entry header buffer to get information from. 1083 * @param encoding encoding to use for file names 1084 * @since 1.4 1085 * @throws IllegalArgumentException if any of the numeric fields 1086 * have an invalid format 1087 * @throws IOException on error 1088 */ 1089 public void parseTarHeader(final byte[] header, final ZipEncoding encoding) 1090 throws IOException { 1091 parseTarHeader(header, encoding, false); 1092 } 1093 1094 private void parseTarHeader(final byte[] header, final ZipEncoding encoding, 1095 final boolean oldStyle) 1096 throws IOException { 1097 int offset = 0; 1098 1099 name = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) 1100 : TarUtils.parseName(header, offset, NAMELEN, encoding); 1101 offset += NAMELEN; 1102 mode = (int) TarUtils.parseOctalOrBinary(header, offset, MODELEN); 1103 offset += MODELEN; 1104 userId = (int) TarUtils.parseOctalOrBinary(header, offset, UIDLEN); 1105 offset += UIDLEN; 1106 groupId = (int) TarUtils.parseOctalOrBinary(header, offset, GIDLEN); 1107 offset += GIDLEN; 1108 size = TarUtils.parseOctalOrBinary(header, offset, SIZELEN); 1109 offset += SIZELEN; 1110 modTime = TarUtils.parseOctalOrBinary(header, offset, MODTIMELEN); 1111 offset += MODTIMELEN; 1112 checkSumOK = TarUtils.verifyCheckSum(header); 1113 offset += CHKSUMLEN; 1114 linkFlag = header[offset++]; 1115 linkName = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) 1116 : TarUtils.parseName(header, offset, NAMELEN, encoding); 1117 offset += NAMELEN; 1118 magic = TarUtils.parseName(header, offset, MAGICLEN); 1119 offset += MAGICLEN; 1120 version = TarUtils.parseName(header, offset, VERSIONLEN); 1121 offset += VERSIONLEN; 1122 userName = oldStyle ? TarUtils.parseName(header, offset, UNAMELEN) 1123 : TarUtils.parseName(header, offset, UNAMELEN, encoding); 1124 offset += UNAMELEN; 1125 groupName = oldStyle ? TarUtils.parseName(header, offset, GNAMELEN) 1126 : TarUtils.parseName(header, offset, GNAMELEN, encoding); 1127 offset += GNAMELEN; 1128 devMajor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN); 1129 offset += DEVLEN; 1130 devMinor = (int) TarUtils.parseOctalOrBinary(header, offset, DEVLEN); 1131 offset += DEVLEN; 1132 1133 final int type = evaluateType(header); 1134 switch (type) { 1135 case FORMAT_OLDGNU: { 1136 offset += ATIMELEN_GNU; 1137 offset += CTIMELEN_GNU; 1138 offset += OFFSETLEN_GNU; 1139 offset += LONGNAMESLEN_GNU; 1140 offset += PAD2LEN_GNU; 1141 offset += SPARSELEN_GNU; 1142 isExtended = TarUtils.parseBoolean(header, offset); 1143 offset += ISEXTENDEDLEN_GNU; 1144 realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU); 1145 offset += REALSIZELEN_GNU; 1146 break; 1147 } 1148 case FORMAT_XSTAR: { 1149 final String xstarPrefix = oldStyle 1150 ? TarUtils.parseName(header, offset, PREFIXLEN_XSTAR) 1151 : TarUtils.parseName(header, offset, PREFIXLEN_XSTAR, encoding); 1152 if (xstarPrefix.length() > 0) { 1153 name = xstarPrefix + "/" + name; 1154 } 1155 break; 1156 } 1157 case FORMAT_POSIX: 1158 default: { 1159 final String prefix = oldStyle 1160 ? TarUtils.parseName(header, offset, PREFIXLEN) 1161 : TarUtils.parseName(header, offset, PREFIXLEN, encoding); 1162 // SunOS tar -E does not add / to directory names, so fix 1163 // up to be consistent 1164 if (isDirectory() && !name.endsWith("/")){ 1165 name = name + "/"; 1166 } 1167 if (prefix.length() > 0){ 1168 name = prefix + "/" + name; 1169 } 1170 } 1171 } 1172 } 1173 1174 /** 1175 * Strips Windows' drive letter as well as any leading slashes, 1176 * turns path separators into forward slahes. 1177 */ 1178 private static String normalizeFileName(String fileName, 1179 final boolean preserveLeadingSlashes) { 1180 final String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); 1181 1182 if (osname != null) { 1183 1184 // Strip off drive letters! 1185 // REVIEW Would a better check be "(File.separator == '\')"? 1186 1187 if (osname.startsWith("windows")) { 1188 if (fileName.length() > 2) { 1189 final char ch1 = fileName.charAt(0); 1190 final char ch2 = fileName.charAt(1); 1191 1192 if (ch2 == ':' 1193 && (ch1 >= 'a' && ch1 <= 'z' 1194 || ch1 >= 'A' && ch1 <= 'Z')) { 1195 fileName = fileName.substring(2); 1196 } 1197 } 1198 } else if (osname.contains("netware")) { 1199 final int colon = fileName.indexOf(':'); 1200 if (colon != -1) { 1201 fileName = fileName.substring(colon + 1); 1202 } 1203 } 1204 } 1205 1206 fileName = fileName.replace(File.separatorChar, '/'); 1207 1208 // No absolute pathnames 1209 // Windows (and Posix?) paths can start with "\\NetworkDrive\", 1210 // so we loop on starting /'s. 1211 while (!preserveLeadingSlashes && fileName.startsWith("/")) { 1212 fileName = fileName.substring(1); 1213 } 1214 return fileName; 1215 } 1216 1217 /** 1218 * Evaluate an entry's header format from a header buffer. 1219 * 1220 * @param header The tar entry header buffer to evaluate the format for. 1221 * @return format type 1222 */ 1223 private int evaluateType(final byte[] header) { 1224 if (ArchiveUtils.matchAsciiBuffer(MAGIC_GNU, header, MAGIC_OFFSET, MAGICLEN)) { 1225 return FORMAT_OLDGNU; 1226 } 1227 if (ArchiveUtils.matchAsciiBuffer(MAGIC_POSIX, header, MAGIC_OFFSET, MAGICLEN)) { 1228 if (ArchiveUtils.matchAsciiBuffer(MAGIC_XSTAR, header, XSTAR_MAGIC_OFFSET, 1229 XSTAR_MAGIC_LEN)) { 1230 return FORMAT_XSTAR; 1231 } 1232 return FORMAT_POSIX; 1233 } 1234 return 0; 1235 } 1236 1237 void fillGNUSparse0xData(final Map<String, String> headers) { 1238 paxGNUSparse = true; 1239 realSize = Integer.parseInt(headers.get("GNU.sparse.size")); 1240 if (headers.containsKey("GNU.sparse.name")) { 1241 // version 0.1 1242 name = headers.get("GNU.sparse.name"); 1243 } 1244 } 1245 1246 void fillGNUSparse1xData(final Map<String, String> headers) { 1247 paxGNUSparse = true; 1248 realSize = Integer.parseInt(headers.get("GNU.sparse.realsize")); 1249 name = headers.get("GNU.sparse.name"); 1250 } 1251 1252 void fillStarSparseData(final Map<String, String> headers) { 1253 starSparse = true; 1254 if (headers.containsKey("SCHILY.realsize")) { 1255 realSize = Long.parseLong(headers.get("SCHILY.realsize")); 1256 } 1257 } 1258} 1259