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 */ 019 package org.apache.commons.compress.archivers.tar; 020 021 import java.io.File; 022 import java.nio.ByteBuffer; 023 import java.util.Date; 024 import java.util.Locale; 025 026 import org.apache.commons.compress.archivers.ArchiveEntry; 027 028 /** 029 * This class represents an entry in a Tar archive. It consists 030 * of the entry's header, as well as the entry's File. Entries 031 * can be instantiated in one of three ways, depending on how 032 * they are to be used. 033 * <p> 034 * TarEntries that are created from the header bytes read from 035 * an archive are instantiated with the TarEntry( byte[] ) 036 * constructor. These entries will be used when extracting from 037 * or listing the contents of an archive. These entries have their 038 * header filled in using the header bytes. They also set the File 039 * to null, since they reference an archive entry not a file. 040 * <p> 041 * TarEntries that are created from Files that are to be written 042 * into an archive are instantiated with the TarEntry( File ) 043 * constructor. These entries have their header filled in using 044 * the File's information. They also keep a reference to the File 045 * for convenience when writing entries. 046 * <p> 047 * Finally, TarEntries can be constructed from nothing but a name. 048 * This allows the programmer to construct the entry by hand, for 049 * instance when only an InputStream is available for writing to 050 * the archive, and the header information is constructed from 051 * other information. In this case the header fields are set to 052 * defaults and the File is set to null. 053 * 054 * <p> 055 * The C structure for a Tar Entry's header is: 056 * <pre> 057 * struct header { 058 * char name[100]; // TarConstants.NAMELEN - offset 0 059 * char mode[8]; // TarConstants.MODELEN - offset 100 060 * char uid[8]; // TarConstants.UIDLEN - offset 108 061 * char gid[8]; // TarConstants.GIDLEN - offset 116 062 * char size[12]; // TarConstants.SIZELEN - offset 124 063 * char mtime[12]; // TarConstants.MODTIMELEN - offset 136 064 * char chksum[8]; // TarConstants.CHKSUMLEN - offset 148 065 * char linkflag[1]; // - offset 156 066 * char linkname[100]; // TarConstants.NAMELEN - offset 157 067 * The following fields are only present in new-style POSIX tar archives: 068 * char magic[6]; // TarConstants.MAGICLEN - offset 257 069 * char version[2]; // TarConstants.VERSIONLEN - offset 263 070 * char uname[32]; // TarConstants.UNAMELEN - offset 265 071 * char gname[32]; // TarConstants.GNAMELEN - offset 297 072 * char devmajor[8]; // TarConstants.DEVLEN - offset 329 073 * char devminor[8]; // TarConstants.DEVLEN - offset 337 074 * char prefix[155]; // TarConstants.PREFIXLEN - offset 345 075 * // Used if "name" field is not long enough to hold the path 076 * char pad[12]; // NULs - offset 500 077 * } header; 078 * All unused bytes are set to null. 079 * New-style GNU tar files are slightly different from the above. 080 * </pre> 081 * 082 * <p> 083 * The C structure for a old GNU Tar Entry's header is: 084 * <pre> 085 * struct oldgnu_header { 086 * char unused_pad1[345]; // TarConstants.PAD1LEN_GNU - offset 0 087 * char atime[12]; // TarConstants.ATIMELEN_GNU - offset 345 088 * char ctime[12]; // TarConstants.CTIMELEN_GNU - offset 357 089 * char offset[12]; // TarConstants.OFFSETLEN_GNU - offset 369 090 * char longnames[4]; // TarConstants.LONGNAMESLEN_GNU - offset 381 091 * char unused_pad2; // TarConstants.PAD2LEN_GNU - offset 385 092 * struct sparse sp[4]; // TarConstants.SPARSELEN_GNU - offset 386 093 * char isextended; // TarConstants.ISEXTENDEDLEN_GNU - offset 482 094 * char realsize[12]; // TarConstants.REALSIZELEN_GNU - offset 483 095 * char unused_pad[17]; // TarConstants.PAD3LEN_GNU - offset 495 096 * }; 097 * </pre> 098 * Whereas, "struct sparse" is: 099 * <pre> 100 * struct sparse { 101 * char offset[12]; // offset 0 102 * char numbytes[12]; // offset 12 103 * }; 104 * </pre> 105 * 106 * @NotThreadSafe 107 */ 108 109 public class TarArchiveEntry implements TarConstants, ArchiveEntry { 110 /** The entry's name. */ 111 private String name; 112 113 /** The entry's permission mode. */ 114 private int mode; 115 116 /** The entry's user id. */ 117 private int userId; 118 119 /** The entry's group id. */ 120 private int groupId; 121 122 /** The entry's size. */ 123 private long size; 124 125 /** The entry's modification time. */ 126 private long modTime; 127 128 /** The entry's link flag. */ 129 private byte linkFlag; 130 131 /** The entry's link name. */ 132 private String linkName; 133 134 /** The entry's magic tag. */ 135 private String magic; 136 /** The version of the format */ 137 private String version; 138 139 /** The entry's user name. */ 140 private String userName; 141 142 /** The entry's group name. */ 143 private String groupName; 144 145 /** The entry's major device number. */ 146 private int devMajor; 147 148 /** The entry's minor device number. */ 149 private int devMinor; 150 151 /** If an extension sparse header follows. */ 152 private boolean isExtended; 153 154 /** The entry's real size in case of a sparse file. */ 155 private long realSize; 156 157 /** The entry's file reference */ 158 private File file; 159 160 /** Maximum length of a user's name in the tar file */ 161 public static final int MAX_NAMELEN = 31; 162 163 /** Default permissions bits for directories */ 164 public static final int DEFAULT_DIR_MODE = 040755; 165 166 /** Default permissions bits for files */ 167 public static final int DEFAULT_FILE_MODE = 0100644; 168 169 /** Convert millis to seconds */ 170 public static final int MILLIS_PER_SECOND = 1000; 171 172 /** 173 * Construct an empty entry and prepares the header values. 174 */ 175 private TarArchiveEntry () { 176 this.magic = MAGIC_POSIX; 177 this.version = VERSION_POSIX; 178 this.name = ""; 179 this.linkName = ""; 180 181 String user = System.getProperty("user.name", ""); 182 183 if (user.length() > MAX_NAMELEN) { 184 user = user.substring(0, MAX_NAMELEN); 185 } 186 187 this.userId = 0; 188 this.groupId = 0; 189 this.userName = user; 190 this.groupName = ""; 191 this.file = null; 192 } 193 194 /** 195 * Construct an entry with only a name. This allows the programmer 196 * to construct the entry's header "by hand". File is set to null. 197 * 198 * @param name the entry name 199 */ 200 public TarArchiveEntry(String name) { 201 this(name, false); 202 } 203 204 /** 205 * Construct an entry with only a name. This allows the programmer 206 * to construct the entry's header "by hand". File is set to null. 207 * 208 * @param name the entry name 209 * @param preserveLeadingSlashes whether to allow leading slashes 210 * in the name. 211 * 212 * @since Apache Commons Compress 1.1 213 */ 214 public TarArchiveEntry(String name, boolean preserveLeadingSlashes) { 215 this(); 216 217 name = normalizeFileName(name, preserveLeadingSlashes); 218 boolean isDir = name.endsWith("/"); 219 220 this.devMajor = 0; 221 this.devMinor = 0; 222 this.name = name; 223 this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE; 224 this.linkFlag = isDir ? LF_DIR : LF_NORMAL; 225 this.userId = 0; 226 this.groupId = 0; 227 this.size = 0; 228 this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND; 229 this.linkName = ""; 230 this.userName = ""; 231 this.groupName = ""; 232 this.devMajor = 0; 233 this.devMinor = 0; 234 235 } 236 237 /** 238 * Construct an entry with a name and a link flag. 239 * 240 * @param name the entry name 241 * @param linkFlag the entry link flag. 242 */ 243 public TarArchiveEntry(String name, byte linkFlag) { 244 this(name); 245 this.linkFlag = linkFlag; 246 if (linkFlag == LF_GNUTYPE_LONGNAME) { 247 magic = MAGIC_GNU; 248 version = VERSION_GNU_SPACE; 249 } 250 } 251 252 /** 253 * Construct an entry for a file. File is set to file, and the 254 * header is constructed from information from the file. 255 * The name is set from the normalized file path. 256 * 257 * @param file The file that the entry represents. 258 */ 259 public TarArchiveEntry(File file) { 260 this(file, normalizeFileName(file.getPath(), false)); 261 } 262 263 /** 264 * Construct an entry for a file. File is set to file, and the 265 * header is constructed from information from the file. 266 * 267 * @param file The file that the entry represents. 268 * @param fileName the name to be used for the entry. 269 */ 270 public TarArchiveEntry(File file, String fileName) { 271 this(); 272 273 this.file = file; 274 275 this.linkName = ""; 276 277 if (file.isDirectory()) { 278 this.mode = DEFAULT_DIR_MODE; 279 this.linkFlag = LF_DIR; 280 281 int nameLength = fileName.length(); 282 if (nameLength == 0 || fileName.charAt(nameLength - 1) != '/') { 283 this.name = fileName + "/"; 284 } else { 285 this.name = fileName; 286 } 287 this.size = 0; 288 } else { 289 this.mode = DEFAULT_FILE_MODE; 290 this.linkFlag = LF_NORMAL; 291 this.size = file.length(); 292 this.name = fileName; 293 } 294 295 this.modTime = file.lastModified() / MILLIS_PER_SECOND; 296 this.devMajor = 0; 297 this.devMinor = 0; 298 } 299 300 /** 301 * Construct an entry from an archive's header bytes. File is set 302 * to null. 303 * 304 * @param headerBuf The header bytes from a tar archive entry. 305 */ 306 public TarArchiveEntry(byte[] headerBuf) { 307 this(); 308 parseTarHeader(headerBuf); 309 } 310 311 /** 312 * Determine if the two entries are equal. Equality is determined 313 * by the header names being equal. 314 * 315 * @param it Entry to be checked for equality. 316 * @return True if the entries are equal. 317 */ 318 public boolean equals(TarArchiveEntry it) { 319 return getName().equals(it.getName()); 320 } 321 322 /** 323 * Determine if the two entries are equal. Equality is determined 324 * by the header names being equal. 325 * 326 * @param it Entry to be checked for equality. 327 * @return True if the entries are equal. 328 */ 329 public boolean equals(Object it) { 330 if (it == null || getClass() != it.getClass()) { 331 return false; 332 } 333 return equals((TarArchiveEntry) it); 334 } 335 336 /** 337 * Hashcodes are based on entry names. 338 * 339 * @return the entry hashcode 340 */ 341 public int hashCode() { 342 return getName().hashCode(); 343 } 344 345 /** 346 * Determine if the given entry is a descendant of this entry. 347 * Descendancy is determined by the name of the descendant 348 * starting with this entry's name. 349 * 350 * @param desc Entry to be checked as a descendent of this. 351 * @return True if entry is a descendant of this. 352 */ 353 public boolean isDescendent(TarArchiveEntry desc) { 354 return desc.getName().startsWith(getName()); 355 } 356 357 /** 358 * Get this entry's name. 359 * 360 * @return This entry's name. 361 */ 362 public String getName() { 363 return name.toString(); 364 } 365 366 /** 367 * Set this entry's name. 368 * 369 * @param name This entry's new name. 370 */ 371 public void setName(String name) { 372 this.name = normalizeFileName(name, false); 373 } 374 375 /** 376 * Set the mode for this entry 377 * 378 * @param mode the mode for this entry 379 */ 380 public void setMode(int mode) { 381 this.mode = mode; 382 } 383 384 /** 385 * Get this entry's link name. 386 * 387 * @return This entry's link name. 388 */ 389 public String getLinkName() { 390 return linkName.toString(); 391 } 392 393 /** 394 * Set this entry's link name. 395 * 396 * @param link the link name to use. 397 * 398 * @since Apache Commons Compress 1.1 399 */ 400 public void setLinkName(String link) { 401 this.linkName = link; 402 } 403 404 /** 405 * Get this entry's user id. 406 * 407 * @return This entry's user id. 408 */ 409 public int getUserId() { 410 return userId; 411 } 412 413 /** 414 * Set this entry's user id. 415 * 416 * @param userId This entry's new user id. 417 */ 418 public void setUserId(int userId) { 419 this.userId = userId; 420 } 421 422 /** 423 * Get this entry's group id. 424 * 425 * @return This entry's group id. 426 */ 427 public int getGroupId() { 428 return groupId; 429 } 430 431 /** 432 * Set this entry's group id. 433 * 434 * @param groupId This entry's new group id. 435 */ 436 public void setGroupId(int groupId) { 437 this.groupId = groupId; 438 } 439 440 /** 441 * Get this entry's user name. 442 * 443 * @return This entry's user name. 444 */ 445 public String getUserName() { 446 return userName.toString(); 447 } 448 449 /** 450 * Set this entry's user name. 451 * 452 * @param userName This entry's new user name. 453 */ 454 public void setUserName(String userName) { 455 this.userName = userName; 456 } 457 458 /** 459 * Get this entry's group name. 460 * 461 * @return This entry's group name. 462 */ 463 public String getGroupName() { 464 return groupName.toString(); 465 } 466 467 /** 468 * Set this entry's group name. 469 * 470 * @param groupName This entry's new group name. 471 */ 472 public void setGroupName(String groupName) { 473 this.groupName = groupName; 474 } 475 476 /** 477 * Convenience method to set this entry's group and user ids. 478 * 479 * @param userId This entry's new user id. 480 * @param groupId This entry's new group id. 481 */ 482 public void setIds(int userId, int groupId) { 483 setUserId(userId); 484 setGroupId(groupId); 485 } 486 487 /** 488 * Convenience method to set this entry's group and user names. 489 * 490 * @param userName This entry's new user name. 491 * @param groupName This entry's new group name. 492 */ 493 public void setNames(String userName, String groupName) { 494 setUserName(userName); 495 setGroupName(groupName); 496 } 497 498 /** 499 * Set this entry's modification time. The parameter passed 500 * to this method is in "Java time". 501 * 502 * @param time This entry's new modification time. 503 */ 504 public void setModTime(long time) { 505 modTime = time / MILLIS_PER_SECOND; 506 } 507 508 /** 509 * Set this entry's modification time. 510 * 511 * @param time This entry's new modification time. 512 */ 513 public void setModTime(Date time) { 514 modTime = time.getTime() / MILLIS_PER_SECOND; 515 } 516 517 /** 518 * Set this entry's modification time. 519 * 520 * @return time This entry's new modification time. 521 */ 522 public Date getModTime() { 523 return new Date(modTime * MILLIS_PER_SECOND); 524 } 525 526 /** {@inheritDoc} */ 527 public Date getLastModifiedDate() { 528 return getModTime(); 529 } 530 531 /** 532 * Get this entry's file. 533 * 534 * @return This entry's file. 535 */ 536 public File getFile() { 537 return file; 538 } 539 540 /** 541 * Get this entry's mode. 542 * 543 * @return This entry's mode. 544 */ 545 public int getMode() { 546 return mode; 547 } 548 549 /** 550 * Get this entry's file size. 551 * 552 * @return This entry's file size. 553 */ 554 public long getSize() { 555 return size; 556 } 557 558 /** 559 * Set this entry's file size. 560 * 561 * @param size This entry's new file size. 562 * @throws IllegalArgumentException if the size is < 0 563 * or > {@link TarConstants#MAXSIZE} (077777777777L). 564 */ 565 public void setSize(long size) { 566 if (size > MAXSIZE || size < 0){ 567 throw new IllegalArgumentException("Size is out of range: "+size); 568 } 569 this.size = size; 570 } 571 572 /** 573 * Indicates in case of a sparse file if an extension sparse header 574 * follows. 575 * 576 * @return true if an extension sparse header follows. 577 */ 578 public boolean isExtended() { 579 return isExtended; 580 } 581 582 /** 583 * Get this entry's real file size in case of a sparse file. 584 * 585 * @return This entry's real file size. 586 */ 587 public long getRealSize() { 588 return realSize; 589 } 590 591 /** 592 * Indicate if this entry is a GNU sparse block 593 * 594 * @return true if this is a sparse extension provided by GNU tar 595 */ 596 public boolean isGNUSparse() { 597 return linkFlag == LF_GNUTYPE_SPARSE; 598 } 599 600 /** 601 * Indicate if this entry is a GNU long name block 602 * 603 * @return true if this is a long name extension provided by GNU tar 604 */ 605 public boolean isGNULongNameEntry() { 606 return linkFlag == LF_GNUTYPE_LONGNAME 607 && name.toString().equals(GNU_LONGLINK); 608 } 609 610 /** 611 * Check if this is a Pax header. 612 * 613 * @return <code>true</code> if this is a Pax header. 614 * 615 * @since Apache Commons Compress 1.1 616 */ 617 public boolean isPaxHeader(){ 618 return linkFlag == LF_PAX_EXTENDED_HEADER_LC 619 || linkFlag == LF_PAX_EXTENDED_HEADER_UC; 620 } 621 622 /** 623 * Check if this is a Pax header. 624 * 625 * @return <code>true</code> if this is a Pax header. 626 * 627 * @since Apache Commons Compress 1.1 628 */ 629 public boolean isGlobalPaxHeader(){ 630 return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER; 631 } 632 633 /** 634 * Return whether or not this entry represents a directory. 635 * 636 * @return True if this entry is a directory. 637 */ 638 public boolean isDirectory() { 639 if (file != null) { 640 return file.isDirectory(); 641 } 642 643 if (linkFlag == LF_DIR) { 644 return true; 645 } 646 647 if (getName().endsWith("/")) { 648 return true; 649 } 650 651 return false; 652 } 653 654 /** 655 * Check if this is a "normal file" 656 * 657 * @since Apache Commons Compress 1.2 658 */ 659 public boolean isFile() { 660 if (file != null) { 661 return file.isFile(); 662 } 663 if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) { 664 return true; 665 } 666 return !getName().endsWith("/"); 667 } 668 669 /** 670 * Check if this is a symbolic link entry. 671 * 672 * @since Apache Commons Compress 1.2 673 */ 674 public boolean isSymbolicLink() { 675 return linkFlag == LF_SYMLINK; 676 } 677 678 /** 679 * Check if this is a link entry. 680 * 681 * @since Apache Commons Compress 1.2 682 */ 683 public boolean isLink() { 684 return linkFlag == LF_LINK; 685 } 686 687 /** 688 * Check if this is a character device entry. 689 * 690 * @since Apache Commons Compress 1.2 691 */ 692 public boolean isCharacterDevice() { 693 return linkFlag == LF_CHR; 694 } 695 696 /** 697 * Check if this is a block device entry. 698 * 699 * @since Apache Commons Compress 1.2 700 */ 701 public boolean isBlockDevice() { 702 return linkFlag == LF_BLK; 703 } 704 705 /** 706 * Check if this is a FIFO (pipe) entry. 707 * 708 * @since Apache Commons Compress 1.2 709 */ 710 public boolean isFIFO() { 711 return linkFlag == LF_FIFO; 712 } 713 714 /** 715 * If this entry represents a file, and the file is a directory, return 716 * an array of TarEntries for this entry's children. 717 * 718 * @return An array of TarEntry's for this entry's children. 719 */ 720 public TarArchiveEntry[] getDirectoryEntries() { 721 if (file == null || !file.isDirectory()) { 722 return new TarArchiveEntry[0]; 723 } 724 725 String[] list = file.list(); 726 TarArchiveEntry[] result = new TarArchiveEntry[list.length]; 727 728 for (int i = 0; i < list.length; ++i) { 729 result[i] = new TarArchiveEntry(new File(file, list[i])); 730 } 731 732 return result; 733 } 734 735 /** 736 * Write an entry's header information to a header buffer. 737 * 738 * @param outbuf The tar entry header buffer to fill in. 739 */ 740 public void writeEntryHeader(byte[] outbuf) { 741 int offset = 0; 742 743 offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN); 744 offset = TarUtils.formatOctalBytes(mode, outbuf, offset, MODELEN); 745 offset = TarUtils.formatOctalBytes(userId, outbuf, offset, UIDLEN); 746 offset = TarUtils.formatOctalBytes(groupId, outbuf, offset, GIDLEN); 747 offset = TarUtils.formatLongOctalBytes(size, outbuf, offset, SIZELEN); 748 offset = TarUtils.formatLongOctalBytes(modTime, outbuf, offset, MODTIMELEN); 749 750 int csOffset = offset; 751 752 for (int c = 0; c < CHKSUMLEN; ++c) { 753 outbuf[offset++] = (byte) ' '; 754 } 755 756 outbuf[offset++] = linkFlag; 757 offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN); 758 offset = TarUtils.formatNameBytes(magic, outbuf, offset, MAGICLEN); 759 offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN); 760 offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN); 761 offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN); 762 offset = TarUtils.formatOctalBytes(devMajor, outbuf, offset, DEVLEN); 763 offset = TarUtils.formatOctalBytes(devMinor, outbuf, offset, DEVLEN); 764 765 while (offset < outbuf.length) { 766 outbuf[offset++] = 0; 767 } 768 769 long chk = TarUtils.computeCheckSum(outbuf); 770 771 TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN); 772 } 773 774 /** 775 * Parse an entry's header information from a header buffer. 776 * 777 * @param header The tar entry header buffer to get information from. 778 */ 779 public void parseTarHeader(byte[] header) { 780 int offset = 0; 781 782 name = TarUtils.parseName(header, offset, NAMELEN); 783 offset += NAMELEN; 784 mode = (int) TarUtils.parseOctal(header, offset, MODELEN); 785 offset += MODELEN; 786 userId = (int) TarUtils.parseOctal(header, offset, UIDLEN); 787 offset += UIDLEN; 788 groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN); 789 offset += GIDLEN; 790 size = TarUtils.parseOctal(header, offset, SIZELEN); 791 offset += SIZELEN; 792 modTime = TarUtils.parseOctal(header, offset, MODTIMELEN); 793 offset += MODTIMELEN; 794 offset += CHKSUMLEN; 795 linkFlag = header[offset++]; 796 linkName = TarUtils.parseName(header, offset, NAMELEN); 797 offset += NAMELEN; 798 magic = TarUtils.parseName(header, offset, MAGICLEN); 799 offset += MAGICLEN; 800 version = TarUtils.parseName(header, offset, VERSIONLEN); 801 offset += VERSIONLEN; 802 userName = TarUtils.parseName(header, offset, UNAMELEN); 803 offset += UNAMELEN; 804 groupName = TarUtils.parseName(header, offset, GNAMELEN); 805 offset += GNAMELEN; 806 devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN); 807 offset += DEVLEN; 808 devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN); 809 offset += DEVLEN; 810 811 int type = evaluateType(header); 812 switch (type) { 813 case FORMAT_OLDGNU: { 814 offset += ATIMELEN_GNU; 815 offset += CTIMELEN_GNU; 816 offset += OFFSETLEN_GNU; 817 offset += LONGNAMESLEN_GNU; 818 offset += PAD2LEN_GNU; 819 offset += SPARSELEN_GNU; 820 isExtended = TarUtils.parseBoolean(header, offset); 821 offset += ISEXTENDEDLEN_GNU; 822 realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU); 823 offset += REALSIZELEN_GNU; 824 break; 825 } 826 case FORMAT_POSIX: 827 default: { 828 String prefix = TarUtils.parseName(header, offset, PREFIXLEN); 829 // SunOS tar -E does not add / to directory names, so fix 830 // up to be consistent 831 if (isDirectory() && !name.endsWith("/")){ 832 name = name + "/"; 833 } 834 if (prefix.length() > 0){ 835 name = prefix + "/" + name; 836 } 837 } 838 } 839 } 840 841 /** 842 * Strips Windows' drive letter as well as any leading slashes, 843 * turns path separators into forward slahes. 844 */ 845 private static String normalizeFileName(String fileName, 846 boolean preserveLeadingSlashes) { 847 String osname = System.getProperty("os.name").toLowerCase(Locale.ENGLISH); 848 849 if (osname != null) { 850 851 // Strip off drive letters! 852 // REVIEW Would a better check be "(File.separator == '\')"? 853 854 if (osname.startsWith("windows")) { 855 if (fileName.length() > 2) { 856 char ch1 = fileName.charAt(0); 857 char ch2 = fileName.charAt(1); 858 859 if (ch2 == ':' 860 && ((ch1 >= 'a' && ch1 <= 'z') 861 || (ch1 >= 'A' && ch1 <= 'Z'))) { 862 fileName = fileName.substring(2); 863 } 864 } 865 } else if (osname.indexOf("netware") > -1) { 866 int colon = fileName.indexOf(':'); 867 if (colon != -1) { 868 fileName = fileName.substring(colon + 1); 869 } 870 } 871 } 872 873 fileName = fileName.replace(File.separatorChar, '/'); 874 875 // No absolute pathnames 876 // Windows (and Posix?) paths can start with "\\NetworkDrive\", 877 // so we loop on starting /'s. 878 while (!preserveLeadingSlashes && fileName.startsWith("/")) { 879 fileName = fileName.substring(1); 880 } 881 return fileName; 882 } 883 884 /** 885 * Evaluate an entry's header format from a header buffer. 886 * 887 * @param header The tar entry header buffer to evaluate the format for. 888 * @return format type 889 */ 890 private int evaluateType(byte[] header) { 891 final ByteBuffer magic = ByteBuffer.wrap(header, MAGIC_OFFSET, MAGICLEN); 892 if (magic.compareTo(ByteBuffer.wrap(MAGIC_GNU.getBytes())) == 0) 893 return FORMAT_OLDGNU; 894 if (magic.compareTo(ByteBuffer.wrap(MAGIC_POSIX.getBytes())) == 0) 895 return FORMAT_POSIX; 896 return 0; 897 } 898 } 899