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