    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.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 <b>c_date</b>;             // date of this dump
    060 *   int32_t <b>c_ddate</b>;            // 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 <b>c_magic</b>;            // 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    <b>c_label[LBLSIZE]</b>;   // dump label
    078 *   int32_t <b>c_level</b>;            // level of this dump
    079 *   char    <b>c_filesys[NAMELEN]</b>; // name of dumpped file system
    080 *   char    <b>c_dev[NAMELEN]</b>;     // name of dumpped device
    081 *   char    <b>c_host[NAMELEN]</b>;    // 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 <b>bold</b> 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    public enum PERMISSION {
    176        SETUID(04000), SETGUI(02000), STICKY(01000), USER_READ(00400), USER_WRITE(00200), USER_EXEC(00100), GROUP_READ(00040), GROUP_WRITE(00020),
    177        GROUP_EXEC(00010), WORLD_READ(00004), WORLD_WRITE(00002), WORLD_EXEC(00001);
    178
    179        public static Set<PERMISSION> find(final int code) {
    180            final Set<PERMISSION> set = new HashSet<>();
    181
    182            for (final PERMISSION p : PERMISSION.values()) {
    183                if ((code & p.code) == p.code) {
    184                    set.add(p);
    185                }
    186            }
    187
    188            if (set.isEmpty()) {
    189                return Collections.emptySet();
    190            }
    191
    192            return EnumSet.copyOf(set);
    193        }
    194
    195        private final int code;
    196
    197        PERMISSION(final int code) {
    198            this.code = code;
    199        }
    200    }
    201
    202    /**
    203     * Archive entry as stored on tape. There is one TSH for (at most) every 512k in the file.
    204     */
    205    static class TapeSegmentHeader {
    206        private DumpArchiveConstants.SEGMENT_TYPE type;
    207        private int volume;
    208        private int ino;
    209        private int count;
    210        private int holes;
    211        private final byte[] cdata = new byte[512]; // map of any 'holes'
    212
    213        public int getCdata(final int idx) {
    214            return cdata[idx];
    215        }
    216
    217        public int getCount() {
    218            return count;
    219        }
    220
    221        public int getHoles() {
    222            return holes;
    223        }
    224
    225        public int getIno() {
    226            return ino;
    227        }
    228
    229        public DumpArchiveConstants.SEGMENT_TYPE getType() {
    230            return type;
    231        }
    232
    233        public int getVolume() {
    234            return volume;
    235        }
    236
    237        void setIno(final int ino) {
    238            this.ino = ino;
    239        }
    240    }
    241
    242    public enum TYPE {
    243        WHITEOUT(14), SOCKET(12), LINK(10), FILE(8), BLKDEV(6), DIRECTORY(4), CHRDEV(2), FIFO(1), UNKNOWN(15);
    244
    245        public static TYPE find(final int code) {
    246            TYPE type = UNKNOWN;
    247
    248            for (final TYPE t : TYPE.values()) {
    249                if (code == t.code) {
    250                    type = t;
    251                }
    252            }
    253
    254            return type;
    255        }
    256
    257        private final int code;
    258
    259        TYPE(final int code) {
    260            this.code = code;
    261        }
    262    }
    263
    264    /**
    265     * Populate the dump archive entry and tape segment header with the contents of the buffer.
    266     *
    267     * @param buffer buffer to read content from
    268     */
    269    static DumpArchiveEntry parse(final byte[] buffer) {
    270        final DumpArchiveEntry entry = new DumpArchiveEntry();
    271        final TapeSegmentHeader header = entry.header;
    272
    273        header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32(buffer, 0));
    274
    275        // header.dumpDate = new Date(1000L * DumpArchiveUtil.convert32(buffer, 4));
    276        // header.previousDumpDate = new Date(1000L * DumpArchiveUtil.convert32(
    277        // buffer, 8));
    278        header.volume = DumpArchiveUtil.convert32(buffer, 12);
    279        // header.tapea = DumpArchiveUtil.convert32(buffer, 16);
    280        entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20);
    281
    282        // header.magic = DumpArchiveUtil.convert32(buffer, 24);
    283        // header.checksum = DumpArchiveUtil.convert32(buffer, 28);
    284        final int m = DumpArchiveUtil.convert16(buffer, 32);
    285
    286        // determine the type of the file.
    287        entry.setType(TYPE.find(m >> 12 & 0x0F));
    288
    289        // determine the standard permissions
    290        entry.setMode(m);
    291
    292        entry.nlink = DumpArchiveUtil.convert16(buffer, 34);
    293        // inumber, oldids?
    294        entry.setSize(DumpArchiveUtil.convert64(buffer, 40));
    295
    296        long t = 1000L * DumpArchiveUtil.convert32(buffer, 48) + DumpArchiveUtil.convert32(buffer, 52) / 1000;
    297        entry.setAccessTime(new Date(t));
    298        t = 1000L * DumpArchiveUtil.convert32(buffer, 56) + DumpArchiveUtil.convert32(buffer, 60) / 1000;
    299        entry.setLastModifiedDate(new Date(t));
    300        t = 1000L * DumpArchiveUtil.convert32(buffer, 64) + DumpArchiveUtil.convert32(buffer, 68) / 1000;
    301        entry.ctime = t;
    302
    303        // db: 72-119 - direct blocks
    304        // id: 120-131 - indirect blocks
    305        // entry.flags = DumpArchiveUtil.convert32(buffer, 132);
    306        // entry.blocks = DumpArchiveUtil.convert32(buffer, 136);
    307        entry.generation = DumpArchiveUtil.convert32(buffer, 140);
    308        entry.setUserId(DumpArchiveUtil.convert32(buffer, 144));
    309        entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148));
    310        // two 32-bit spare values.
    311        header.count = DumpArchiveUtil.convert32(buffer, 160);
    312
    313        header.holes = 0;
    314
    315        for (int i = 0; i < 512 && i < header.count; i++) {
    316            if (buffer[164 + i] == 0) {
    317                header.holes++;
    318            }
    319        }
    320
    321        System.arraycopy(buffer, 164, header.cdata, 0, 512);
    322
    323        entry.volume = header.getVolume();
    324
    325        // entry.isSummaryOnly = false;
    326        return entry;
    327    }
    328
    329    private String name;
    330    private TYPE type = TYPE.UNKNOWN;
    331    private int mode;
    332    private Set<PERMISSION> permissions = Collections.emptySet();
    333    private long size;
    334
    335    private long atime;
    336
    337    private long mtime;
    338    private int uid;
    339    private int gid;
    340
    341    /**
    342     * Currently unused
    343     */
    344    private final DumpArchiveSummary summary = null;
    345    // this information is available from standard index.
    346    private final TapeSegmentHeader header = new TapeSegmentHeader();
    347    private String simpleName;
    348    private String originalName;
    349    // this information is available from QFA index
    350    private int volume;
    351    private long offset;
    352    private int ino;
    353
    354    private int nlink;
    355
    356    private long ctime;
    357
    358    private int generation;
    359
    360    private boolean isDeleted;
    361
    362    /**
    363     * Default constructor.
    364     */
    365    public DumpArchiveEntry() {
    366    }
    367
    368    /**
    369     * Constructor taking only file name.
    370     *
    371     * @param name       path name
    372     * @param simpleName actual file name.
    373     */
    374    public DumpArchiveEntry(final String name, final String simpleName) {
    375        setName(name);
    376        this.simpleName = simpleName;
    377    }
    378
    379    /**
    380     * Constructor taking name, inode and type.
    381     *
    382     * @param name       the name
    383     * @param simpleName the simple name
    384     * @param ino        the ino
    385     * @param type       the type
    386     */
    387    protected DumpArchiveEntry(final String name, final String simpleName, final int ino, final TYPE type) {
    388        setType(type);
    389        setName(name);
    390        this.simpleName = simpleName;
    391        this.ino = ino;
    392        this.offset = 0;
    393    }
    394
    395    @Override
    396    public boolean equals(final Object o) {
    397        if (o == this) {
    398            return true;
    399        }
    400        if (o == null || !o.getClass().equals(getClass())) {
    401            return false;
    402        }
    403
    404        final DumpArchiveEntry rhs = (DumpArchiveEntry) o;
    405
    406        if (ino != rhs.ino) {
    407            return false;
    408        }
    409
    410        // summary is always null right now, but this may change some day
    411        if (summary == null && rhs.summary != null // NOSONAR
    412                || summary != null && !summary.equals(rhs.summary)) { // NOSONAR
    413            return false;
    414        }
    415
    416        return true;
    417    }
    418
    419    /**
    420     * Returns the time the file was last accessed.
    421     *
    422     * @return the access time
    423     */
    424    public Date getAccessTime() {
    425        return new Date(atime);
    426    }
    427
    428    /**
    429     * Gets file creation time.
    430     *
    431     * @return the creation time
    432     */
    433    public Date getCreationTime() {
    434        return new Date(ctime);
    435    }
    436
    437    /**
    438     * Returns the size of the entry as read from the archive.
    439     */
    440    long getEntrySize() {
    441        return size;
    442    }
    443
    444    /**
    445     * Gets the generation of the file.
    446     *
    447     * @return the generation
    448     */
    449    public int getGeneration() {
    450        return generation;
    451    }
    452
    453    /**
    454     * Gets the group id
    455     *
    456     * @return the group id
    457     */
    458    public int getGroupId() {
    459        return gid;
    460    }
    461
    462    /**
    463     * Gets the number of records in this segment.
    464     *
    465     * @return the number of records
    466     */
    467    public int getHeaderCount() {
    468        return header.getCount();
    469    }
    470
    471    /**
    472     * Gets the number of sparse records in this segment.
    473     *
    474     * @return the number of sparse records
    475     */
    476    public int getHeaderHoles() {
    477        return header.getHoles();
    478    }
    479
    480    /**
    481     * Gets the type of the tape segment header.
    482     *
    483     * @return the segment header
    484     */
    485    public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() {
    486        return header.getType();
    487    }
    488
    489    /**
    490     * Returns the ino of the entry.
    491     *
    492     * @return the ino
    493     */
    494    public int getIno() {
    495        return header.getIno();
    496    }
    497
    498    /**
    499     * The last modified date.
    500     *
    501     * @return the last modified date
    502     */
    503    @Override
    504    public Date getLastModifiedDate() {
    505        return new Date(mtime);
    506    }
    507
    508    /**
    509     * Gets the access permissions on the entry.
    510     *
    511     * @return the access permissions
    512     */
    513    public int getMode() {
    514        return mode;
    515    }
    516
    517    /**
    518     * Returns the name of the entry.
    519     *
    520     * <p>
    521     * This method returns the raw name as it is stored inside of the archive.
    522     * </p>
    523     *
    524     * @return the name of the entry.
    525     */
    526    @Override
    527    public String getName() {
    528        return name;
    529    }
    530
    531    /**
    532     * Gets the number of hard links to the entry.
    533     *
    534     * @return the number of hard links
    535     */
    536    public int getNlink() {
    537        return nlink;
    538    }
    539
    540    /**
    541     * Gets the offset within the archive
    542     *
    543     * @return the offset
    544     */
    545    public long getOffset() {
    546        return offset;
    547    }
    548
    549    /**
    550     * Returns the unmodified name of the entry.
    551     *
    552     * @return the name of the entry.
    553     */
    554    String getOriginalName() {
    555        return originalName;
    556    }
    557
    558    /**
    559     * Returns the permissions on the entry.
    560     *
    561     * @return the permissions
    562     */
    563    public Set<PERMISSION> getPermissions() {
    564        return permissions;
    565    }
    566
    567    /**
    568     * Returns the path of the entry.
    569     *
    570     * @return the path of the entry.
    571     */
    572    public String getSimpleName() {
    573        return simpleName;
    574    }
    575
    576    /**
    577     * Returns the size of the entry.
    578     *
    579     * @return the size
    580     */
    581    @Override
    582    public long getSize() {
    583        return isDirectory() ? SIZE_UNKNOWN : size;
    584    }
    585
    586    /**
    587     * Gets the type of the entry.
    588     *
    589     * @return the type
    590     */
    591    public TYPE getType() {
    592        return type;
    593    }
    594
    595    /**
    596     * Gets the user id.
    597     *
    598     * @return the user id
    599     */
    600    public int getUserId() {
    601        return uid;
    602    }
    603
    604    /**
    605     * Gets the tape volume where this file is located.
    606     *
    607     * @return the volume
    608     */
    609    public int getVolume() {
    610        return volume;
    611    }
    612
    613    @Override
    614    public int hashCode() {
    615        return ino;
    616    }
    617
    618    /**
    619     * Is this a block device?
    620     *
    621     * @return whether this is a block device
    622     */
    623    public boolean isBlkDev() {
    624        return type == TYPE.BLKDEV;
    625    }
    626
    627    /**
    628     * Is this a character device?
    629     *
    630     * @return whether this is a character device
    631     */
    632    public boolean isChrDev() {
    633        return type == TYPE.CHRDEV;
    634    }
    635
    636    /**
    637     * Has this file been deleted? (On valid on incremental dumps.)
    638     *
    639     * @return whether the file has been deleted
    640     */
    641    public boolean isDeleted() {
    642        return isDeleted;
    643    }
    644
    645    /**
    646     * Is this a directory?
    647     *
    648     * @return whether this is a directory
    649     */
    650    @Override
    651    public boolean isDirectory() {
    652        return type == TYPE.DIRECTORY;
    653    }
    654
    655    /**
    656     * Is this a fifo/pipe?
    657     *
    658     * @return whether this is a fifo
    659     */
    660    public boolean isFifo() {
    661        return type == TYPE.FIFO;
    662    }
    663
    664    /**
    665     * Is this a regular file?
    666     *
    667     * @return whether this is a regular file
    668     */
    669    public boolean isFile() {
    670        return type == TYPE.FILE;
    671    }
    672
    673    /**
    674     * Is this a network device?
    675     *
    676     * @return whether this is a socket
    677     */
    678    public boolean isSocket() {
    679        return type == TYPE.SOCKET;
    680    }
    681
    682    /**
    683     * Is this a sparse record?
    684     *
    685     * @param idx index of the record to check
    686     * @return whether this is a sparse record
    687     */
    688    public boolean isSparseRecord(final int idx) {
    689        return (header.getCdata(idx) & 0x01) == 0;
    690    }
    691
    692    /**
    693     * Sets the time the file was last accessed.
    694     *
    695     * @param atime the access time
    696     */
    697    public void setAccessTime(final Date atime) {
    698        this.atime = atime.getTime();
    699    }
    700
    701    /**
    702     * Sets the file creation time.
    703     *
    704     * @param ctime the creation time
    705     */
    706    public void setCreationTime(final Date ctime) {
    707        this.ctime = ctime.getTime();
    708    }
    709
    710    /**
    711     * Sets whether this file has been deleted.
    712     *
    713     * @param isDeleted whether the file has been deleted
    714     */
    715    public void setDeleted(final boolean isDeleted) {
    716        this.isDeleted = isDeleted;
    717    }
    718
    719    /**
    720     * Sets the generation of the file.
    721     *
    722     * @param generation the generation
    723     */
    724    public void setGeneration(final int generation) {
    725        this.generation = generation;
    726    }
    727
    728    /**
    729     * Sets the group id.
    730     *
    731     * @param gid the group id
    732     */
    733    public void setGroupId(final int gid) {
    734        this.gid = gid;
    735    }
    736
    737    /**
    738     * Sets the time the file was last modified.
    739     *
    740     * @param mtime the last modified time
    741     */
    742    public void setLastModifiedDate(final Date mtime) {
    743        this.mtime = mtime.getTime();
    744    }
    745
    746    /**
    747     * Sets the access permissions on the entry.
    748     *
    749     * @param mode the access permissions
    750     */
    751    public void setMode(final int mode) {
    752        this.mode = mode & 07777;
    753        this.permissions = PERMISSION.find(mode);
    754    }
    755
    756    /**
    757     * Sets the name of the entry.
    758     *
    759     * @param name the name
    760     */
    761    public final void setName(String name) {
    762        this.originalName = name;
    763        if (name != null) {
    764            if (isDirectory() && !name.endsWith("/")) {
    765                name += "/";
    766            }
    767            if (name.startsWith("./")) {
    768                name = name.substring(2);
    769            }
    770        }
    771        this.name = name;
    772    }
    773
    774    /**
    775     * Sets the number of hard links.
    776     *
    777     * @param nlink the number of hard links
    778     */
    779    public void setNlink(final int nlink) {
    780        this.nlink = nlink;
    781    }
    782
    783    /**
    784     * Sets the offset within the archive.
    785     *
    786     * @param offset the offset
    787     */
    788    public void setOffset(final long offset) {
    789        this.offset = offset;
    790    }
    791
    792    /**
    793     * Sets the path of the entry.
    794     *
    795     * @param simpleName the simple name
    796     */
    797    protected void setSimpleName(final String simpleName) {
    798        this.simpleName = simpleName;
    799    }
    800
    801    /**
    802     * Sets the size of the entry.
    803     *
    804     * @param size the size
    805     */
    806    public void setSize(final long size) {
    807        this.size = size;
    808    }
    809
    810    /**
    811     * Sets the type of the entry.
    812     *
    813     * @param type the type
    814     */
    815    public void setType(final TYPE type) {
    816        this.type = type;
    817    }
    818
    819    /**
    820     * Sets the user id.
    821     *
    822     * @param uid the user id
    823     */
    824    public void setUserId(final int uid) {
    825        this.uid = uid;
    826    }
    827
    828    /**
    829     * Sets the tape volume.
    830     *
    831     * @param volume the volume
    832     */
    833    public void setVolume(final int volume) {
    834        this.volume = volume;
    835    }
    836
    837    @Override
    838    public String toString() {
    839        return getName();
    840    }
    841
    842    /**
    843     * Update entry with information from next tape segment header.
    844     */
    845    void update(final byte[] buffer) {
    846        header.volume = DumpArchiveUtil.convert32(buffer, 16);
    847        header.count = DumpArchiveUtil.convert32(buffer, 160);
    848
    849        header.holes = 0;
    850
    851        for (int i = 0; i < 512 && i < header.count; i++) {
    852            if (buffer[164 + i] == 0) {
    853                header.holes++;
    854            }
    855        }
    856
    857        System.arraycopy(buffer, 164, header.cdata, 0, 512);
    858    }
    859}