    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 *   https://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.dump;
    020
    021import java.util.Collections;
    022import java.util.Date;
    023import java.util.EnumSet;
    024import java.util.HashSet;
    025import java.util.Set;
    026
    027import org.apache.commons.compress.archivers.ArchiveEntry;
    028
    029/**
    030 * This class represents an entry in a Dump archive. It consists of the entry's header, the entry's File and any extended attributes.
    031 * <p>
    032 * DumpEntries that are created from the header bytes read from an archive are instantiated with the DumpArchiveEntry( byte[] ) constructor. These entries will
    033 * be used when extracting from or listing the contents of an archive. These entries have their header filled in using the header bytes. They also set the File
    034 * to null, since they reference an archive entry not a file.
    035 * <p>
    036 * DumpEntries can also be constructed from nothing but a name. This allows the programmer to construct the entry by hand, for instance when only an InputStream
    037 * is available for writing to the archive, and the header information is constructed from other information. In this case the header fields are set to defaults
    038 * and the File is set to null.
    039 *
    040 * <p>
    041 * The C structure for a Dump Entry's header is:
    042 *
    043 * <pre>
    044 * #define TP_BSIZE    1024          // size of each file block
    045 * #define NTREC       10            // number of blocks to write at once
    046 * #define HIGHDENSITYTREC 32        // number of blocks to write on high-density tapes
    047 * #define TP_NINDIR   (TP_BSIZE/2)  // number if indirect inodes in record
    048 * #define TP_NINOS    (TP_NINDIR / sizeof (int32_t))
    049 * #define LBLSIZE     16
    050 * #define NAMELEN     64
    051 *
    052 * #define OFS_MAGIC     (int) 60011  // old format magic value
    053 * #define NFS_MAGIC     (int) 60012  // new format magic value
    054 * #define FS_UFS2_MAGIC (int) 0x19540119
    055 * #define CHECKSUM      (int) 84446  // constant used in checksum algorithm
    056 *
    057 * struct  s_spcl {
    058 *   int32_t c_type;             // record type (see below)
    059 *   int32_t <strong>c_date</strong>;             // date of this dump
    060 *   int32_t <strong>c_ddate</strong>;            // date of previous dump
    061 *   int32_t c_volume;           // dump volume number
    062 *   u_int32_t c_tapea;          // logical block of this record
    063 *   dump_ino_t c_ino;           // number of inode
    064 *   int32_t <strong>c_magic</strong>;            // magic number (see above)
    065 *   int32_t c_checksum;         // record checksum
    066 * #ifdef  __linux__
    067 *   struct  new_bsd_inode c_dinode;
    068 * #else
    069 * #ifdef sunos
    070 *   struct  new_bsd_inode c_dinode;
    071 * #else
    072 *   struct  dinode  c_dinode;   // ownership and mode of inode
    073 * #endif
    074 * #endif
    075 *   int32_t c_count;            // number of valid c_addr entries
    076 *   union u_data c_data;        // see above
    077 *   char    <strong>c_label[LBLSIZE]</strong>;   // dump label
    078 *   int32_t <strong>c_level</strong>;            // level of this dump
    079 *   char    <strong>c_filesys[NAMELEN]</strong>; // name of dumpped file system
    080 *   char    <strong>c_dev[NAMELEN]</strong>;     // name of dumpped device
    081 *   char    <strong>c_host[NAMELEN]</strong>;    // name of dumpped host
    082 *   int32_t c_flags;            // additional information (see below)
    083 *   int32_t c_firstrec;         // first record on volume
    084 *   int32_t c_ntrec;            // blocksize on volume
    085 *   int32_t c_extattributes;    // additional inode info (see below)
    086 *   int32_t c_spare[30];        // reserved for future uses
    087 * } s_spcl;
    088 *
    089 * //
    090 * // flag values
    091 * //
    092 * #define DR_NEWHEADER     0x0001  // new format tape header
    093 * #define DR_NEWINODEFMT   0x0002  // new format inodes on tape
    094 * #define DR_COMPRESSED    0x0080  // dump tape is compressed
    095 * #define DR_METAONLY      0x0100  // only the metadata of the inode has been dumped
    096 * #define DR_INODEINFO     0x0002  // [SIC] TS_END header contains c_inos information
    097 * #define DR_EXTATTRIBUTES 0x8000
    098 *
    099 * //
    100 * // extattributes inode info
    101 * //
    102 * #define EXT_REGULAR         0
    103 * #define EXT_MACOSFNDRINFO   1
    104 * #define EXT_MACOSRESFORK    2
    105 * #define EXT_XATTR           3
    106 *
    107 * // used for EA on tape
    108 * #define EXT2_GOOD_OLD_INODE_SIZE    128
    109 * #define EXT2_XATTR_MAGIC        0xEA020000  // block EA
    110 * #define EXT2_XATTR_MAGIC2       0xEA020001  // in inode EA
    111 * </pre>
    112 * <p>
    113 * The fields in <strong>bold</strong> are the same for all blocks. (This permitted multiple dumps to be written to a single tape.)
    114 * </p>
    115 *
    116 * <p>
    117 * The C structure for the inode (file) information is:
    118 *
    119 * <pre>
    120 * struct bsdtimeval {           //  **** alpha-*-linux is deviant
    121 *   __u32   tv_sec;
    122 *   __u32   tv_usec;
    123 * };
    124 *
    125 * #define NDADDR      12
    126 * #define NIADDR       3
    127 *
    128 * //
    129 * // This is the new (4.4) BSD inode structure
    130 * // copied from the FreeBSD 2.0 &lt;ufs/ufs/dinode.h&gt; include file
    131 * //
    132 * struct new_bsd_inode {
    133 *   __u16       di_mode;           // file type, standard Unix permissions
    134 *   __s16       di_nlink;          // number of hard links to file.
    135 *   union {
    136 *      __u16       oldids[2];
    137 *      __u32       inumber;
    138 *   }           di_u;
    139 *   u_quad_t    di_size;           // file size
    140 *   struct bsdtimeval   di_atime;  // time file was last accessed
    141 *   struct bsdtimeval   di_mtime;  // time file was last modified
    142 *   struct bsdtimeval   di_ctime;  // time file was created
    143 *   __u32       di_db[NDADDR];
    144 *   __u32       di_ib[NIADDR];
    145 *   __u32       di_flags;          //
    146 *   __s32       di_blocks;         // number of disk blocks
    147 *   __s32       di_gen;            // generation number
    148 *   __u32       di_uid;            // user id (see /etc/passwd)
    149 *   __u32       di_gid;            // group id (see /etc/group)
    150 *   __s32       di_spare[2];       // unused
    151 * };
    152 * </pre>
    153 * <p>
    154 * It is important to note that the header DOES NOT have the name of the file. It can't since hard links mean that you may have multiple file names for a single
    155 * physical file. You must read the contents of the directory entries to learn the mapping(s) from file name to inode.
    156 * </p>
    157 *
    158 * <p>
    159 * The C structure that indicates if a specific block is a real block that contains data or is a sparse block that is not persisted to the disk is:
    160 * </p>
    161 *
    162 * <pre>
    163 * #define TP_BSIZE    1024
    164 * #define TP_NINDIR   (TP_BSIZE/2)
    165 *
    166 * union u_data {
    167 *   char    s_addrs[TP_NINDIR]; // 1 =&gt; data; 0 =&gt; hole in inode
    168 *   int32_t s_inos[TP_NINOS];   // table of first inode on each volume
    169 * } u_data;
    170 * </pre>
    171 *
    172 * @NotThreadSafe
    173 */
    174public class DumpArchiveEntry implements ArchiveEntry {
    175
    176    /**
    177     * Enumerates permissions with values.
    178     */
    179    public enum PERMISSION {
    180        // Note: The arguments are octal values
    181
    182        /**
    183         * Permission SETUID (octal value 04000).
    184         */
    185        SETUID(04000),
    186
    187        /**
    188         * Permission SETGUI (octal value 02000).
    189         */
    190        SETGUI(02000),
    191
    192        /**
    193         * Permission STICKY (octal value 01000).
    194         */
    195        STICKY(01000),
    196
    197        /**
    198         * Permission USER_READ (octal value 00400).
    199         */
    200        USER_READ(00400),
    201
    202        /**
    203         * Permission USER_WRITE (octal value 00200).
    204         */
    205        USER_WRITE(00200),
    206
    207        /**
    208         * Permission USER_EXEC (octal value 00100).
    209         */
    210        USER_EXEC(00100),
    211
    212        /**
    213         * Permission GROUP_READ (octal value 00040).
    214         */
    215        GROUP_READ(00040),
    216
    217        /**
    218         * Permission GROUP_WRITE (octal value 00020).
    219         */
    220        GROUP_WRITE(00020),
    221
    222        /**
    223         * Permission 00020 (octal value 00010).
    224         */
    225        GROUP_EXEC(00010),
    226
    227        /**
    228         * Permission WORLD_READ (octal value 00004).
    229         */
    230        WORLD_READ(00004),
    231
    232        /**
    233         * Permission WORLD_WRITE (octal value 00002).
    234         */
    235        WORLD_WRITE(00002),
    236
    237        /**
    238         * Permission WORLD_EXEC (octal value 00001).
    239         */
    240        WORLD_EXEC(00001);
    241
    242        /**
    243         * Finds a matching set of enumeration values for the given code.
    244         *
    245         * @param code a code.
    246         * @return a Set of values, never null.
    247         */
    248        public static Set<PERMISSION> find(final int code) {
    249            final Set<PERMISSION> set = new HashSet<>();
    250            for (final PERMISSION p : values()) {
    251                if ((code & p.code) == p.code) {
    252                    set.add(p);
    253                }
    254            }
    255            if (set.isEmpty()) {
    256                return Collections.emptySet();
    257            }
    258            return EnumSet.copyOf(set);
    259        }
    260
    261        private final int code;
    262
    263        PERMISSION(final int code) {
    264            this.code = code;
    265        }
    266    }
    267
    268    /**
    269     * Archive entry as stored on tape. There is one TSH for (at most) every 512k in the file.
    270     */
    271    static final class TapeSegmentHeader {
    272        private DumpArchiveConstants.SEGMENT_TYPE type;
    273        private int volume;
    274        private int ino;
    275        private int count;
    276        private int holes;
    277        private final byte[] cdata = new byte[512]; // map of any 'holes'
    278
    279        public int getCdata(final int idx) {
    280            return cdata[idx];
    281        }
    282
    283        public int getCount() {
    284            return count;
    285        }
    286
    287        public int getHoles() {
    288            return holes;
    289        }
    290
    291        public int getIno() {
    292            return ino;
    293        }
    294
    295        public DumpArchiveConstants.SEGMENT_TYPE getType() {
    296            return type;
    297        }
    298
    299        public int getVolume() {
    300            return volume;
    301        }
    302
    303        void setIno(final int ino) {
    304            this.ino = ino;
    305        }
    306    }
    307
    308    /**
    309     * Enumerates types.
    310     */
    311    public enum TYPE {
    312
    313        /**
    314         * WHITEOUT with code 14.
    315         */
    316        WHITEOUT(14),
    317
    318        /**
    319         * SOCKET with code 12.
    320         */
    321        SOCKET(12),
    322
    323        /**
    324         * LINK with code 10.
    325         */
    326        LINK(10),
    327
    328        /**
    329         * FILE with code 8.
    330         */
    331        FILE(8),
    332
    333        /**
    334         * BLKDEV with code 6.
    335         */
    336        BLKDEV(6),
    337
    338        /**
    339         * DIRECTORY with code 4.
    340         */
    341        DIRECTORY(4),
    342
    343        /**
    344         * CHRDEV with code 2.
    345         */
    346        CHRDEV(2),
    347
    348        /**
    349         * CHRDEV with code 1.
    350         */
    351        FIFO(1),
    352
    353        /**
    354         * UNKNOWN with code 15.
    355         */
    356        UNKNOWN(15);
    357
    358        /**
    359         * Finds a matching enumeration value for the given code.
    360         *
    361         * @param code a code.
    362         * @return a value, never null.
    363         */
    364        public static TYPE find(final int code) {
    365            TYPE type = UNKNOWN;
    366            for (final TYPE t : values()) {
    367                if (code == t.code) {
    368                    type = t;
    369                }
    370            }
    371            return type;
    372        }
    373
    374        private final int code;
    375
    376        TYPE(final int code) {
    377            this.code = code;
    378        }
    379    }
    380
    381    /**
    382     * Populate the dump archive entry and tape segment header with the contents of the buffer.
    383     *
    384     * @param buffer buffer to read content from
    385     */
    386    static DumpArchiveEntry parse(final byte[] buffer) {
    387        final DumpArchiveEntry entry = new DumpArchiveEntry();
    388        final TapeSegmentHeader header = entry.header;
    389
    390        header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32(buffer, 0));
    391
    392        // header.dumpDate = new Date(1000L * DumpArchiveUtil.convert32(buffer, 4));
    393        // header.previousDumpDate = new Date(1000L * DumpArchiveUtil.convert32(
    394        // buffer, 8));
    395        header.volume = DumpArchiveUtil.convert32(buffer, 12);
    396        // header.tapea = DumpArchiveUtil.convert32(buffer, 16);
    397        entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20);
    398
    399        // header.magic = DumpArchiveUtil.convert32(buffer, 24);
    400        // header.checksum = DumpArchiveUtil.convert32(buffer, 28);
    401        final int m = DumpArchiveUtil.convert16(buffer, 32);
    402
    403        // determine the type of the file.
    404        entry.setType(TYPE.find(m >> 12 & 0x0F));
    405
    406        // determine the standard permissions
    407        entry.setMode(m);
    408
    409        entry.nlink = DumpArchiveUtil.convert16(buffer, 34);
    410        // inumber, oldids?
    411        entry.setSize(DumpArchiveUtil.convert64(buffer, 40));
    412
    413        long t = 1000L * DumpArchiveUtil.convert32(buffer, 48) + DumpArchiveUtil.convert32(buffer, 52) / 1000;
    414        entry.setAccessTime(new Date(t));
    415        t = 1000L * DumpArchiveUtil.convert32(buffer, 56) + DumpArchiveUtil.convert32(buffer, 60) / 1000;
    416        entry.setLastModifiedDate(new Date(t));
    417        t = 1000L * DumpArchiveUtil.convert32(buffer, 64) + DumpArchiveUtil.convert32(buffer, 68) / 1000;
    418        entry.ctime = t;
    419
    420        // db: 72-119 - direct blocks
    421        // id: 120-131 - indirect blocks
    422        // entry.flags = DumpArchiveUtil.convert32(buffer, 132);
    423        // entry.blocks = DumpArchiveUtil.convert32(buffer, 136);
    424        entry.generation = DumpArchiveUtil.convert32(buffer, 140);
    425        entry.setUserId(DumpArchiveUtil.convert32(buffer, 144));
    426        entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148));
    427        // two 32-bit spare values.
    428        header.count = DumpArchiveUtil.convert32(buffer, 160);
    429
    430        header.holes = 0;
    431
    432        for (int i = 0; i < 512 && i < header.count; i++) {
    433            if (buffer[164 + i] == 0) {
    434                header.holes++;
    435            }
    436        }
    437
    438        System.arraycopy(buffer, 164, header.cdata, 0, 512);
    439
    440        entry.volume = header.getVolume();
    441
    442        // entry.isSummaryOnly = false;
    443        return entry;
    444    }
    445
    446    private String name;
    447    private TYPE type = TYPE.UNKNOWN;
    448    private int mode;
    449    private Set<PERMISSION> permissions = Collections.emptySet();
    450    private long size;
    451
    452    private long atime;
    453
    454    private long mtime;
    455    private int uid;
    456    private int gid;
    457
    458    /**
    459     * Currently unused
    460     */
    461    private final DumpArchiveSummary summary = null;
    462
    463    /**
    464     * This value is available from the standard index.
    465     */
    466    private final TapeSegmentHeader header = new TapeSegmentHeader();
    467    private String simpleName;
    468    private String originalName;
    469
    470    /**
    471     * This value is available from the QFA index.
    472     */
    473    private int volume;
    474    private long offset;
    475    private int ino;
    476
    477    private int nlink;
    478
    479    private long ctime;
    480
    481    private int generation;
    482
    483    private boolean isDeleted;
    484
    485    /**
    486     * Constructs a default instance.
    487     */
    488    public DumpArchiveEntry() {
    489    }
    490
    491    /**
    492     * Constructs a new instance with only names.
    493     *
    494     * @param name       path name
    495     * @param simpleName actual file name.
    496     */
    497    public DumpArchiveEntry(final String name, final String simpleName) {
    498        setName(name);
    499        this.simpleName = simpleName;
    500    }
    501
    502    /**
    503     * Constructs a new instance with name, inode and type.
    504     *
    505     * @param name       the name
    506     * @param simpleName the simple name
    507     * @param ino        the ino
    508     * @param type       the type
    509     */
    510    protected DumpArchiveEntry(final String name, final String simpleName, final int ino, final TYPE type) {
    511        setType(type);
    512        setName(name);
    513        this.simpleName = simpleName;
    514        this.ino = ino;
    515        this.offset = 0;
    516    }
    517
    518    @Override
    519    public boolean equals(final Object o) {
    520        if (o == this) {
    521            return true;
    522        }
    523        if (o == null || !o.getClass().equals(getClass())) {
    524            return false;
    525        }
    526
    527        final DumpArchiveEntry rhs = (DumpArchiveEntry) o;
    528
    529        if (ino != rhs.ino) {
    530            return false;
    531        }
    532
    533        // summary is always null right now, but this may change some day
    534        if (summary == null && rhs.summary != null // NOSONAR
    535                || summary != null && !summary.equals(rhs.summary)) { // NOSONAR
    536            return false;
    537        }
    538
    539        return true;
    540    }
    541
    542    /**
    543     * Returns the time the file was last accessed.
    544     *
    545     * @return the access time
    546     */
    547    public Date getAccessTime() {
    548        return new Date(atime);
    549    }
    550
    551    /**
    552     * Gets file creation time.
    553     *
    554     * @return the creation time
    555     */
    556    public Date getCreationTime() {
    557        return new Date(ctime);
    558    }
    559
    560    /**
    561     * Returns the size of the entry as read from the archive.
    562     */
    563    long getEntrySize() {
    564        return size;
    565    }
    566
    567    /**
    568     * Gets the generation of the file.
    569     *
    570     * @return the generation
    571     */
    572    public int getGeneration() {
    573        return generation;
    574    }
    575
    576    /**
    577     * Gets the group id
    578     *
    579     * @return the group id
    580     */
    581    public int getGroupId() {
    582        return gid;
    583    }
    584
    585    /**
    586     * Gets the number of records in this segment.
    587     *
    588     * @return the number of records
    589     */
    590    public int getHeaderCount() {
    591        return header.getCount();
    592    }
    593
    594    /**
    595     * Gets the number of sparse records in this segment.
    596     *
    597     * @return the number of sparse records
    598     */
    599    public int getHeaderHoles() {
    600        return header.getHoles();
    601    }
    602
    603    /**
    604     * Gets the type of the tape segment header.
    605     *
    606     * @return the segment header
    607     */
    608    public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() {
    609        return header.getType();
    610    }
    611
    612    /**
    613     * Returns the ino of the entry.
    614     *
    615     * @return the ino
    616     */
    617    public int getIno() {
    618        return header.getIno();
    619    }
    620
    621    /**
    622     * The last modified date.
    623     *
    624     * @return the last modified date
    625     */
    626    @Override
    627    public Date getLastModifiedDate() {
    628        return new Date(mtime);
    629    }
    630
    631    /**
    632     * Gets the access permissions on the entry.
    633     *
    634     * @return the access permissions
    635     */
    636    public int getMode() {
    637        return mode;
    638    }
    639
    640    /**
    641     * Returns the name of the entry.
    642     *
    643     * <p>
    644     * This method returns the raw name as it is stored inside of the archive.
    645     * </p>
    646     *
    647     * @return the name of the entry.
    648     */
    649    @Override
    650    public String getName() {
    651        return name;
    652    }
    653
    654    /**
    655     * Gets the number of hard links to the entry.
    656     *
    657     * @return the number of hard links
    658     */
    659    public int getNlink() {
    660        return nlink;
    661    }
    662
    663    /**
    664     * Gets the offset within the archive
    665     *
    666     * @return the offset
    667     */
    668    public long getOffset() {
    669        return offset;
    670    }
    671
    672    /**
    673     * Returns the unmodified name of the entry.
    674     *
    675     * @return the name of the entry.
    676     */
    677    String getOriginalName() {
    678        return originalName;
    679    }
    680
    681    /**
    682     * Returns the permissions on the entry.
    683     *
    684     * @return the permissions
    685     */
    686    public Set<PERMISSION> getPermissions() {
    687        return permissions;
    688    }
    689
    690    /**
    691     * Returns the path of the entry.
    692     *
    693     * @return the path of the entry.
    694     */
    695    public String getSimpleName() {
    696        return simpleName;
    697    }
    698
    699    /**
    700     * Returns the size of the entry.
    701     *
    702     * @return the size
    703     */
    704    @Override
    705    public long getSize() {
    706        return isDirectory() ? SIZE_UNKNOWN : size;
    707    }
    708
    709    /**
    710     * Gets the type of the entry.
    711     *
    712     * @return the type
    713     */
    714    public TYPE getType() {
    715        return type;
    716    }
    717
    718    /**
    719     * Gets the user id.
    720     *
    721     * @return the user id
    722     */
    723    public int getUserId() {
    724        return uid;
    725    }
    726
    727    /**
    728     * Gets the tape volume where this file is located.
    729     *
    730     * @return the volume
    731     */
    732    public int getVolume() {
    733        return volume;
    734    }
    735
    736    @Override
    737    public int hashCode() {
    738        return ino;
    739    }
    740
    741    /**
    742     * Tests whether this is a block device.
    743     *
    744     * @return whether this is a block device.
    745     */
    746    public boolean isBlkDev() {
    747        return type == TYPE.BLKDEV;
    748    }
    749
    750    /**
    751     * Tests whether this is a character device.
    752     *
    753     * @return whether this is a character device
    754     */
    755    public boolean isChrDev() {
    756        return type == TYPE.CHRDEV;
    757    }
    758
    759    /**
    760     * Tests whether this file been deleted.
    761     * For valid on incremental dumps.
    762     *
    763     * @return whether the file has been deleted.
    764     */
    765    public boolean isDeleted() {
    766        return isDeleted;
    767    }
    768
    769    /**
    770     * Tests whether this is a directory.
    771     *
    772     * @return whether this is a directory
    773     */
    774    @Override
    775    public boolean isDirectory() {
    776        return type == TYPE.DIRECTORY;
    777    }
    778
    779    /**
    780     * Tests whether whether this is a fifo/pipe.
    781     *
    782     * @return whether this is a fifo/pipe.
    783     */
    784    public boolean isFifo() {
    785        return type == TYPE.FIFO;
    786    }
    787
    788    /**
    789     * Tests whether this is a regular file.
    790     *
    791     * @return whether this is a regular file.
    792     */
    793    public boolean isFile() {
    794        return type == TYPE.FILE;
    795    }
    796
    797    /**
    798     * Tests whether this is a socket.
    799     *
    800     * @return whether this is a socket.
    801     */
    802    public boolean isSocket() {
    803        return type == TYPE.SOCKET;
    804    }
    805
    806    /**
    807     * Tests whether this is a sparse record.
    808     *
    809     * @param idx index of the record to check.
    810     * @return whether this is a sparse record.
    811     */
    812    public boolean isSparseRecord(final int idx) {
    813        return (header.getCdata(idx) & 0x01) == 0;
    814    }
    815
    816    /**
    817     * Sets the time the file was last accessed.
    818     *
    819     * @param atime the access time
    820     */
    821    public void setAccessTime(final Date atime) {
    822        this.atime = atime.getTime();
    823    }
    824
    825    /**
    826     * Sets the file creation time.
    827     *
    828     * @param ctime the creation time
    829     */
    830    public void setCreationTime(final Date ctime) {
    831        this.ctime = ctime.getTime();
    832    }
    833
    834    /**
    835     * Sets whether this file has been deleted.
    836     *
    837     * @param isDeleted whether the file has been deleted
    838     */
    839    public void setDeleted(final boolean isDeleted) {
    840        this.isDeleted = isDeleted;
    841    }
    842
    843    /**
    844     * Sets the generation of the file.
    845     *
    846     * @param generation the generation
    847     */
    848    public void setGeneration(final int generation) {
    849        this.generation = generation;
    850    }
    851
    852    /**
    853     * Sets the group id.
    854     *
    855     * @param gid the group id
    856     */
    857    public void setGroupId(final int gid) {
    858        this.gid = gid;
    859    }
    860
    861    /**
    862     * Sets the time the file was last modified.
    863     *
    864     * @param mtime the last modified time
    865     */
    866    public void setLastModifiedDate(final Date mtime) {
    867        this.mtime = mtime.getTime();
    868    }
    869
    870    /**
    871     * Sets the access permissions on the entry.
    872     *
    873     * @param mode the access permissions
    874     */
    875    public void setMode(final int mode) {
    876        this.mode = mode & 07777;
    877        this.permissions = PERMISSION.find(mode);
    878    }
    879
    880    /**
    881     * Sets the name of the entry.
    882     *
    883     * @param name the name
    884     */
    885    public final void setName(String name) {
    886        this.originalName = name;
    887        if (name != null) {
    888            if (isDirectory() && !name.endsWith("/")) {
    889                name += "/";
    890            }
    891            if (name.startsWith("./")) {
    892                name = name.substring(2);
    893            }
    894        }
    895        this.name = name;
    896    }
    897
    898    /**
    899     * Sets the number of hard links.
    900     *
    901     * @param nlink the number of hard links
    902     */
    903    public void setNlink(final int nlink) {
    904        this.nlink = nlink;
    905    }
    906
    907    /**
    908     * Sets the offset within the archive.
    909     *
    910     * @param offset the offset
    911     */
    912    public void setOffset(final long offset) {
    913        this.offset = offset;
    914    }
    915
    916    /**
    917     * Sets the path of the entry.
    918     *
    919     * @param simpleName the simple name
    920     */
    921    protected void setSimpleName(final String simpleName) {
    922        this.simpleName = simpleName;
    923    }
    924
    925    /**
    926     * Sets the size of the entry.
    927     *
    928     * @param size the size
    929     */
    930    public void setSize(final long size) {
    931        this.size = size;
    932    }
    933
    934    /**
    935     * Sets the type of the entry.
    936     *
    937     * @param type the type
    938     */
    939    public void setType(final TYPE type) {
    940        this.type = type;
    941    }
    942
    943    /**
    944     * Sets the user id.
    945     *
    946     * @param uid the user id
    947     */
    948    public void setUserId(final int uid) {
    949        this.uid = uid;
    950    }
    951
    952    /**
    953     * Sets the tape volume.
    954     *
    955     * @param volume the volume
    956     */
    957    public void setVolume(final int volume) {
    958        this.volume = volume;
    959    }
    960
    961    @Override
    962    public String toString() {
    963        return getName();
    964    }
    965
    966    /**
    967     * Update entry with information from next tape segment header.
    968     */
    969    void update(final byte[] buffer) {
    970        header.volume = DumpArchiveUtil.convert32(buffer, 16);
    971        header.count = DumpArchiveUtil.convert32(buffer, 160);
    972
    973        header.holes = 0;
    974
    975        for (int i = 0; i < 512 && i < header.count; i++) {
    976            if (buffer[164 + i] == 0) {
    977                header.holes++;
    978            }
    979        }
    980
    981        System.arraycopy(buffer, 164, header.cdata, 0, 512);
    982    }
    983}