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