TarArchiveEntry.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  * http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing,
  13.  * software distributed under the License is distributed on an
  14.  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15.  * KIND, either express or implied.  See the License for the
  16.  * specific language governing permissions and limitations
  17.  * under the License.
  18.  */
  19. package org.apache.commons.compress.archivers.tar;

  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.UncheckedIOException;
  23. import java.math.BigDecimal;
  24. import java.nio.file.DirectoryStream;
  25. import java.nio.file.Files;
  26. import java.nio.file.LinkOption;
  27. import java.nio.file.Path;
  28. import java.nio.file.attribute.BasicFileAttributes;
  29. import java.nio.file.attribute.DosFileAttributes;
  30. import java.nio.file.attribute.FileTime;
  31. import java.nio.file.attribute.PosixFileAttributes;
  32. import java.time.DateTimeException;
  33. import java.time.Instant;
  34. import java.util.ArrayList;
  35. import java.util.Collections;
  36. import java.util.Comparator;
  37. import java.util.Date;
  38. import java.util.HashMap;
  39. import java.util.List;
  40. import java.util.Locale;
  41. import java.util.Map;
  42. import java.util.Objects;
  43. import java.util.Set;
  44. import java.util.regex.Pattern;
  45. import java.util.stream.Collectors;

  46. import org.apache.commons.compress.archivers.ArchiveEntry;
  47. import org.apache.commons.compress.archivers.EntryStreamOffsets;
  48. import org.apache.commons.compress.archivers.zip.ZipEncoding;
  49. import org.apache.commons.compress.utils.ArchiveUtils;
  50. import org.apache.commons.compress.utils.IOUtils;
  51. import org.apache.commons.compress.utils.ParsingUtils;
  52. import org.apache.commons.compress.utils.TimeUtils;
  53. import org.apache.commons.io.file.attribute.FileTimes;
  54. import org.apache.commons.lang3.SystemProperties;

  55. /**
  56.  * An entry in a <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html">Tar archive</a>.
  57.  * It consists of the entry's header, as well as the entry's File. Entries can be instantiated in one of three
  58.  * ways, depending on how they are to be used.
  59.  * <p>
  60.  * TarEntries that are created from the header bytes read from an archive are instantiated with the {@link TarArchiveEntry#TarArchiveEntry(byte[])} constructor.
  61.  * These entries will be used when extracting from or listing the contents of an archive. These entries have their header filled in using the header bytes. They
  62.  * also set the File to null, since they reference an archive entry not a file.
  63.  * </p>
  64.  * <p>
  65.  * TarEntries that are created from Files that are to be written into an archive are instantiated with the {@link TarArchiveEntry#TarArchiveEntry(File)} or
  66.  * {@link TarArchiveEntry#TarArchiveEntry(Path)} constructor. These entries have their header filled in using the File's information. They also keep a reference
  67.  * to the File for convenience when writing entries.
  68.  * </p>
  69.  * <p>
  70.  * Finally, TarEntries can be constructed from nothing but a name. This allows the programmer to construct the entry by hand, for instance when only an
  71.  * InputStream is available for writing to the archive, and the header information is constructed from other information. In this case the header fields are set
  72.  * to defaults and the File is set to null.
  73.  * </p>
  74.  * <p>
  75.  * The C structure for a Tar Entry's header is:
  76.  * </p>
  77.  * <pre>
  78.  * struct header {
  79.  *   char name[100];     // TarConstants.NAMELEN    - offset   0
  80.  *   char mode[8];       // TarConstants.MODELEN    - offset 100
  81.  *   char uid[8];        // TarConstants.UIDLEN     - offset 108
  82.  *   char gid[8];        // TarConstants.GIDLEN     - offset 116
  83.  *   char size[12];      // TarConstants.SIZELEN    - offset 124
  84.  *   char mtime[12];     // TarConstants.MODTIMELEN - offset 136
  85.  *   char chksum[8];     // TarConstants.CHKSUMLEN  - offset 148
  86.  *   char linkflag[1];   //                         - offset 156
  87.  *   char linkname[100]; // TarConstants.NAMELEN    - offset 157
  88.  *   // The following fields are only present in new-style POSIX tar archives:
  89.  *   char magic[6];      // TarConstants.MAGICLEN   - offset 257
  90.  *   char version[2];    // TarConstants.VERSIONLEN - offset 263
  91.  *   char uname[32];     // TarConstants.UNAMELEN   - offset 265
  92.  *   char gname[32];     // TarConstants.GNAMELEN   - offset 297
  93.  *   char devmajor[8];   // TarConstants.DEVLEN     - offset 329
  94.  *   char devminor[8];   // TarConstants.DEVLEN     - offset 337
  95.  *   char prefix[155];   // TarConstants.PREFIXLEN  - offset 345
  96.  *   // Used if "name" field is not long enough to hold the path
  97.  *   char pad[12];       // NULs                    - offset 500
  98.  * } header;
  99.  * </pre>
  100.  * <p>
  101.  * All unused bytes are set to null. New-style GNU tar files are slightly different from the above. For values of size larger than 077777777777L (11 7s) or uid
  102.  * and gid larger than 07777777L (7 7s) the sign bit of the first byte is set, and the rest of the field is the binary representation of the number. See
  103.  * {@link TarUtils#parseOctalOrBinary(byte[], int, int)}.
  104.  * <p>
  105.  * The C structure for a old GNU Tar Entry's header is:
  106.  * </p>
  107.  * <pre>
  108.  * struct oldgnu_header {
  109.  *   char unused_pad1[345]; // TarConstants.PAD1LEN_GNU       - offset 0
  110.  *   char atime[12];        // TarConstants.ATIMELEN_GNU      - offset 345
  111.  *   char ctime[12];        // TarConstants.CTIMELEN_GNU      - offset 357
  112.  *   char offset[12];       // TarConstants.OFFSETLEN_GNU     - offset 369
  113.  *   char longnames[4];     // TarConstants.LONGNAMESLEN_GNU  - offset 381
  114.  *   char unused_pad2;      // TarConstants.PAD2LEN_GNU       - offset 385
  115.  *   struct sparse sp[4];   // TarConstants.SPARSELEN_GNU     - offset 386
  116.  *   char isextended;       // TarConstants.ISEXTENDEDLEN_GNU - offset 482
  117.  *   char realsize[12];     // TarConstants.REALSIZELEN_GNU   - offset 483
  118.  *   char unused_pad[17];   // TarConstants.PAD3LEN_GNU       - offset 495
  119.  * };
  120.  * </pre>
  121.  * <p>
  122.  * Whereas, "struct sparse" is:
  123.  * </p>
  124.  * <pre>
  125.  * struct sparse {
  126.  *   char offset[12];   // offset 0
  127.  *   char numbytes[12]; // offset 12
  128.  * };
  129.  * </pre>
  130.  * <p>
  131.  * The C structure for a xstar (Jörg Schilling star) Tar Entry's header is:
  132.  * </p>
  133.  * <pre>
  134.  * struct star_header {
  135.  *   char name[100];     // offset   0
  136.  *   char mode[8];       // offset 100
  137.  *   char uid[8];        // offset 108
  138.  *   char gid[8];        // offset 116
  139.  *   char size[12];      // offset 124
  140.  *   char mtime[12];     // offset 136
  141.  *   char chksum[8];     // offset 148
  142.  *   char typeflag;      // offset 156
  143.  *   char linkname[100]; // offset 157
  144.  *   char magic[6];      // offset 257
  145.  *   char version[2];    // offset 263
  146.  *   char uname[32];     // offset 265
  147.  *   char gname[32];     // offset 297
  148.  *   char devmajor[8];   // offset 329
  149.  *   char devminor[8];   // offset 337
  150.  *   char prefix[131];   // offset 345
  151.  *   char atime[12];     // offset 476
  152.  *   char ctime[12];     // offset 488
  153.  *   char mfill[8];      // offset 500
  154.  *   char xmagic[4];     // offset 508  "tar\0"
  155.  * };
  156.  * </pre>
  157.  * <p>
  158.  * which is identical to new-style POSIX up to the first 130 bytes of the prefix.
  159.  * </p>
  160.  * <p>
  161.  * The C structure for the xstar-specific parts of a xstar Tar Entry's header is:
  162.  * </p>
  163.  * <pre>
  164.  * struct xstar_in_header {
  165.  *   char fill[345];         // offset 0     Everything before t_prefix
  166.  *   char prefix[1];         // offset 345   Prefix for t_name
  167.  *   char fill2;             // offset 346
  168.  *   char fill3[8];          // offset 347
  169.  *   char isextended;        // offset 355
  170.  *   struct sparse sp[SIH];  // offset 356   8 x 12
  171.  *   char realsize[12];      // offset 452   Real size for sparse data
  172.  *   char offset[12];        // offset 464   Offset for multivolume data
  173.  *   char atime[12];         // offset 476
  174.  *   char ctime[12];         // offset 488
  175.  *   char mfill[8];          // offset 500
  176.  *   char xmagic[4];         // offset 508   "tar\0"
  177.  * };
  178.  * </pre>
  179.  *
  180.  * @NotThreadSafe
  181.  */
  182. public class TarArchiveEntry implements ArchiveEntry, TarConstants, EntryStreamOffsets {

  183.     private static final TarArchiveEntry[] EMPTY_TAR_ARCHIVE_ENTRY_ARRAY = {};

  184.     /**
  185.      * Value used to indicate unknown mode, user/groupids, device numbers and modTime when parsing a file in lenient mode and the archive contains illegal
  186.      * fields.
  187.      *
  188.      * @since 1.19
  189.      */
  190.     public static final long UNKNOWN = -1L;

  191.     /** Maximum length of a user's name in the tar file */
  192.     public static final int MAX_NAMELEN = 31;

  193.     /** Default permissions bits for directories */
  194.     public static final int DEFAULT_DIR_MODE = 040755;

  195.     /** Default permissions bits for files */
  196.     public static final int DEFAULT_FILE_MODE = 0100644;

  197.     /**
  198.      * Convert millis to seconds
  199.      *
  200.      * @deprecated Unused.
  201.      */
  202.     @Deprecated
  203.     public static final int MILLIS_PER_SECOND = 1000;

  204.     /**
  205.      * Regular expression pattern for validating values in pax extended header file time fields. These fields contain two numeric values (seconds and sub-second
  206.      * values) as per this definition: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_05
  207.      * <p>
  208.      * Since they are parsed into long values, maximum length of each is the same as Long.MAX_VALUE which is 19 digits.
  209.      * </p>
  210.      */
  211.     private static final Pattern PAX_EXTENDED_HEADER_FILE_TIMES_PATTERN = Pattern.compile("-?\\d{1,19}(?:\\.\\d{1,19})?");

  212.     private static FileTime fileTimeFromOptionalSeconds(final long seconds) {
  213.         return seconds <= 0 ? null : FileTimes.fromUnixTime(seconds);
  214.     }

  215.     /**
  216.      * Strips Windows' drive letter as well as any leading slashes, turns path separators into forward slashes.
  217.      */
  218.     private static String normalizeFileName(String fileName, final boolean preserveAbsolutePath) {
  219.         if (!preserveAbsolutePath) {
  220.             final String property = SystemProperties.getOsName();
  221.             if (property != null) {
  222.                 final String osName = property.toLowerCase(Locale.ROOT);

  223.                 // Strip off drive letters!
  224.                 // REVIEW Would a better check be "(File.separator == '\')"?

  225.                 if (osName.startsWith("windows")) {
  226.                     if (fileName.length() > 2) {
  227.                         final char ch1 = fileName.charAt(0);
  228.                         final char ch2 = fileName.charAt(1);

  229.                         if (ch2 == ':' && (ch1 >= 'a' && ch1 <= 'z' || ch1 >= 'A' && ch1 <= 'Z')) {
  230.                             fileName = fileName.substring(2);
  231.                         }
  232.                     }
  233.                 } else if (osName.contains("netware")) {
  234.                     final int colon = fileName.indexOf(':');
  235.                     if (colon != -1) {
  236.                         fileName = fileName.substring(colon + 1);
  237.                     }
  238.                 }
  239.             }
  240.         }

  241.         fileName = fileName.replace(File.separatorChar, '/');

  242.         // No absolute pathnames
  243.         // Windows (and Posix?) paths can start with "\\NetworkDrive\",
  244.         // so we loop on starting /'s.
  245.         while (!preserveAbsolutePath && fileName.startsWith("/")) {
  246.             fileName = fileName.substring(1);
  247.         }
  248.         return fileName;
  249.     }

  250.     private static Instant parseInstantFromDecimalSeconds(final String value) throws IOException {
  251.         // Validate field values to prevent denial of service attacks with BigDecimal values (see JDK-6560193)
  252.         if (!PAX_EXTENDED_HEADER_FILE_TIMES_PATTERN.matcher(value).matches()) {
  253.             throw new IOException("Corrupted PAX header. Time field value is invalid '" + value + "'");
  254.         }

  255.         final BigDecimal epochSeconds = new BigDecimal(value);
  256.         final long seconds = epochSeconds.longValue();
  257.         final long nanos = epochSeconds.remainder(BigDecimal.ONE).movePointRight(9).longValue();
  258.         try {
  259.             return Instant.ofEpochSecond(seconds, nanos);
  260.         } catch (DateTimeException | ArithmeticException e) {
  261.             // DateTimeException: Thrown if the instant exceeds the maximum or minimum instant.
  262.             // ArithmeticException: Thrown if numeric overflow occurs.
  263.             throw new IOException("Corrupted PAX header. Time field value is invalid '" + value + "'", e);
  264.         }
  265.     }

  266.     /** The entry's name. */
  267.     private String name = "";

  268.     /** Whether to allow leading slashes or drive names inside the name */
  269.     private final boolean preserveAbsolutePath;

  270.     /** The entry's permission mode. */
  271.     private int mode;

  272.     /** The entry's user id. */
  273.     private long userId;

  274.     /** The entry's group id. */
  275.     private long groupId;

  276.     /** The entry's size. */
  277.     private long size;

  278.     /**
  279.      * The entry's modification time. Corresponds to the POSIX {@code mtime} attribute.
  280.      */
  281.     private FileTime mTime;

  282.     /**
  283.      * The entry's status change time. Corresponds to the POSIX {@code ctime} attribute.
  284.      *
  285.      * @since 1.22
  286.      */
  287.     private FileTime cTime;

  288.     /**
  289.      * The entry's last access time. Corresponds to the POSIX {@code atime} attribute.
  290.      *
  291.      * @since 1.22
  292.      */
  293.     private FileTime aTime;

  294.     /**
  295.      * The entry's creation time. Corresponds to the POSIX {@code birthtime} attribute.
  296.      *
  297.      * @since 1.22
  298.      */
  299.     private FileTime birthTime;

  300.     /** If the header checksum is reasonably correct. */
  301.     private boolean checkSumOK;

  302.     /** The entry's link flag. */
  303.     private byte linkFlag;

  304.     /** The entry's link name. */
  305.     private String linkName = "";

  306.     /** The entry's magic tag. */
  307.     private String magic = MAGIC_POSIX;

  308.     /** The version of the format */
  309.     private String version = VERSION_POSIX;

  310.     /** The entry's user name. */
  311.     private String userName;

  312.     /** The entry's group name. */
  313.     private String groupName = "";

  314.     /** The entry's major device number. */
  315.     private int devMajor;

  316.     /** The entry's minor device number. */
  317.     private int devMinor;

  318.     /** The sparse headers in tar */
  319.     private List<TarArchiveStructSparse> sparseHeaders;

  320.     /** If an extension sparse header follows. */
  321.     private boolean isExtended;

  322.     /** The entry's real size in case of a sparse file. */
  323.     private long realSize;

  324.     /** Is this entry a GNU sparse entry using one of the PAX formats? */
  325.     private boolean paxGNUSparse;

  326.     /**
  327.      * is this entry a GNU sparse entry using 1.X PAX formats? the sparse headers of 1.x PAX Format is stored in file data block
  328.      */
  329.     private boolean paxGNU1XSparse;

  330.     /** Is this entry a star sparse entry using the PAX header? */
  331.     private boolean starSparse;

  332.     /** The entry's file reference */
  333.     private final Path file;

  334.     /** The entry's file linkOptions */
  335.     private final LinkOption[] linkOptions;

  336.     /** Extra, user supplied pax headers */
  337.     private final Map<String, String> extraPaxHeaders = new HashMap<>();

  338.     private long dataOffset = OFFSET_UNKNOWN;

  339.     /**
  340.      * Constructs an empty entry and prepares the header values.
  341.      */
  342.     private TarArchiveEntry(final boolean preserveAbsolutePath) {
  343.         String user = System.getProperty("user.name", "");
  344.         if (user.length() > MAX_NAMELEN) {
  345.             user = user.substring(0, MAX_NAMELEN);
  346.         }
  347.         this.userName = user;
  348.         this.file = null;
  349.         this.linkOptions = IOUtils.EMPTY_LINK_OPTIONS;
  350.         this.preserveAbsolutePath = preserveAbsolutePath;
  351.     }

  352.     /**
  353.      * Constructs an entry from an archive's header bytes. File is set to null.
  354.      *
  355.      * @param headerBuf The header bytes from a tar archive entry.
  356.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  357.      */
  358.     public TarArchiveEntry(final byte[] headerBuf) {
  359.         this(false);
  360.         parseTarHeader(headerBuf);
  361.     }

  362.     /**
  363.      * Constructs an entry from an archive's header bytes. File is set to null.
  364.      *
  365.      * @param headerBuf The header bytes from a tar archive entry.
  366.      * @param encoding  encoding to use for file names
  367.      * @since 1.4
  368.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  369.      * @throws IOException              on error
  370.      */
  371.     public TarArchiveEntry(final byte[] headerBuf, final ZipEncoding encoding) throws IOException {
  372.         this(headerBuf, encoding, false);
  373.     }

  374.     /**
  375.      * Constructs an entry from an archive's header bytes. File is set to null.
  376.      *
  377.      * @param headerBuf The header bytes from a tar archive entry.
  378.      * @param encoding  encoding to use for file names
  379.      * @param lenient   when set to true illegal values for group/userid, mode, device numbers and timestamp will be ignored and the fields set to
  380.      *                  {@link #UNKNOWN}. When set to false such illegal fields cause an exception instead.
  381.      * @since 1.19
  382.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  383.      * @throws IOException              on error
  384.      */
  385.     public TarArchiveEntry(final byte[] headerBuf, final ZipEncoding encoding, final boolean lenient) throws IOException {
  386.         this(Collections.emptyMap(), headerBuf, encoding, lenient);
  387.     }

  388.     /**
  389.      * Constructs an entry from an archive's header bytes for random access tar. File is set to null.
  390.      *
  391.      * @param headerBuf  the header bytes from a tar archive entry.
  392.      * @param encoding   encoding to use for file names.
  393.      * @param lenient    when set to true illegal values for group/userid, mode, device numbers and timestamp will be ignored and the fields set to
  394.      *                   {@link #UNKNOWN}. When set to false such illegal fields cause an exception instead.
  395.      * @param dataOffset position of the entry data in the random access file.
  396.      * @since 1.21
  397.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format.
  398.      * @throws IOException              on error.
  399.      */
  400.     public TarArchiveEntry(final byte[] headerBuf, final ZipEncoding encoding, final boolean lenient, final long dataOffset) throws IOException {
  401.         this(headerBuf, encoding, lenient);
  402.         setDataOffset(dataOffset);
  403.     }

  404.     /**
  405.      * Constructs an entry for a file. File is set to file, and the header is constructed from information from the file. The name is set from the normalized
  406.      * file path.
  407.      * <p>
  408.      * The entry's name will be the value of the {@code file}'s path with all file separators replaced by forward slashes and leading slashes as well as Windows
  409.      * drive letters stripped. The name will end in a slash if the {@code file} represents a directory.
  410.      * </p>
  411.      * <p>
  412.      * Note: Since 1.21 this internally uses the same code as the TarArchiveEntry constructors with a {@link Path} as parameter. But all thrown exceptions are
  413.      * ignored. If handling those exceptions is needed consider switching to the path constructors.
  414.      * </p>
  415.      *
  416.      * @param file The file that the entry represents.
  417.      */
  418.     public TarArchiveEntry(final File file) {
  419.         this(file, file.getPath());
  420.     }

  421.     /**
  422.      * Constructs an entry for a file. File is set to file, and the header is constructed from information from the file.
  423.      * <p>
  424.      * The entry's name will be the value of the {@code fileName} argument with all file separators replaced by forward slashes and leading slashes as well as
  425.      * Windows drive letters stripped. The name will end in a slash if the {@code file} represents a directory.
  426.      * </p>
  427.      * <p>
  428.      * Note: Since 1.21 this internally uses the same code as the TarArchiveEntry constructors with a {@link Path} as parameter. But all thrown exceptions are
  429.      * ignored. If handling those exceptions is needed consider switching to the path constructors.
  430.      * </p>
  431.      *
  432.      * @param file     The file that the entry represents.
  433.      * @param fileName the name to be used for the entry.
  434.      */
  435.     public TarArchiveEntry(final File file, final String fileName) {
  436.         final String normalizedName = normalizeFileName(fileName, false);
  437.         this.file = file.toPath();
  438.         this.linkOptions = IOUtils.EMPTY_LINK_OPTIONS;
  439.         try {
  440.             readFileMode(this.file, normalizedName);
  441.         } catch (final IOException e) {
  442.             // Ignore exceptions from NIO for backwards compatibility
  443.             // Fallback to get size of file if it's no directory to the old file api
  444.             if (!file.isDirectory()) {
  445.                 this.size = file.length();
  446.             }
  447.         }
  448.         this.userName = "";
  449.         try {
  450.             readOsSpecificProperties(this.file);
  451.         } catch (final IOException e) {
  452.             // Ignore exceptions from NIO for backwards compatibility
  453.             // Fallback to get the last modified date of the file from the old file api
  454.             this.mTime = FileTime.fromMillis(file.lastModified());
  455.         }
  456.         preserveAbsolutePath = false;
  457.     }

  458.     /**
  459.      * Constructs an entry from an archive's header bytes. File is set to null.
  460.      *
  461.      * @param globalPaxHeaders the parsed global PAX headers, or null if this is the first one.
  462.      * @param headerBuf        The header bytes from a tar archive entry.
  463.      * @param encoding         encoding to use for file names
  464.      * @param lenient          when set to true illegal values for group/userid, mode, device numbers and timestamp will be ignored and the fields set to
  465.      *                         {@link #UNKNOWN}. When set to false such illegal fields cause an exception instead.
  466.      * @since 1.22
  467.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  468.      * @throws IOException              on error
  469.      */
  470.     public TarArchiveEntry(final Map<String, String> globalPaxHeaders, final byte[] headerBuf, final ZipEncoding encoding, final boolean lenient)
  471.             throws IOException {
  472.         this(false);
  473.         parseTarHeader(globalPaxHeaders, headerBuf, encoding, false, lenient);
  474.     }

  475.     /**
  476.      * Constructs an entry from an archive's header bytes for random access tar. File is set to null.
  477.      *
  478.      * @param globalPaxHeaders the parsed global PAX headers, or null if this is the first one.
  479.      * @param headerBuf        the header bytes from a tar archive entry.
  480.      * @param encoding         encoding to use for file names.
  481.      * @param lenient          when set to true illegal values for group/userid, mode, device numbers and timestamp will be ignored and the fields set to
  482.      *                         {@link #UNKNOWN}. When set to false such illegal fields cause an exception instead.
  483.      * @param dataOffset       position of the entry data in the random access file.
  484.      * @since 1.22
  485.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format.
  486.      * @throws IOException              on error.
  487.      */
  488.     public TarArchiveEntry(final Map<String, String> globalPaxHeaders, final byte[] headerBuf, final ZipEncoding encoding, final boolean lenient,
  489.             final long dataOffset) throws IOException {
  490.         this(globalPaxHeaders, headerBuf, encoding, lenient);
  491.         setDataOffset(dataOffset);
  492.     }

  493.     /**
  494.      * Constructs an entry for a file. File is set to file, and the header is constructed from information from the file. The name is set from the normalized
  495.      * file path.
  496.      * <p>
  497.      * The entry's name will be the value of the {@code file}'s path with all file separators replaced by forward slashes and leading slashes as well as Windows
  498.      * drive letters stripped. The name will end in a slash if the {@code file} represents a directory.
  499.      * </p>
  500.      *
  501.      * @param file The file that the entry represents.
  502.      * @throws IOException if an I/O error occurs
  503.      * @since 1.21
  504.      */
  505.     public TarArchiveEntry(final Path file) throws IOException {
  506.         this(file, file.toString());
  507.     }

  508.     /**
  509.      * Constructs an entry for a file. File is set to file, and the header is constructed from information from the file.
  510.      * <p>
  511.      * The entry's name will be the value of the {@code fileName} argument with all file separators replaced by forward slashes and leading slashes as well as
  512.      * Windows drive letters stripped. The name will end in a slash if the {@code file} represents a directory.
  513.      * </p>
  514.      *
  515.      * @param file        The file that the entry represents.
  516.      * @param fileName    the name to be used for the entry.
  517.      * @param linkOptions options indicating how symbolic links are handled.
  518.      * @throws IOException if an I/O error occurs
  519.      * @since 1.21
  520.      */
  521.     public TarArchiveEntry(final Path file, final String fileName, final LinkOption... linkOptions) throws IOException {
  522.         final String normalizedName = normalizeFileName(fileName, false);
  523.         this.file = file;
  524.         this.linkOptions = linkOptions == null ? IOUtils.EMPTY_LINK_OPTIONS : linkOptions;
  525.         readFileMode(file, normalizedName, linkOptions);
  526.         this.userName = "";
  527.         readOsSpecificProperties(file);
  528.         preserveAbsolutePath = false;
  529.     }

  530.     /**
  531.      * Constructs an entry with only a name. This allows the programmer to construct the entry's header "by hand". File is set to null.
  532.      * <p>
  533.      * The entry's name will be the value of the {@code name} argument with all file separators replaced by forward slashes and leading slashes as well as
  534.      * Windows drive letters stripped.
  535.      * </p>
  536.      *
  537.      * @param name the entry name
  538.      */
  539.     public TarArchiveEntry(final String name) {
  540.         this(name, false);
  541.     }

  542.     /**
  543.      * Constructs an entry with only a name. This allows the programmer to construct the entry's header "by hand". File is set to null.
  544.      * <p>
  545.      * The entry's name will be the value of the {@code name} argument with all file separators replaced by forward slashes. Leading slashes and Windows drive
  546.      * letters are stripped if {@code preserveAbsolutePath} is {@code false}.
  547.      * </p>
  548.      *
  549.      * @param name                 the entry name
  550.      * @param preserveAbsolutePath whether to allow leading slashes or drive letters in the name.
  551.      *
  552.      * @since 1.1
  553.      */
  554.     public TarArchiveEntry(String name, final boolean preserveAbsolutePath) {
  555.         this(preserveAbsolutePath);
  556.         name = normalizeFileName(name, preserveAbsolutePath);
  557.         final boolean isDir = name.endsWith("/");
  558.         this.name = name;
  559.         this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
  560.         this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
  561.         this.mTime = FileTime.from(Instant.now());
  562.         this.userName = "";
  563.     }

  564.     /**
  565.      * Constructs an entry with a name and a link flag.
  566.      * <p>
  567.      * The entry's name will be the value of the {@code name} argument with all file separators replaced by forward slashes and leading slashes as well as
  568.      * Windows drive letters stripped.
  569.      * </p>
  570.      *
  571.      * @param name     the entry name
  572.      * @param linkFlag the entry link flag.
  573.      */
  574.     public TarArchiveEntry(final String name, final byte linkFlag) {
  575.         this(name, linkFlag, false);
  576.     }

  577.     /**
  578.      * Constructs an entry with a name and a link flag.
  579.      * <p>
  580.      * The entry's name will be the value of the {@code name} argument with all file separators replaced by forward slashes. Leading slashes and Windows drive
  581.      * letters are stripped if {@code preserveAbsolutePath} is {@code false}.
  582.      * </p>
  583.      *
  584.      * @param name                 the entry name
  585.      * @param linkFlag             the entry link flag.
  586.      * @param preserveAbsolutePath whether to allow leading slashes or drive letters in the name.
  587.      *
  588.      * @since 1.5
  589.      */
  590.     public TarArchiveEntry(final String name, final byte linkFlag, final boolean preserveAbsolutePath) {
  591.         this(name, preserveAbsolutePath);
  592.         this.linkFlag = linkFlag;
  593.         if (linkFlag == LF_GNUTYPE_LONGNAME) {
  594.             magic = MAGIC_GNU;
  595.             version = VERSION_GNU_SPACE;
  596.         }
  597.     }

  598.     /**
  599.      * Adds a PAX header to this entry. If the header corresponds to an existing field in the entry, that field will be set; otherwise the header will be added
  600.      * to the extraPaxHeaders Map
  601.      *
  602.      * @param name  The full name of the header to set.
  603.      * @param value value of header.
  604.      * @since 1.15
  605.      */
  606.     public void addPaxHeader(final String name, final String value) {
  607.         try {
  608.             processPaxHeader(name, value);
  609.         } catch (final IOException ex) {
  610.             throw new IllegalArgumentException("Invalid input", ex);
  611.         }
  612.     }

  613.     /**
  614.      * Clears all extra PAX headers.
  615.      *
  616.      * @since 1.15
  617.      */
  618.     public void clearExtraPaxHeaders() {
  619.         extraPaxHeaders.clear();
  620.     }

  621.     /**
  622.      * Determine if the two entries are equal. Equality is determined by the header names being equal.
  623.      *
  624.      * @param it Entry to be checked for equality.
  625.      * @return True if the entries are equal.
  626.      */
  627.     @Override
  628.     public boolean equals(final Object it) {
  629.         if (it == null || getClass() != it.getClass()) {
  630.             return false;
  631.         }
  632.         return equals((TarArchiveEntry) it);
  633.     }

  634.     /**
  635.      * Determine if the two entries are equal. Equality is determined by the header names being equal.
  636.      *
  637.      * @param it Entry to be checked for equality.
  638.      * @return True if the entries are equal.
  639.      */
  640.     public boolean equals(final TarArchiveEntry it) {
  641.         return it != null && getName().equals(it.getName());
  642.     }

  643.     /**
  644.      * Evaluates an entry's header format from a header buffer.
  645.      *
  646.      * @param header The tar entry header buffer to evaluate the format for.
  647.      * @return format type
  648.      */
  649.     private int evaluateType(final Map<String, String> globalPaxHeaders, final byte[] header) {
  650.         if (ArchiveUtils.matchAsciiBuffer(MAGIC_GNU, header, MAGIC_OFFSET, MAGICLEN)) {
  651.             return FORMAT_OLDGNU;
  652.         }
  653.         if (ArchiveUtils.matchAsciiBuffer(MAGIC_POSIX, header, MAGIC_OFFSET, MAGICLEN)) {
  654.             if (isXstar(globalPaxHeaders, header)) {
  655.                 return FORMAT_XSTAR;
  656.             }
  657.             return FORMAT_POSIX;
  658.         }
  659.         return 0;
  660.     }

  661.     private int fill(final byte value, final int offset, final byte[] outbuf, final int length) {
  662.         for (int i = 0; i < length; i++) {
  663.             outbuf[offset + i] = value;
  664.         }
  665.         return offset + length;
  666.     }

  667.     private int fill(final int value, final int offset, final byte[] outbuf, final int length) {
  668.         return fill((byte) value, offset, outbuf, length);
  669.     }

  670.     void fillGNUSparse0xData(final Map<String, String> headers) throws IOException {
  671.         paxGNUSparse = true;
  672.         realSize = ParsingUtils.parseIntValue(headers.get(TarGnuSparseKeys.SIZE));
  673.         if (headers.containsKey(TarGnuSparseKeys.NAME)) {
  674.             // version 0.1
  675.             name = headers.get(TarGnuSparseKeys.NAME);
  676.         }
  677.     }

  678.     void fillGNUSparse1xData(final Map<String, String> headers) throws IOException {
  679.         paxGNUSparse = true;
  680.         paxGNU1XSparse = true;
  681.         if (headers.containsKey(TarGnuSparseKeys.NAME)) {
  682.             name = headers.get(TarGnuSparseKeys.NAME);
  683.         }
  684.         if (headers.containsKey(TarGnuSparseKeys.REALSIZE)) {
  685.             realSize = ParsingUtils.parseIntValue(headers.get(TarGnuSparseKeys.REALSIZE));
  686.         }
  687.     }

  688.     void fillStarSparseData(final Map<String, String> headers) throws IOException {
  689.         starSparse = true;
  690.         if (headers.containsKey("SCHILY.realsize")) {
  691.             realSize = ParsingUtils.parseLongValue(headers.get("SCHILY.realsize"));
  692.         }
  693.     }

  694.     /**
  695.      * Gets this entry's creation time.
  696.      *
  697.      * @since 1.22
  698.      * @return This entry's computed creation time.
  699.      */
  700.     public FileTime getCreationTime() {
  701.         return birthTime;
  702.     }

  703.     /**
  704.      * {@inheritDoc}
  705.      *
  706.      * @since 1.21
  707.      */
  708.     @Override
  709.     public long getDataOffset() {
  710.         return dataOffset;
  711.     }

  712.     /**
  713.      * Gets this entry's major device number.
  714.      *
  715.      * @return This entry's major device number.
  716.      * @since 1.4
  717.      */
  718.     public int getDevMajor() {
  719.         return devMajor;
  720.     }

  721.     /**
  722.      * Gets this entry's minor device number.
  723.      *
  724.      * @return This entry's minor device number.
  725.      * @since 1.4
  726.      */
  727.     public int getDevMinor() {
  728.         return devMinor;
  729.     }

  730.     /**
  731.      * If this entry represents a file, and the file is a directory, return an array of TarEntries for this entry's children.
  732.      * <p>
  733.      * This method is only useful for entries created from a {@code
  734.      * File} or {@code Path} but not for entries read from an archive.
  735.      * </p>
  736.      *
  737.      * @return An array of TarEntry's for this entry's children.
  738.      */
  739.     public TarArchiveEntry[] getDirectoryEntries() {
  740.         if (file == null || !isDirectory()) {
  741.             return EMPTY_TAR_ARCHIVE_ENTRY_ARRAY;
  742.         }
  743.         final List<TarArchiveEntry> entries = new ArrayList<>();
  744.         try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(file)) {
  745.             for (final Path p : dirStream) {
  746.                 entries.add(new TarArchiveEntry(p));
  747.             }
  748.         } catch (final IOException e) {
  749.             return EMPTY_TAR_ARCHIVE_ENTRY_ARRAY;
  750.         }
  751.         return entries.toArray(EMPTY_TAR_ARCHIVE_ENTRY_ARRAY);
  752.     }

  753.     /**
  754.      * Gets named extra PAX header
  755.      *
  756.      * @param name The full name of an extended PAX header to retrieve
  757.      * @return The value of the header, if any.
  758.      * @since 1.15
  759.      */
  760.     public String getExtraPaxHeader(final String name) {
  761.         return extraPaxHeaders.get(name);
  762.     }

  763.     /**
  764.      * Gets extra PAX Headers
  765.      *
  766.      * @return read-only map containing any extra PAX Headers
  767.      * @since 1.15
  768.      */
  769.     public Map<String, String> getExtraPaxHeaders() {
  770.         return Collections.unmodifiableMap(extraPaxHeaders);
  771.     }

  772.     /**
  773.      * Gets this entry's file.
  774.      * <p>
  775.      * This method is only useful for entries created from a {@code
  776.      * File} or {@code Path} but not for entries read from an archive.
  777.      * </p>
  778.      *
  779.      * @return this entry's file or null if the entry was not created from a file.
  780.      */
  781.     public File getFile() {
  782.         if (file == null) {
  783.             return null;
  784.         }
  785.         return file.toFile();
  786.     }

  787.     /**
  788.      * Gets this entry's group id.
  789.      *
  790.      * @return This entry's group id.
  791.      * @deprecated use #getLongGroupId instead as group ids can be bigger than {@link Integer#MAX_VALUE}
  792.      */
  793.     @Deprecated
  794.     public int getGroupId() {
  795.         return (int) (groupId & 0xffffffff);
  796.     }

  797.     /**
  798.      * Gets this entry's group name.
  799.      *
  800.      * @return This entry's group name.
  801.      */
  802.     public String getGroupName() {
  803.         return groupName;
  804.     }

  805.     /**
  806.      * Gets this entry's last access time.
  807.      *
  808.      * @since 1.22
  809.      * @return This entry's last access time.
  810.      */
  811.     public FileTime getLastAccessTime() {
  812.         return aTime;
  813.     }

  814.     /**
  815.      * Gets this entry's modification time. This is equivalent to {@link TarArchiveEntry#getLastModifiedTime()}, but precision is truncated to milliseconds.
  816.      *
  817.      * @return This entry's modification time.
  818.      * @see TarArchiveEntry#getLastModifiedTime()
  819.      */
  820.     @Override
  821.     public Date getLastModifiedDate() {
  822.         return getModTime();
  823.     }

  824.     /**
  825.      * Gets this entry's modification time.
  826.      *
  827.      * @since 1.22
  828.      * @return This entry's modification time.
  829.      */
  830.     public FileTime getLastModifiedTime() {
  831.         return mTime;
  832.     }

  833.     /**
  834.      * Gets this entry's link flag.
  835.      *
  836.      * @return this entry's link flag.
  837.      * @since 1.23
  838.      */
  839.     public byte getLinkFlag() {
  840.         return this.linkFlag;
  841.     }

  842.     /**
  843.      * Gets this entry's link name.
  844.      *
  845.      * @return This entry's link name.
  846.      */
  847.     public String getLinkName() {
  848.         return linkName;
  849.     }

  850.     /**
  851.      * Gets this entry's group id.
  852.      *
  853.      * @since 1.10
  854.      * @return This entry's group id.
  855.      */
  856.     public long getLongGroupId() {
  857.         return groupId;
  858.     }

  859.     /**
  860.      * Gets this entry's user id.
  861.      *
  862.      * @return This entry's user id.
  863.      * @since 1.10
  864.      */
  865.     public long getLongUserId() {
  866.         return userId;
  867.     }

  868.     /**
  869.      * Gets this entry's mode.
  870.      *
  871.      * @return This entry's mode.
  872.      */
  873.     public int getMode() {
  874.         return mode;
  875.     }

  876.     /**
  877.      * Gets this entry's modification time. This is equivalent to {@link TarArchiveEntry#getLastModifiedTime()}, but precision is truncated to milliseconds.
  878.      *
  879.      * @return This entry's modification time.
  880.      * @see TarArchiveEntry#getLastModifiedTime()
  881.      */
  882.     public Date getModTime() {
  883.         final FileTime fileTime = mTime;
  884.         return FileTimes.toDate(fileTime);
  885.     }

  886.     /**
  887.      * Gets this entry's name.
  888.      * <p>
  889.      * This method returns the raw name as it is stored inside of the archive.
  890.      * </p>
  891.      *
  892.      * @return This entry's name.
  893.      */
  894.     @Override
  895.     public String getName() {
  896.         return name;
  897.     }

  898.     /**
  899.      * Gets this entry's sparse headers ordered by offset with all empty sparse sections at the start filtered out.
  900.      *
  901.      * @return immutable list of this entry's sparse headers, never null
  902.      * @since 1.21
  903.      * @throws IOException if the list of sparse headers contains blocks that overlap
  904.      */
  905.     public List<TarArchiveStructSparse> getOrderedSparseHeaders() throws IOException {
  906.         if (sparseHeaders == null || sparseHeaders.isEmpty()) {
  907.             return Collections.emptyList();
  908.         }
  909.         final List<TarArchiveStructSparse> orderedAndFiltered = sparseHeaders.stream().filter(s -> s.getOffset() > 0 || s.getNumbytes() > 0)
  910.                 .sorted(Comparator.comparingLong(TarArchiveStructSparse::getOffset)).collect(Collectors.toList());
  911.         final int numberOfHeaders = orderedAndFiltered.size();
  912.         for (int i = 0; i < numberOfHeaders; i++) {
  913.             final TarArchiveStructSparse str = orderedAndFiltered.get(i);
  914.             if (i + 1 < numberOfHeaders && str.getOffset() + str.getNumbytes() > orderedAndFiltered.get(i + 1).getOffset()) {
  915.                 throw new IOException("Corrupted TAR archive. Sparse blocks for " + getName() + " overlap each other.");
  916.             }
  917.             if (str.getOffset() + str.getNumbytes() < 0) {
  918.                 // integer overflow?
  919.                 throw new IOException("Unreadable TAR archive. Offset and numbytes for sparse block in " + getName() + " too large.");
  920.             }
  921.         }
  922.         if (!orderedAndFiltered.isEmpty()) {
  923.             final TarArchiveStructSparse last = orderedAndFiltered.get(numberOfHeaders - 1);
  924.             if (last.getOffset() + last.getNumbytes() > getRealSize()) {
  925.                 throw new IOException("Corrupted TAR archive. Sparse block extends beyond real size of the entry");
  926.             }
  927.         }
  928.         return orderedAndFiltered;
  929.     }

  930.     /**
  931.      * Gets this entry's file.
  932.      * <p>
  933.      * This method is only useful for entries created from a {@code
  934.      * File} or {@code Path} but not for entries read from an archive.
  935.      * </p>
  936.      *
  937.      * @return this entry's file or null if the entry was not created from a file.
  938.      * @since 1.21
  939.      */
  940.     public Path getPath() {
  941.         return file;
  942.     }

  943.     /**
  944.      * Gets this entry's real file size in case of a sparse file.
  945.      * <p>
  946.      * This is the size a file would take on disk if the entry was expanded.
  947.      * </p>
  948.      * <p>
  949.      * If the file is not a sparse file, return size instead of realSize.
  950.      * </p>
  951.      *
  952.      * @return This entry's real file size, if the file is not a sparse file, return size instead of realSize.
  953.      */
  954.     public long getRealSize() {
  955.         if (!isSparse()) {
  956.             return getSize();
  957.         }
  958.         return realSize;
  959.     }

  960.     /**
  961.      * Gets this entry's file size.
  962.      * <p>
  963.      * This is the size the entry's data uses inside the archive. Usually this is the same as {@link #getRealSize}, but it doesn't take the "holes" into account
  964.      * when the entry represents a sparse file.
  965.      * </p>
  966.      *
  967.      * @return This entry's file size.
  968.      */
  969.     @Override
  970.     public long getSize() {
  971.         return size;
  972.     }

  973.     /**
  974.      * Gets this entry's sparse headers
  975.      *
  976.      * @return This entry's sparse headers
  977.      * @since 1.20
  978.      */
  979.     public List<TarArchiveStructSparse> getSparseHeaders() {
  980.         return sparseHeaders;
  981.     }

  982.     /**
  983.      * Gets this entry's status change time.
  984.      *
  985.      * @since 1.22
  986.      * @return This entry's status change time.
  987.      */
  988.     public FileTime getStatusChangeTime() {
  989.         return cTime;
  990.     }

  991.     /**
  992.      * Gets this entry's user id.
  993.      *
  994.      * @return This entry's user id.
  995.      * @deprecated use #getLongUserId instead as user ids can be bigger than {@link Integer#MAX_VALUE}
  996.      */
  997.     @Deprecated
  998.     public int getUserId() {
  999.         return (int) (userId & 0xffffffff);
  1000.     }

  1001.     /**
  1002.      * Gets this entry's user name.
  1003.      *
  1004.      * @return This entry's user name.
  1005.      */
  1006.     public String getUserName() {
  1007.         return userName;
  1008.     }

  1009.     /**
  1010.      * Hash codes are based on entry names.
  1011.      *
  1012.      * @return the entry hash code
  1013.      */
  1014.     @Override
  1015.     public int hashCode() {
  1016.         return getName().hashCode();
  1017.     }

  1018.     /**
  1019.      * Tests whether this is a block device entry.
  1020.      *
  1021.      * @since 1.2
  1022.      * @return whether this is a block device
  1023.      */
  1024.     public boolean isBlockDevice() {
  1025.         return linkFlag == LF_BLK;
  1026.     }

  1027.     /**
  1028.      * Tests whether this is a character device entry.
  1029.      *
  1030.      * @since 1.2
  1031.      * @return whether this is a character device
  1032.      */
  1033.     public boolean isCharacterDevice() {
  1034.         return linkFlag == LF_CHR;
  1035.     }

  1036.     /**
  1037.      * Tests whether this entry's checksum status.
  1038.      *
  1039.      * @return if the header checksum is reasonably correct
  1040.      * @see TarUtils#verifyCheckSum(byte[])
  1041.      * @since 1.5
  1042.      */
  1043.     public boolean isCheckSumOK() {
  1044.         return checkSumOK;
  1045.     }

  1046.     /**
  1047.      * Tests whether the given entry is a descendant of this entry. Descendancy is determined by the name of the descendant starting with this entry's name.
  1048.      *
  1049.      * @param desc Entry to be checked as a descendent of this.
  1050.      * @return True if entry is a descendant of this.
  1051.      */
  1052.     public boolean isDescendent(final TarArchiveEntry desc) {
  1053.         return desc.getName().startsWith(getName());
  1054.     }

  1055.     /**
  1056.      * Tests whether or not this entry represents a directory.
  1057.      *
  1058.      * @return True if this entry is a directory.
  1059.      */
  1060.     @Override
  1061.     public boolean isDirectory() {
  1062.         if (file != null) {
  1063.             return Files.isDirectory(file, linkOptions);
  1064.         }
  1065.         if (linkFlag == LF_DIR) {
  1066.             return true;
  1067.         }
  1068.         return !isPaxHeader() && !isGlobalPaxHeader() && getName().endsWith("/");
  1069.     }

  1070.     /**
  1071.      * Tests whether in case of an oldgnu sparse file if an extension sparse header follows.
  1072.      *
  1073.      * @return true if an extension oldgnu sparse header follows.
  1074.      */
  1075.     public boolean isExtended() {
  1076.         return isExtended;
  1077.     }

  1078.     /**
  1079.      * Tests whether this is a FIFO (pipe) entry.
  1080.      *
  1081.      * @since 1.2
  1082.      * @return whether this is a FIFO entry
  1083.      */
  1084.     public boolean isFIFO() {
  1085.         return linkFlag == LF_FIFO;
  1086.     }

  1087.     /**
  1088.      * Tests whether this is a "normal file"
  1089.      *
  1090.      * @since 1.2
  1091.      * @return whether this is a "normal file"
  1092.      */
  1093.     public boolean isFile() {
  1094.         if (file != null) {
  1095.             return Files.isRegularFile(file, linkOptions);
  1096.         }
  1097.         if (linkFlag == LF_OLDNORM || linkFlag == LF_NORMAL) {
  1098.             return true;
  1099.         }
  1100.         return linkFlag != LF_DIR && !getName().endsWith("/");
  1101.     }

  1102.     /**
  1103.      * Tests whether this is a Pax header.
  1104.      *
  1105.      * @return {@code true} if this is a Pax header.
  1106.      *
  1107.      * @since 1.1
  1108.      */
  1109.     public boolean isGlobalPaxHeader() {
  1110.         return linkFlag == LF_PAX_GLOBAL_EXTENDED_HEADER;
  1111.     }

  1112.     /**
  1113.      * Tests whether this entry is a GNU long linkname block
  1114.      *
  1115.      * @return true if this is a long name extension provided by GNU tar
  1116.      */
  1117.     public boolean isGNULongLinkEntry() {
  1118.         return linkFlag == LF_GNUTYPE_LONGLINK;
  1119.     }

  1120.     /**
  1121.      * Tests whether this entry is a GNU long name block
  1122.      *
  1123.      * @return true if this is a long name extension provided by GNU tar
  1124.      */
  1125.     public boolean isGNULongNameEntry() {
  1126.         return linkFlag == LF_GNUTYPE_LONGNAME;
  1127.     }

  1128.     /**
  1129.      * Tests whether this entry is a GNU sparse block.
  1130.      *
  1131.      * @return true if this is a sparse extension provided by GNU tar
  1132.      */
  1133.     public boolean isGNUSparse() {
  1134.         return isOldGNUSparse() || isPaxGNUSparse();
  1135.     }

  1136.     private boolean isInvalidPrefix(final byte[] header) {
  1137.         // prefix[130] is guaranteed to be '\0' with XSTAR/XUSTAR
  1138.         if (header[XSTAR_PREFIX_OFFSET + 130] != 0) {
  1139.             // except when typeflag is 'M'
  1140.             if (header[LF_OFFSET] != LF_MULTIVOLUME) {
  1141.                 return true;
  1142.             }
  1143.             // We come only here if we try to read in a GNU/xstar/xustar multivolume archive starting past volume #0
  1144.             // As of 1.22, commons-compress does not support multivolume tar archives.
  1145.             // If/when it does, this should work as intended.
  1146.             if ((header[XSTAR_MULTIVOLUME_OFFSET] & 0x80) == 0 && header[XSTAR_MULTIVOLUME_OFFSET + 11] != ' ') {
  1147.                 return true;
  1148.             }
  1149.         }
  1150.         return false;
  1151.     }

  1152.     private boolean isInvalidXtarTime(final byte[] buffer, final int offset, final int length) {
  1153.         // If atime[0]...atime[10] or ctime[0]...ctime[10] is not a POSIX octal number it cannot be 'xstar'.
  1154.         if ((buffer[offset] & 0x80) == 0) {
  1155.             final int lastIndex = length - 1;
  1156.             for (int i = 0; i < lastIndex; i++) {
  1157.                 final byte b = buffer[offset + i];
  1158.                 if (b < '0' || b > '7') {
  1159.                     return true;
  1160.                 }
  1161.             }
  1162.             // Check for both POSIX compliant end of number characters if not using base 256
  1163.             final byte b = buffer[offset + lastIndex];
  1164.             if (b != ' ' && b != 0) {
  1165.                 return true;
  1166.             }
  1167.         }
  1168.         return false;
  1169.     }

  1170.     /**
  1171.      * Tests whether this is a link entry.
  1172.      *
  1173.      * @since 1.2
  1174.      * @return whether this is a link entry
  1175.      */
  1176.     public boolean isLink() {
  1177.         return linkFlag == LF_LINK;
  1178.     }

  1179.     /**
  1180.      * Tests whether this entry is a GNU or star sparse block using the oldgnu format.
  1181.      *
  1182.      * @return true if this is a sparse extension provided by GNU tar or star
  1183.      * @since 1.11
  1184.      */
  1185.     public boolean isOldGNUSparse() {
  1186.         return linkFlag == LF_GNUTYPE_SPARSE;
  1187.     }

  1188.     /**
  1189.      * Tests whether this entry is a sparse file with 1.X PAX Format or not
  1190.      *
  1191.      * @return True if this entry is a sparse file with 1.X PAX Format
  1192.      * @since 1.20
  1193.      */
  1194.     public boolean isPaxGNU1XSparse() {
  1195.         return paxGNU1XSparse;
  1196.     }

  1197.     /**
  1198.      * Tests whether this entry is a GNU sparse block using one of the PAX formats.
  1199.      *
  1200.      * @return true if this is a sparse extension provided by GNU tar
  1201.      * @since 1.11
  1202.      */
  1203.     public boolean isPaxGNUSparse() {
  1204.         return paxGNUSparse;
  1205.     }

  1206.     /**
  1207.      * Tests whether this is a Pax header.
  1208.      *
  1209.      * @return {@code true} if this is a Pax header.
  1210.      *
  1211.      * @since 1.1
  1212.      */
  1213.     public boolean isPaxHeader() {
  1214.         return linkFlag == LF_PAX_EXTENDED_HEADER_LC || linkFlag == LF_PAX_EXTENDED_HEADER_UC;
  1215.     }

  1216.     /**
  1217.      * Tests whether this is a sparse entry.
  1218.      *
  1219.      * @return whether this is a sparse entry
  1220.      * @since 1.11
  1221.      */
  1222.     public boolean isSparse() {
  1223.         return isGNUSparse() || isStarSparse();
  1224.     }

  1225.     /**
  1226.      * Tests whether this entry is a star sparse block using PAX headers.
  1227.      *
  1228.      * @return true if this is a sparse extension provided by star
  1229.      * @since 1.11
  1230.      */
  1231.     public boolean isStarSparse() {
  1232.         return starSparse;
  1233.     }

  1234.     /**
  1235.      * {@inheritDoc}
  1236.      *
  1237.      * @since 1.21
  1238.      */
  1239.     @Override
  1240.     public boolean isStreamContiguous() {
  1241.         return true;
  1242.     }

  1243.     /**
  1244.      * Tests whether this is a symbolic link entry.
  1245.      *
  1246.      * @since 1.2
  1247.      * @return whether this is a symbolic link
  1248.      */
  1249.     public boolean isSymbolicLink() {
  1250.         return linkFlag == LF_SYMLINK;
  1251.     }

  1252.     /**
  1253.      * Tests whether the given header is in XSTAR / XUSTAR format.
  1254.      *
  1255.      * Use the same logic found in star version 1.6 in {@code header.c}, function {@code isxmagic(TCB *ptb)}.
  1256.      */
  1257.     private boolean isXstar(final Map<String, String> globalPaxHeaders, final byte[] header) {
  1258.         // Check if this is XSTAR
  1259.         if (ArchiveUtils.matchAsciiBuffer(MAGIC_XSTAR, header, XSTAR_MAGIC_OFFSET, XSTAR_MAGIC_LEN)) {
  1260.             return true;
  1261.         }
  1262.         //
  1263.         // If SCHILY.archtype is present in the global PAX header, we can use it to identify the type of archive.
  1264.         //
  1265.         // Possible values for XSTAR: - xustar: 'xstar' format without "tar" signature at header offset 508. - exustar: 'xustar' format variant that always
  1266.         // includes x-headers and g-headers.
  1267.         //
  1268.         final String archType = globalPaxHeaders.get("SCHILY.archtype");
  1269.         if (archType != null) {
  1270.             return "xustar".equals(archType) || "exustar".equals(archType);
  1271.         }
  1272.         // Check if this is XUSTAR
  1273.         if (isInvalidPrefix(header)) {
  1274.             return false;
  1275.         }
  1276.         if (isInvalidXtarTime(header, XSTAR_ATIME_OFFSET, ATIMELEN_XSTAR)) {
  1277.             return false;
  1278.         }
  1279.         if (isInvalidXtarTime(header, XSTAR_CTIME_OFFSET, CTIMELEN_XSTAR)) {
  1280.             return false;
  1281.         }
  1282.         return true;
  1283.     }

  1284.     private long parseOctalOrBinary(final byte[] header, final int offset, final int length, final boolean lenient) {
  1285.         if (lenient) {
  1286.             try {
  1287.                 return TarUtils.parseOctalOrBinary(header, offset, length);
  1288.             } catch (final IllegalArgumentException ex) { // NOSONAR
  1289.                 return UNKNOWN;
  1290.             }
  1291.         }
  1292.         return TarUtils.parseOctalOrBinary(header, offset, length);
  1293.     }

  1294.     /**
  1295.      * Parses an entry's header information from a header buffer.
  1296.      *
  1297.      * @param header The tar entry header buffer to get information from.
  1298.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  1299.      */
  1300.     public void parseTarHeader(final byte[] header) {
  1301.         try {
  1302.             parseTarHeader(header, TarUtils.DEFAULT_ENCODING);
  1303.         } catch (final IOException ex) { // NOSONAR
  1304.             try {
  1305.                 parseTarHeader(header, TarUtils.DEFAULT_ENCODING, true, false);
  1306.             } catch (final IOException ex2) {
  1307.                 // not really possible
  1308.                 throw new UncheckedIOException(ex2); // NOSONAR
  1309.             }
  1310.         }
  1311.     }

  1312.     /**
  1313.      * Parse an entry's header information from a header buffer.
  1314.      *
  1315.      * @param header   The tar entry header buffer to get information from.
  1316.      * @param encoding encoding to use for file names
  1317.      * @since 1.4
  1318.      * @throws IllegalArgumentException if any of the numeric fields have an invalid format
  1319.      * @throws IOException              on error
  1320.      */
  1321.     public void parseTarHeader(final byte[] header, final ZipEncoding encoding) throws IOException {
  1322.         parseTarHeader(header, encoding, false, false);
  1323.     }

  1324.     private void parseTarHeader(final byte[] header, final ZipEncoding encoding, final boolean oldStyle, final boolean lenient) throws IOException {
  1325.         parseTarHeader(Collections.emptyMap(), header, encoding, oldStyle, lenient);
  1326.     }

  1327.     private void parseTarHeader(final Map<String, String> globalPaxHeaders, final byte[] header, final ZipEncoding encoding, final boolean oldStyle,
  1328.             final boolean lenient) throws IOException {
  1329.         try {
  1330.             parseTarHeaderUnwrapped(globalPaxHeaders, header, encoding, oldStyle, lenient);
  1331.         } catch (final IllegalArgumentException ex) {
  1332.             throw new IOException("Corrupted TAR archive.", ex);
  1333.         }
  1334.     }

  1335.     private void parseTarHeaderUnwrapped(final Map<String, String> globalPaxHeaders, final byte[] header, final ZipEncoding encoding, final boolean oldStyle,
  1336.             final boolean lenient) throws IOException {
  1337.         int offset = 0;
  1338.         name = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) : TarUtils.parseName(header, offset, NAMELEN, encoding);
  1339.         offset += NAMELEN;
  1340.         mode = (int) parseOctalOrBinary(header, offset, MODELEN, lenient);
  1341.         offset += MODELEN;
  1342.         userId = (int) parseOctalOrBinary(header, offset, UIDLEN, lenient);
  1343.         offset += UIDLEN;
  1344.         groupId = (int) parseOctalOrBinary(header, offset, GIDLEN, lenient);
  1345.         offset += GIDLEN;
  1346.         size = TarUtils.parseOctalOrBinary(header, offset, SIZELEN);
  1347.         if (size < 0) {
  1348.             throw new IOException("broken archive, entry with negative size");
  1349.         }
  1350.         offset += SIZELEN;
  1351.         mTime = FileTimes.fromUnixTime(parseOctalOrBinary(header, offset, MODTIMELEN, lenient));
  1352.         offset += MODTIMELEN;
  1353.         checkSumOK = TarUtils.verifyCheckSum(header);
  1354.         offset += CHKSUMLEN;
  1355.         linkFlag = header[offset++];
  1356.         linkName = oldStyle ? TarUtils.parseName(header, offset, NAMELEN) : TarUtils.parseName(header, offset, NAMELEN, encoding);
  1357.         offset += NAMELEN;
  1358.         magic = TarUtils.parseName(header, offset, MAGICLEN);
  1359.         offset += MAGICLEN;
  1360.         version = TarUtils.parseName(header, offset, VERSIONLEN);
  1361.         offset += VERSIONLEN;
  1362.         userName = oldStyle ? TarUtils.parseName(header, offset, UNAMELEN) : TarUtils.parseName(header, offset, UNAMELEN, encoding);
  1363.         offset += UNAMELEN;
  1364.         groupName = oldStyle ? TarUtils.parseName(header, offset, GNAMELEN) : TarUtils.parseName(header, offset, GNAMELEN, encoding);
  1365.         offset += GNAMELEN;
  1366.         if (linkFlag == LF_CHR || linkFlag == LF_BLK) {
  1367.             devMajor = (int) parseOctalOrBinary(header, offset, DEVLEN, lenient);
  1368.             offset += DEVLEN;
  1369.             devMinor = (int) parseOctalOrBinary(header, offset, DEVLEN, lenient);
  1370.             offset += DEVLEN;
  1371.         } else {
  1372.             offset += 2 * DEVLEN;
  1373.         }
  1374.         final int type = evaluateType(globalPaxHeaders, header);
  1375.         switch (type) {
  1376.         case FORMAT_OLDGNU: {
  1377.             aTime = fileTimeFromOptionalSeconds(parseOctalOrBinary(header, offset, ATIMELEN_GNU, lenient));
  1378.             offset += ATIMELEN_GNU;
  1379.             cTime = fileTimeFromOptionalSeconds(parseOctalOrBinary(header, offset, CTIMELEN_GNU, lenient));
  1380.             offset += CTIMELEN_GNU;
  1381.             offset += OFFSETLEN_GNU;
  1382.             offset += LONGNAMESLEN_GNU;
  1383.             offset += PAD2LEN_GNU;
  1384.             sparseHeaders = new ArrayList<>(TarUtils.readSparseStructs(header, offset, SPARSE_HEADERS_IN_OLDGNU_HEADER));
  1385.             offset += SPARSELEN_GNU;
  1386.             isExtended = TarUtils.parseBoolean(header, offset);
  1387.             offset += ISEXTENDEDLEN_GNU;
  1388.             realSize = TarUtils.parseOctal(header, offset, REALSIZELEN_GNU);
  1389.             offset += REALSIZELEN_GNU; // NOSONAR - assignment as documentation
  1390.             break;
  1391.         }
  1392.         case FORMAT_XSTAR: {
  1393.             final String xstarPrefix = oldStyle ? TarUtils.parseName(header, offset, PREFIXLEN_XSTAR)
  1394.                     : TarUtils.parseName(header, offset, PREFIXLEN_XSTAR, encoding);
  1395.             offset += PREFIXLEN_XSTAR;
  1396.             if (!xstarPrefix.isEmpty()) {
  1397.                 name = xstarPrefix + "/" + name;
  1398.             }
  1399.             aTime = fileTimeFromOptionalSeconds(parseOctalOrBinary(header, offset, ATIMELEN_XSTAR, lenient));
  1400.             offset += ATIMELEN_XSTAR;
  1401.             cTime = fileTimeFromOptionalSeconds(parseOctalOrBinary(header, offset, CTIMELEN_XSTAR, lenient));
  1402.             offset += CTIMELEN_XSTAR; // NOSONAR - assignment as documentation
  1403.             break;
  1404.         }
  1405.         case FORMAT_POSIX:
  1406.         default: {
  1407.             final String prefix = oldStyle ? TarUtils.parseName(header, offset, PREFIXLEN) : TarUtils.parseName(header, offset, PREFIXLEN, encoding);
  1408.             offset += PREFIXLEN; // NOSONAR - assignment as documentation
  1409.             // SunOS tar -E does not add / to directory names, so fix
  1410.             // up to be consistent
  1411.             if (isDirectory() && !name.endsWith("/")) {
  1412.                 name += "/";
  1413.             }
  1414.             if (!prefix.isEmpty()) {
  1415.                 name = prefix + "/" + name;
  1416.             }
  1417.         }
  1418.         }
  1419.     }

  1420.     /**
  1421.      * Processes one pax header, using the entries extraPaxHeaders map as source for extra headers used when handling entries for sparse files.
  1422.      *
  1423.      * @param key
  1424.      * @param val
  1425.      * @since 1.15
  1426.      */
  1427.     private void processPaxHeader(final String key, final String val) throws IOException {
  1428.         processPaxHeader(key, val, extraPaxHeaders);
  1429.     }

  1430.     /**
  1431.      * Processes one pax header, using the supplied map as source for extra headers to be used when handling entries for sparse files
  1432.      *
  1433.      * @param key     the header name.
  1434.      * @param val     the header value.
  1435.      * @param headers map of headers used for dealing with sparse file.
  1436.      * @throws NumberFormatException if encountered errors when parsing the numbers
  1437.      * @since 1.15
  1438.      */
  1439.     private void processPaxHeader(final String key, final String val, final Map<String, String> headers) throws IOException {
  1440.         /*
  1441.          * The following headers are defined for Pax. charset: cannot use these without changing TarArchiveEntry fields mtime atime ctime
  1442.          * LIBARCHIVE.creationtime comment gid, gname linkpath size uid,uname SCHILY.devminor, SCHILY.devmajor: don't have setters/getters for those
  1443.          *
  1444.          * GNU sparse files use additional members, we use GNU.sparse.size to detect the 0.0 and 0.1 versions and GNU.sparse.realsize for 1.0.
  1445.          *
  1446.          * star files use additional members of which we use SCHILY.filetype in order to detect star sparse files.
  1447.          *
  1448.          * If called from addExtraPaxHeader, these additional headers must be already present .
  1449.          */
  1450.         switch (key) {
  1451.         case "path":
  1452.             setName(val);
  1453.             break;
  1454.         case "linkpath":
  1455.             setLinkName(val);
  1456.             break;
  1457.         case "gid":
  1458.             setGroupId(ParsingUtils.parseLongValue(val));
  1459.             break;
  1460.         case "gname":
  1461.             setGroupName(val);
  1462.             break;
  1463.         case "uid":
  1464.             setUserId(ParsingUtils.parseLongValue(val));
  1465.             break;
  1466.         case "uname":
  1467.             setUserName(val);
  1468.             break;
  1469.         case "size":
  1470.             final long size = ParsingUtils.parseLongValue(val);
  1471.             if (size < 0) {
  1472.                 throw new IOException("Corrupted TAR archive. Entry size is negative");
  1473.             }
  1474.             setSize(size);
  1475.             break;
  1476.         case "mtime":
  1477.             setLastModifiedTime(FileTime.from(parseInstantFromDecimalSeconds(val)));
  1478.             break;
  1479.         case "atime":
  1480.             setLastAccessTime(FileTime.from(parseInstantFromDecimalSeconds(val)));
  1481.             break;
  1482.         case "ctime":
  1483.             setStatusChangeTime(FileTime.from(parseInstantFromDecimalSeconds(val)));
  1484.             break;
  1485.         case "LIBARCHIVE.creationtime":
  1486.             setCreationTime(FileTime.from(parseInstantFromDecimalSeconds(val)));
  1487.             break;
  1488.         case "SCHILY.devminor":
  1489.             final int devMinor = ParsingUtils.parseIntValue(val);
  1490.             if (devMinor < 0) {
  1491.                 throw new IOException("Corrupted TAR archive. Dev-Minor is negative");
  1492.             }
  1493.             setDevMinor(devMinor);
  1494.             break;
  1495.         case "SCHILY.devmajor":
  1496.             final int devMajor = ParsingUtils.parseIntValue(val);
  1497.             if (devMajor < 0) {
  1498.                 throw new IOException("Corrupted TAR archive. Dev-Major is negative");
  1499.             }
  1500.             setDevMajor(devMajor);
  1501.             break;
  1502.         case TarGnuSparseKeys.SIZE:
  1503.             fillGNUSparse0xData(headers);
  1504.             break;
  1505.         case TarGnuSparseKeys.REALSIZE:
  1506.             fillGNUSparse1xData(headers);
  1507.             break;
  1508.         case "SCHILY.filetype":
  1509.             if ("sparse".equals(val)) {
  1510.                 fillStarSparseData(headers);
  1511.             }
  1512.             break;
  1513.         default:
  1514.             extraPaxHeaders.put(key, val);
  1515.         }
  1516.     }

  1517.     private void readFileMode(final Path file, final String normalizedName, final LinkOption... options) throws IOException {
  1518.         if (Files.isDirectory(file, options)) {
  1519.             this.mode = DEFAULT_DIR_MODE;
  1520.             this.linkFlag = LF_DIR;

  1521.             final int nameLength = normalizedName.length();
  1522.             if (nameLength == 0 || normalizedName.charAt(nameLength - 1) != '/') {
  1523.                 this.name = normalizedName + "/";
  1524.             } else {
  1525.                 this.name = normalizedName;
  1526.             }
  1527.         } else {
  1528.             this.mode = DEFAULT_FILE_MODE;
  1529.             this.linkFlag = LF_NORMAL;
  1530.             this.name = normalizedName;
  1531.             this.size = Files.size(file);
  1532.         }
  1533.     }

  1534.     private void readOsSpecificProperties(final Path file, final LinkOption... options) throws IOException {
  1535.         final Set<String> availableAttributeViews = file.getFileSystem().supportedFileAttributeViews();
  1536.         if (availableAttributeViews.contains("posix")) {
  1537.             final PosixFileAttributes posixFileAttributes = Files.readAttributes(file, PosixFileAttributes.class, options);
  1538.             setLastModifiedTime(posixFileAttributes.lastModifiedTime());
  1539.             setCreationTime(posixFileAttributes.creationTime());
  1540.             setLastAccessTime(posixFileAttributes.lastAccessTime());
  1541.             this.userName = posixFileAttributes.owner().getName();
  1542.             this.groupName = posixFileAttributes.group().getName();
  1543.             if (availableAttributeViews.contains("unix")) {
  1544.                 this.userId = ((Number) Files.getAttribute(file, "unix:uid", options)).longValue();
  1545.                 this.groupId = ((Number) Files.getAttribute(file, "unix:gid", options)).longValue();
  1546.                 try {
  1547.                     setStatusChangeTime((FileTime) Files.getAttribute(file, "unix:ctime", options));
  1548.                 } catch (final IllegalArgumentException ignored) {
  1549.                     // ctime is not supported
  1550.                 }
  1551.             }
  1552.         } else {
  1553.             if (availableAttributeViews.contains("dos")) {
  1554.                 final DosFileAttributes dosFileAttributes = Files.readAttributes(file, DosFileAttributes.class, options);
  1555.                 setLastModifiedTime(dosFileAttributes.lastModifiedTime());
  1556.                 setCreationTime(dosFileAttributes.creationTime());
  1557.                 setLastAccessTime(dosFileAttributes.lastAccessTime());
  1558.             } else {
  1559.                 final BasicFileAttributes basicFileAttributes = Files.readAttributes(file, BasicFileAttributes.class, options);
  1560.                 setLastModifiedTime(basicFileAttributes.lastModifiedTime());
  1561.                 setCreationTime(basicFileAttributes.creationTime());
  1562.                 setLastAccessTime(basicFileAttributes.lastAccessTime());
  1563.             }
  1564.             this.userName = Files.getOwner(file, options).getName();
  1565.         }
  1566.     }

  1567.     /**
  1568.      * Sets this entry's creation time.
  1569.      *
  1570.      * @param time This entry's new creation time.
  1571.      * @since 1.22
  1572.      */
  1573.     public void setCreationTime(final FileTime time) {
  1574.         birthTime = time;
  1575.     }

  1576.     /**
  1577.      * Sets the offset of the data for the tar entry.
  1578.      *
  1579.      * @param dataOffset the position of the data in the tar.
  1580.      * @since 1.21
  1581.      */
  1582.     public void setDataOffset(final long dataOffset) {
  1583.         if (dataOffset < 0) {
  1584.             throw new IllegalArgumentException("The offset can not be smaller than 0");
  1585.         }
  1586.         this.dataOffset = dataOffset;
  1587.     }

  1588.     /**
  1589.      * Sets this entry's major device number.
  1590.      *
  1591.      * @param devNo This entry's major device number.
  1592.      * @throws IllegalArgumentException if the devNo is &lt; 0.
  1593.      * @since 1.4
  1594.      */
  1595.     public void setDevMajor(final int devNo) {
  1596.         if (devNo < 0) {
  1597.             throw new IllegalArgumentException("Major device number is out of " + "range: " + devNo);
  1598.         }
  1599.         this.devMajor = devNo;
  1600.     }

  1601.     /**
  1602.      * Sets this entry's minor device number.
  1603.      *
  1604.      * @param devNo This entry's minor device number.
  1605.      * @throws IllegalArgumentException if the devNo is &lt; 0.
  1606.      * @since 1.4
  1607.      */
  1608.     public void setDevMinor(final int devNo) {
  1609.         if (devNo < 0) {
  1610.             throw new IllegalArgumentException("Minor device number is out of " + "range: " + devNo);
  1611.         }
  1612.         this.devMinor = devNo;
  1613.     }

  1614.     /**
  1615.      * Sets this entry's group id.
  1616.      *
  1617.      * @param groupId This entry's new group id.
  1618.      */
  1619.     public void setGroupId(final int groupId) {
  1620.         setGroupId((long) groupId);
  1621.     }

  1622.     /**
  1623.      * Sets this entry's group id.
  1624.      *
  1625.      * @since 1.10
  1626.      * @param groupId This entry's new group id.
  1627.      */
  1628.     public void setGroupId(final long groupId) {
  1629.         this.groupId = groupId;
  1630.     }

  1631.     /**
  1632.      * Sets this entry's group name.
  1633.      *
  1634.      * @param groupName This entry's new group name.
  1635.      */
  1636.     public void setGroupName(final String groupName) {
  1637.         this.groupName = groupName;
  1638.     }

  1639.     /**
  1640.      * Convenience method to set this entry's group and user ids.
  1641.      *
  1642.      * @param userId  This entry's new user id.
  1643.      * @param groupId This entry's new group id.
  1644.      */
  1645.     public void setIds(final int userId, final int groupId) {
  1646.         setUserId(userId);
  1647.         setGroupId(groupId);
  1648.     }

  1649.     /**
  1650.      * Sets this entry's last access time.
  1651.      *
  1652.      * @param time This entry's new last access time.
  1653.      * @since 1.22
  1654.      */
  1655.     public void setLastAccessTime(final FileTime time) {
  1656.         aTime = time;
  1657.     }

  1658.     /**
  1659.      * Sets this entry's modification time.
  1660.      *
  1661.      * @param time This entry's new modification time.
  1662.      * @since 1.22
  1663.      */
  1664.     public void setLastModifiedTime(final FileTime time) {
  1665.         mTime = Objects.requireNonNull(time, "time");
  1666.     }

  1667.     /**
  1668.      * Sets this entry's link name.
  1669.      *
  1670.      * @param link the link name to use.
  1671.      *
  1672.      * @since 1.1
  1673.      */
  1674.     public void setLinkName(final String link) {
  1675.         this.linkName = link;
  1676.     }

  1677.     /**
  1678.      * Sets the mode for this entry
  1679.      *
  1680.      * @param mode the mode for this entry
  1681.      */
  1682.     public void setMode(final int mode) {
  1683.         this.mode = mode;
  1684.     }

  1685.     /**
  1686.      * Sets this entry's modification time.
  1687.      *
  1688.      * @param time This entry's new modification time.
  1689.      * @see TarArchiveEntry#setLastModifiedTime(FileTime)
  1690.      */
  1691.     public void setModTime(final Date time) {
  1692.         setLastModifiedTime(FileTimes.toFileTime(time));
  1693.     }

  1694.     /**
  1695.      * Sets this entry's modification time.
  1696.      *
  1697.      * @param time This entry's new modification time.
  1698.      * @since 1.21
  1699.      * @see TarArchiveEntry#setLastModifiedTime(FileTime)
  1700.      */
  1701.     public void setModTime(final FileTime time) {
  1702.         setLastModifiedTime(time);
  1703.     }

  1704.     /**
  1705.      * Sets this entry's modification time. The parameter passed to this method is in "Java time".
  1706.      *
  1707.      * @param time This entry's new modification time.
  1708.      * @see TarArchiveEntry#setLastModifiedTime(FileTime)
  1709.      */
  1710.     public void setModTime(final long time) {
  1711.         setLastModifiedTime(FileTime.fromMillis(time));
  1712.     }

  1713.     /**
  1714.      * Sets this entry's name.
  1715.      *
  1716.      * @param name This entry's new name.
  1717.      */
  1718.     public void setName(final String name) {
  1719.         this.name = normalizeFileName(name, this.preserveAbsolutePath);
  1720.     }

  1721.     /**
  1722.      * Convenience method to set this entry's group and user names.
  1723.      *
  1724.      * @param userName  This entry's new user name.
  1725.      * @param groupName This entry's new group name.
  1726.      */
  1727.     public void setNames(final String userName, final String groupName) {
  1728.         setUserName(userName);
  1729.         setGroupName(groupName);
  1730.     }

  1731.     /**
  1732.      * Sets this entry's file size.
  1733.      *
  1734.      * @param size This entry's new file size.
  1735.      * @throws IllegalArgumentException if the size is &lt; 0.
  1736.      */
  1737.     public void setSize(final long size) {
  1738.         if (size < 0) {
  1739.             throw new IllegalArgumentException("Size is out of range: " + size);
  1740.         }
  1741.         this.size = size;
  1742.     }

  1743.     /**
  1744.      * Sets this entry's sparse headers
  1745.      *
  1746.      * @param sparseHeaders The new sparse headers
  1747.      * @since 1.20
  1748.      */
  1749.     public void setSparseHeaders(final List<TarArchiveStructSparse> sparseHeaders) {
  1750.         this.sparseHeaders = sparseHeaders;
  1751.     }

  1752.     /**
  1753.      * Sets this entry's status change time.
  1754.      *
  1755.      * @param time This entry's new status change time.
  1756.      * @since 1.22
  1757.      */
  1758.     public void setStatusChangeTime(final FileTime time) {
  1759.         cTime = time;
  1760.     }

  1761.     /**
  1762.      * Sets this entry's user id.
  1763.      *
  1764.      * @param userId This entry's new user id.
  1765.      */
  1766.     public void setUserId(final int userId) {
  1767.         setUserId((long) userId);
  1768.     }

  1769.     /**
  1770.      * Sets this entry's user id.
  1771.      *
  1772.      * @param userId This entry's new user id.
  1773.      * @since 1.10
  1774.      */
  1775.     public void setUserId(final long userId) {
  1776.         this.userId = userId;
  1777.     }

  1778.     /**
  1779.      * Sets this entry's user name.
  1780.      *
  1781.      * @param userName This entry's new user name.
  1782.      */
  1783.     public void setUserName(final String userName) {
  1784.         this.userName = userName;
  1785.     }

  1786.     /**
  1787.      * Update the entry using a map of pax headers.
  1788.      *
  1789.      * @param headers
  1790.      * @since 1.15
  1791.      */
  1792.     void updateEntryFromPaxHeaders(final Map<String, String> headers) throws IOException {
  1793.         for (final Map.Entry<String, String> ent : headers.entrySet()) {
  1794.             processPaxHeader(ent.getKey(), ent.getValue(), headers);
  1795.         }
  1796.     }

  1797.     /**
  1798.      * Writes an entry's header information to a header buffer.
  1799.      * <p>
  1800.      * This method does not use the star/GNU tar/BSD tar extensions.
  1801.      * </p>
  1802.      *
  1803.      * @param outbuf The tar entry header buffer to fill in.
  1804.      */
  1805.     public void writeEntryHeader(final byte[] outbuf) {
  1806.         try {
  1807.             writeEntryHeader(outbuf, TarUtils.DEFAULT_ENCODING, false);
  1808.         } catch (final IOException ex) { // NOSONAR
  1809.             try {
  1810.                 writeEntryHeader(outbuf, TarUtils.FALLBACK_ENCODING, false);
  1811.             } catch (final IOException ex2) {
  1812.                 // impossible
  1813.                 throw new UncheckedIOException(ex2); // NOSONAR
  1814.             }
  1815.         }
  1816.     }

  1817.     /**
  1818.      * Writes an entry's header information to a header buffer.
  1819.      *
  1820.      * @param outbuf   The tar entry header buffer to fill in.
  1821.      * @param encoding encoding to use when writing the file name.
  1822.      * @param starMode whether to use the star/GNU tar/BSD tar extension for numeric fields if their value doesn't fit in the maximum size of standard tar
  1823.      *                 archives
  1824.      * @since 1.4
  1825.      * @throws IOException on error
  1826.      */
  1827.     public void writeEntryHeader(final byte[] outbuf, final ZipEncoding encoding, final boolean starMode) throws IOException {
  1828.         int offset = 0;
  1829.         offset = TarUtils.formatNameBytes(name, outbuf, offset, NAMELEN, encoding);
  1830.         offset = writeEntryHeaderField(mode, outbuf, offset, MODELEN, starMode);
  1831.         offset = writeEntryHeaderField(userId, outbuf, offset, UIDLEN, starMode);
  1832.         offset = writeEntryHeaderField(groupId, outbuf, offset, GIDLEN, starMode);
  1833.         offset = writeEntryHeaderField(size, outbuf, offset, SIZELEN, starMode);
  1834.         offset = writeEntryHeaderField(TimeUtils.toUnixTime(mTime), outbuf, offset, MODTIMELEN, starMode);
  1835.         final int csOffset = offset;
  1836.         offset = fill((byte) ' ', offset, outbuf, CHKSUMLEN);
  1837.         outbuf[offset++] = linkFlag;
  1838.         offset = TarUtils.formatNameBytes(linkName, outbuf, offset, NAMELEN, encoding);
  1839.         offset = TarUtils.formatNameBytes(magic, outbuf, offset, MAGICLEN);
  1840.         offset = TarUtils.formatNameBytes(version, outbuf, offset, VERSIONLEN);
  1841.         offset = TarUtils.formatNameBytes(userName, outbuf, offset, UNAMELEN, encoding);
  1842.         offset = TarUtils.formatNameBytes(groupName, outbuf, offset, GNAMELEN, encoding);
  1843.         offset = writeEntryHeaderField(devMajor, outbuf, offset, DEVLEN, starMode);
  1844.         offset = writeEntryHeaderField(devMinor, outbuf, offset, DEVLEN, starMode);
  1845.         if (starMode) {
  1846.             // skip prefix
  1847.             offset = fill(0, offset, outbuf, PREFIXLEN_XSTAR);
  1848.             offset = writeEntryHeaderOptionalTimeField(aTime, offset, outbuf, ATIMELEN_XSTAR);
  1849.             offset = writeEntryHeaderOptionalTimeField(cTime, offset, outbuf, CTIMELEN_XSTAR);
  1850.             // 8-byte fill
  1851.             offset = fill(0, offset, outbuf, 8);
  1852.             // Do not write MAGIC_XSTAR because it causes issues with some TAR tools
  1853.             // This makes it effectively XUSTAR, which guarantees compatibility with USTAR
  1854.             offset = fill(0, offset, outbuf, XSTAR_MAGIC_LEN);
  1855.         }
  1856.         offset = fill(0, offset, outbuf, outbuf.length - offset); // NOSONAR - assignment as documentation
  1857.         final long chk = TarUtils.computeCheckSum(outbuf);
  1858.         TarUtils.formatCheckSumOctalBytes(chk, outbuf, csOffset, CHKSUMLEN);
  1859.     }

  1860.     private int writeEntryHeaderField(final long value, final byte[] outbuf, final int offset, final int length, final boolean starMode) {
  1861.         if (!starMode && (value < 0 || value >= 1L << 3 * (length - 1))) {
  1862.             // value doesn't fit into field when written as octal
  1863.             // number, will be written to PAX header or causes an
  1864.             // error
  1865.             return TarUtils.formatLongOctalBytes(0, outbuf, offset, length);
  1866.         }
  1867.         return TarUtils.formatLongOctalOrBinaryBytes(value, outbuf, offset, length);
  1868.     }

  1869.     private int writeEntryHeaderOptionalTimeField(final FileTime time, int offset, final byte[] outbuf, final int fieldLength) {
  1870.         if (time != null) {
  1871.             offset = writeEntryHeaderField(TimeUtils.toUnixTime(time), outbuf, offset, fieldLength, true);
  1872.         } else {
  1873.             offset = fill(0, offset, outbuf, fieldLength);
  1874.         }
  1875.         return offset;
  1876.     }

  1877. }