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