DumpArchiveEntry.java

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

  20. import java.util.Collections;
  21. import java.util.Date;
  22. import java.util.EnumSet;
  23. import java.util.HashSet;
  24. import java.util.Set;

  25. import org.apache.commons.compress.archivers.ArchiveEntry;

  26. /**
  27.  * This class represents an entry in a Dump archive. It consists of the entry's header, the entry's File and any extended attributes.
  28.  * <p>
  29.  * DumpEntries that are created from the header bytes read from an archive are instantiated with the DumpArchiveEntry( byte[] ) constructor. These entries will
  30.  * 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
  31.  * to null, since they reference an archive entry not a file.
  32.  * <p>
  33.  * 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
  34.  * 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
  35.  * and the File is set to null.
  36.  *
  37.  * <p>
  38.  * The C structure for a Dump Entry's header is:
  39.  *
  40.  * <pre>
  41.  * #define TP_BSIZE    1024          // size of each file block
  42.  * #define NTREC       10            // number of blocks to write at once
  43.  * #define HIGHDENSITYTREC 32        // number of blocks to write on high-density tapes
  44.  * #define TP_NINDIR   (TP_BSIZE/2)  // number if indirect inodes in record
  45.  * #define TP_NINOS    (TP_NINDIR / sizeof (int32_t))
  46.  * #define LBLSIZE     16
  47.  * #define NAMELEN     64
  48.  *
  49.  * #define OFS_MAGIC     (int) 60011  // old format magic value
  50.  * #define NFS_MAGIC     (int) 60012  // new format magic value
  51.  * #define FS_UFS2_MAGIC (int) 0x19540119
  52.  * #define CHECKSUM      (int) 84446  // constant used in checksum algorithm
  53.  *
  54.  * struct  s_spcl {
  55.  *   int32_t c_type;             // record type (see below)
  56.  *   int32_t <b>c_date</b>;             // date of this dump
  57.  *   int32_t <b>c_ddate</b>;            // date of previous dump
  58.  *   int32_t c_volume;           // dump volume number
  59.  *   u_int32_t c_tapea;          // logical block of this record
  60.  *   dump_ino_t c_ino;           // number of inode
  61.  *   int32_t <b>c_magic</b>;            // magic number (see above)
  62.  *   int32_t c_checksum;         // record checksum
  63.  * #ifdef  __linux__
  64.  *   struct  new_bsd_inode c_dinode;
  65.  * #else
  66.  * #ifdef sunos
  67.  *   struct  new_bsd_inode c_dinode;
  68.  * #else
  69.  *   struct  dinode  c_dinode;   // ownership and mode of inode
  70.  * #endif
  71.  * #endif
  72.  *   int32_t c_count;            // number of valid c_addr entries
  73.  *   union u_data c_data;        // see above
  74.  *   char    <b>c_label[LBLSIZE]</b>;   // dump label
  75.  *   int32_t <b>c_level</b>;            // level of this dump
  76.  *   char    <b>c_filesys[NAMELEN]</b>; // name of dumpped file system
  77.  *   char    <b>c_dev[NAMELEN]</b>;     // name of dumpped device
  78.  *   char    <b>c_host[NAMELEN]</b>;    // name of dumpped host
  79.  *   int32_t c_flags;            // additional information (see below)
  80.  *   int32_t c_firstrec;         // first record on volume
  81.  *   int32_t c_ntrec;            // blocksize on volume
  82.  *   int32_t c_extattributes;    // additional inode info (see below)
  83.  *   int32_t c_spare[30];        // reserved for future uses
  84.  * } s_spcl;
  85.  *
  86.  * //
  87.  * // flag values
  88.  * //
  89.  * #define DR_NEWHEADER     0x0001  // new format tape header
  90.  * #define DR_NEWINODEFMT   0x0002  // new format inodes on tape
  91.  * #define DR_COMPRESSED    0x0080  // dump tape is compressed
  92.  * #define DR_METAONLY      0x0100  // only the metadata of the inode has been dumped
  93.  * #define DR_INODEINFO     0x0002  // [SIC] TS_END header contains c_inos information
  94.  * #define DR_EXTATTRIBUTES 0x8000
  95.  *
  96.  * //
  97.  * // extattributes inode info
  98.  * //
  99.  * #define EXT_REGULAR         0
  100.  * #define EXT_MACOSFNDRINFO   1
  101.  * #define EXT_MACOSRESFORK    2
  102.  * #define EXT_XATTR           3
  103.  *
  104.  * // used for EA on tape
  105.  * #define EXT2_GOOD_OLD_INODE_SIZE    128
  106.  * #define EXT2_XATTR_MAGIC        0xEA020000  // block EA
  107.  * #define EXT2_XATTR_MAGIC2       0xEA020001  // in inode EA
  108.  * </pre>
  109.  * <p>
  110.  * The fields in <b>bold</b> are the same for all blocks. (This permitted multiple dumps to be written to a single tape.)
  111.  * </p>
  112.  *
  113.  * <p>
  114.  * The C structure for the inode (file) information is:
  115.  *
  116.  * <pre>
  117.  * struct bsdtimeval {           //  **** alpha-*-linux is deviant
  118.  *   __u32   tv_sec;
  119.  *   __u32   tv_usec;
  120.  * };
  121.  *
  122.  * #define NDADDR      12
  123.  * #define NIADDR       3
  124.  *
  125.  * //
  126.  * // This is the new (4.4) BSD inode structure
  127.  * // copied from the FreeBSD 2.0 &lt;ufs/ufs/dinode.h&gt; include file
  128.  * //
  129.  * struct new_bsd_inode {
  130.  *   __u16       di_mode;           // file type, standard UNIX permissions
  131.  *   __s16       di_nlink;          // number of hard links to file.
  132.  *   union {
  133.  *      __u16       oldids[2];
  134.  *      __u32       inumber;
  135.  *   }           di_u;
  136.  *   u_quad_t    di_size;           // file size
  137.  *   struct bsdtimeval   di_atime;  // time file was last accessed
  138.  *   struct bsdtimeval   di_mtime;  // time file was last modified
  139.  *   struct bsdtimeval   di_ctime;  // time file was created
  140.  *   __u32       di_db[NDADDR];
  141.  *   __u32       di_ib[NIADDR];
  142.  *   __u32       di_flags;          //
  143.  *   __s32       di_blocks;         // number of disk blocks
  144.  *   __s32       di_gen;            // generation number
  145.  *   __u32       di_uid;            // user id (see /etc/passwd)
  146.  *   __u32       di_gid;            // group id (see /etc/group)
  147.  *   __s32       di_spare[2];       // unused
  148.  * };
  149.  * </pre>
  150.  * <p>
  151.  * 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
  152.  * physical file. You must read the contents of the directory entries to learn the mapping(s) from file name to inode.
  153.  * </p>
  154.  *
  155.  * <p>
  156.  * 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:
  157.  * </p>
  158.  *
  159.  * <pre>
  160.  * #define TP_BSIZE    1024
  161.  * #define TP_NINDIR   (TP_BSIZE/2)
  162.  *
  163.  * union u_data {
  164.  *   char    s_addrs[TP_NINDIR]; // 1 =&gt; data; 0 =&gt; hole in inode
  165.  *   int32_t s_inos[TP_NINOS];   // table of first inode on each volume
  166.  * } u_data;
  167.  * </pre>
  168.  *
  169.  * @NotThreadSafe
  170.  */
  171. public class DumpArchiveEntry implements ArchiveEntry {

  172.     public enum PERMISSION {
  173.         // Note: The arguments are octal values
  174.         // @formatter:off
  175.         SETUID(04000),
  176.         SETGUI(02000),
  177.         STICKY(01000),
  178.         USER_READ(00400),
  179.         USER_WRITE(00200),
  180.         USER_EXEC(00100),
  181.         GROUP_READ(00040),
  182.         GROUP_WRITE(00020),
  183.         GROUP_EXEC(00010),
  184.         WORLD_READ(00004),
  185.         WORLD_WRITE(00002),
  186.         WORLD_EXEC(00001);
  187.         // @formatter:on

  188.         public static Set<PERMISSION> find(final int code) {
  189.             final Set<PERMISSION> set = new HashSet<>();

  190.             for (final PERMISSION p : values()) {
  191.                 if ((code & p.code) == p.code) {
  192.                     set.add(p);
  193.                 }
  194.             }

  195.             if (set.isEmpty()) {
  196.                 return Collections.emptySet();
  197.             }

  198.             return EnumSet.copyOf(set);
  199.         }

  200.         private final int code;

  201.         PERMISSION(final int code) {
  202.             this.code = code;
  203.         }
  204.     }

  205.     /**
  206.      * Archive entry as stored on tape. There is one TSH for (at most) every 512k in the file.
  207.      */
  208.     static class TapeSegmentHeader {
  209.         private DumpArchiveConstants.SEGMENT_TYPE type;
  210.         private int volume;
  211.         private int ino;
  212.         private int count;
  213.         private int holes;
  214.         private final byte[] cdata = new byte[512]; // map of any 'holes'

  215.         public int getCdata(final int idx) {
  216.             return cdata[idx];
  217.         }

  218.         public int getCount() {
  219.             return count;
  220.         }

  221.         public int getHoles() {
  222.             return holes;
  223.         }

  224.         public int getIno() {
  225.             return ino;
  226.         }

  227.         public DumpArchiveConstants.SEGMENT_TYPE getType() {
  228.             return type;
  229.         }

  230.         public int getVolume() {
  231.             return volume;
  232.         }

  233.         void setIno(final int ino) {
  234.             this.ino = ino;
  235.         }
  236.     }

  237.     public enum TYPE {
  238.         WHITEOUT(14), SOCKET(12), LINK(10), FILE(8), BLKDEV(6), DIRECTORY(4), CHRDEV(2), FIFO(1), UNKNOWN(15);

  239.         public static TYPE find(final int code) {
  240.             TYPE type = UNKNOWN;

  241.             for (final TYPE t : values()) {
  242.                 if (code == t.code) {
  243.                     type = t;
  244.                 }
  245.             }

  246.             return type;
  247.         }

  248.         private final int code;

  249.         TYPE(final int code) {
  250.             this.code = code;
  251.         }
  252.     }

  253.     /**
  254.      * Populate the dump archive entry and tape segment header with the contents of the buffer.
  255.      *
  256.      * @param buffer buffer to read content from
  257.      */
  258.     static DumpArchiveEntry parse(final byte[] buffer) {
  259.         final DumpArchiveEntry entry = new DumpArchiveEntry();
  260.         final TapeSegmentHeader header = entry.header;

  261.         header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32(buffer, 0));

  262.         // header.dumpDate = new Date(1000L * DumpArchiveUtil.convert32(buffer, 4));
  263.         // header.previousDumpDate = new Date(1000L * DumpArchiveUtil.convert32(
  264.         // buffer, 8));
  265.         header.volume = DumpArchiveUtil.convert32(buffer, 12);
  266.         // header.tapea = DumpArchiveUtil.convert32(buffer, 16);
  267.         entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20);

  268.         // header.magic = DumpArchiveUtil.convert32(buffer, 24);
  269.         // header.checksum = DumpArchiveUtil.convert32(buffer, 28);
  270.         final int m = DumpArchiveUtil.convert16(buffer, 32);

  271.         // determine the type of the file.
  272.         entry.setType(TYPE.find(m >> 12 & 0x0F));

  273.         // determine the standard permissions
  274.         entry.setMode(m);

  275.         entry.nlink = DumpArchiveUtil.convert16(buffer, 34);
  276.         // inumber, oldids?
  277.         entry.setSize(DumpArchiveUtil.convert64(buffer, 40));

  278.         long t = 1000L * DumpArchiveUtil.convert32(buffer, 48) + DumpArchiveUtil.convert32(buffer, 52) / 1000;
  279.         entry.setAccessTime(new Date(t));
  280.         t = 1000L * DumpArchiveUtil.convert32(buffer, 56) + DumpArchiveUtil.convert32(buffer, 60) / 1000;
  281.         entry.setLastModifiedDate(new Date(t));
  282.         t = 1000L * DumpArchiveUtil.convert32(buffer, 64) + DumpArchiveUtil.convert32(buffer, 68) / 1000;
  283.         entry.ctime = t;

  284.         // db: 72-119 - direct blocks
  285.         // id: 120-131 - indirect blocks
  286.         // entry.flags = DumpArchiveUtil.convert32(buffer, 132);
  287.         // entry.blocks = DumpArchiveUtil.convert32(buffer, 136);
  288.         entry.generation = DumpArchiveUtil.convert32(buffer, 140);
  289.         entry.setUserId(DumpArchiveUtil.convert32(buffer, 144));
  290.         entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148));
  291.         // two 32-bit spare values.
  292.         header.count = DumpArchiveUtil.convert32(buffer, 160);

  293.         header.holes = 0;

  294.         for (int i = 0; i < 512 && i < header.count; i++) {
  295.             if (buffer[164 + i] == 0) {
  296.                 header.holes++;
  297.             }
  298.         }

  299.         System.arraycopy(buffer, 164, header.cdata, 0, 512);

  300.         entry.volume = header.getVolume();

  301.         // entry.isSummaryOnly = false;
  302.         return entry;
  303.     }

  304.     private String name;
  305.     private TYPE type = TYPE.UNKNOWN;
  306.     private int mode;
  307.     private Set<PERMISSION> permissions = Collections.emptySet();
  308.     private long size;

  309.     private long atime;

  310.     private long mtime;
  311.     private int uid;
  312.     private int gid;

  313.     /**
  314.      * Currently unused
  315.      */
  316.     private final DumpArchiveSummary summary = null;

  317.     /**
  318.      * This value is available from the standard index.
  319.      */
  320.     private final TapeSegmentHeader header = new TapeSegmentHeader();
  321.     private String simpleName;
  322.     private String originalName;

  323.     /**
  324.      * This value is available from the QFA index.
  325.      */
  326.     private int volume;
  327.     private long offset;
  328.     private int ino;

  329.     private int nlink;

  330.     private long ctime;

  331.     private int generation;

  332.     private boolean isDeleted;

  333.     /**
  334.      * Constructs a default instance.
  335.      */
  336.     public DumpArchiveEntry() {
  337.     }

  338.     /**
  339.      * Constructs a new instance with only names.
  340.      *
  341.      * @param name       path name
  342.      * @param simpleName actual file name.
  343.      */
  344.     public DumpArchiveEntry(final String name, final String simpleName) {
  345.         setName(name);
  346.         this.simpleName = simpleName;
  347.     }

  348.     /**
  349.      * Constructs a new instance with name, inode and type.
  350.      *
  351.      * @param name       the name
  352.      * @param simpleName the simple name
  353.      * @param ino        the ino
  354.      * @param type       the type
  355.      */
  356.     protected DumpArchiveEntry(final String name, final String simpleName, final int ino, final TYPE type) {
  357.         setType(type);
  358.         setName(name);
  359.         this.simpleName = simpleName;
  360.         this.ino = ino;
  361.         this.offset = 0;
  362.     }

  363.     @Override
  364.     public boolean equals(final Object o) {
  365.         if (o == this) {
  366.             return true;
  367.         }
  368.         if (o == null || !o.getClass().equals(getClass())) {
  369.             return false;
  370.         }

  371.         final DumpArchiveEntry rhs = (DumpArchiveEntry) o;

  372.         if (ino != rhs.ino) {
  373.             return false;
  374.         }

  375.         // summary is always null right now, but this may change some day
  376.         if (summary == null && rhs.summary != null // NOSONAR
  377.                 || summary != null && !summary.equals(rhs.summary)) { // NOSONAR
  378.             return false;
  379.         }

  380.         return true;
  381.     }

  382.     /**
  383.      * Returns the time the file was last accessed.
  384.      *
  385.      * @return the access time
  386.      */
  387.     public Date getAccessTime() {
  388.         return new Date(atime);
  389.     }

  390.     /**
  391.      * Gets file creation time.
  392.      *
  393.      * @return the creation time
  394.      */
  395.     public Date getCreationTime() {
  396.         return new Date(ctime);
  397.     }

  398.     /**
  399.      * Returns the size of the entry as read from the archive.
  400.      */
  401.     long getEntrySize() {
  402.         return size;
  403.     }

  404.     /**
  405.      * Gets the generation of the file.
  406.      *
  407.      * @return the generation
  408.      */
  409.     public int getGeneration() {
  410.         return generation;
  411.     }

  412.     /**
  413.      * Gets the group id
  414.      *
  415.      * @return the group id
  416.      */
  417.     public int getGroupId() {
  418.         return gid;
  419.     }

  420.     /**
  421.      * Gets the number of records in this segment.
  422.      *
  423.      * @return the number of records
  424.      */
  425.     public int getHeaderCount() {
  426.         return header.getCount();
  427.     }

  428.     /**
  429.      * Gets the number of sparse records in this segment.
  430.      *
  431.      * @return the number of sparse records
  432.      */
  433.     public int getHeaderHoles() {
  434.         return header.getHoles();
  435.     }

  436.     /**
  437.      * Gets the type of the tape segment header.
  438.      *
  439.      * @return the segment header
  440.      */
  441.     public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() {
  442.         return header.getType();
  443.     }

  444.     /**
  445.      * Returns the ino of the entry.
  446.      *
  447.      * @return the ino
  448.      */
  449.     public int getIno() {
  450.         return header.getIno();
  451.     }

  452.     /**
  453.      * The last modified date.
  454.      *
  455.      * @return the last modified date
  456.      */
  457.     @Override
  458.     public Date getLastModifiedDate() {
  459.         return new Date(mtime);
  460.     }

  461.     /**
  462.      * Gets the access permissions on the entry.
  463.      *
  464.      * @return the access permissions
  465.      */
  466.     public int getMode() {
  467.         return mode;
  468.     }

  469.     /**
  470.      * Returns the name of the entry.
  471.      *
  472.      * <p>
  473.      * This method returns the raw name as it is stored inside of the archive.
  474.      * </p>
  475.      *
  476.      * @return the name of the entry.
  477.      */
  478.     @Override
  479.     public String getName() {
  480.         return name;
  481.     }

  482.     /**
  483.      * Gets the number of hard links to the entry.
  484.      *
  485.      * @return the number of hard links
  486.      */
  487.     public int getNlink() {
  488.         return nlink;
  489.     }

  490.     /**
  491.      * Gets the offset within the archive
  492.      *
  493.      * @return the offset
  494.      */
  495.     public long getOffset() {
  496.         return offset;
  497.     }

  498.     /**
  499.      * Returns the unmodified name of the entry.
  500.      *
  501.      * @return the name of the entry.
  502.      */
  503.     String getOriginalName() {
  504.         return originalName;
  505.     }

  506.     /**
  507.      * Returns the permissions on the entry.
  508.      *
  509.      * @return the permissions
  510.      */
  511.     public Set<PERMISSION> getPermissions() {
  512.         return permissions;
  513.     }

  514.     /**
  515.      * Returns the path of the entry.
  516.      *
  517.      * @return the path of the entry.
  518.      */
  519.     public String getSimpleName() {
  520.         return simpleName;
  521.     }

  522.     /**
  523.      * Returns the size of the entry.
  524.      *
  525.      * @return the size
  526.      */
  527.     @Override
  528.     public long getSize() {
  529.         return isDirectory() ? SIZE_UNKNOWN : size;
  530.     }

  531.     /**
  532.      * Gets the type of the entry.
  533.      *
  534.      * @return the type
  535.      */
  536.     public TYPE getType() {
  537.         return type;
  538.     }

  539.     /**
  540.      * Gets the user id.
  541.      *
  542.      * @return the user id
  543.      */
  544.     public int getUserId() {
  545.         return uid;
  546.     }

  547.     /**
  548.      * Gets the tape volume where this file is located.
  549.      *
  550.      * @return the volume
  551.      */
  552.     public int getVolume() {
  553.         return volume;
  554.     }

  555.     @Override
  556.     public int hashCode() {
  557.         return ino;
  558.     }

  559.     /**
  560.      * Is this a block device?
  561.      *
  562.      * @return whether this is a block device
  563.      */
  564.     public boolean isBlkDev() {
  565.         return type == TYPE.BLKDEV;
  566.     }

  567.     /**
  568.      * Is this a character device?
  569.      *
  570.      * @return whether this is a character device
  571.      */
  572.     public boolean isChrDev() {
  573.         return type == TYPE.CHRDEV;
  574.     }

  575.     /**
  576.      * Has this file been deleted? (On valid on incremental dumps.)
  577.      *
  578.      * @return whether the file has been deleted
  579.      */
  580.     public boolean isDeleted() {
  581.         return isDeleted;
  582.     }

  583.     /**
  584.      * Is this a directory?
  585.      *
  586.      * @return whether this is a directory
  587.      */
  588.     @Override
  589.     public boolean isDirectory() {
  590.         return type == TYPE.DIRECTORY;
  591.     }

  592.     /**
  593.      * Is this a fifo/pipe?
  594.      *
  595.      * @return whether this is a fifo
  596.      */
  597.     public boolean isFifo() {
  598.         return type == TYPE.FIFO;
  599.     }

  600.     /**
  601.      * Is this a regular file?
  602.      *
  603.      * @return whether this is a regular file
  604.      */
  605.     public boolean isFile() {
  606.         return type == TYPE.FILE;
  607.     }

  608.     /**
  609.      * Is this a network device?
  610.      *
  611.      * @return whether this is a socket
  612.      */
  613.     public boolean isSocket() {
  614.         return type == TYPE.SOCKET;
  615.     }

  616.     /**
  617.      * Is this a sparse record?
  618.      *
  619.      * @param idx index of the record to check
  620.      * @return whether this is a sparse record
  621.      */
  622.     public boolean isSparseRecord(final int idx) {
  623.         return (header.getCdata(idx) & 0x01) == 0;
  624.     }

  625.     /**
  626.      * Sets the time the file was last accessed.
  627.      *
  628.      * @param atime the access time
  629.      */
  630.     public void setAccessTime(final Date atime) {
  631.         this.atime = atime.getTime();
  632.     }

  633.     /**
  634.      * Sets the file creation time.
  635.      *
  636.      * @param ctime the creation time
  637.      */
  638.     public void setCreationTime(final Date ctime) {
  639.         this.ctime = ctime.getTime();
  640.     }

  641.     /**
  642.      * Sets whether this file has been deleted.
  643.      *
  644.      * @param isDeleted whether the file has been deleted
  645.      */
  646.     public void setDeleted(final boolean isDeleted) {
  647.         this.isDeleted = isDeleted;
  648.     }

  649.     /**
  650.      * Sets the generation of the file.
  651.      *
  652.      * @param generation the generation
  653.      */
  654.     public void setGeneration(final int generation) {
  655.         this.generation = generation;
  656.     }

  657.     /**
  658.      * Sets the group id.
  659.      *
  660.      * @param gid the group id
  661.      */
  662.     public void setGroupId(final int gid) {
  663.         this.gid = gid;
  664.     }

  665.     /**
  666.      * Sets the time the file was last modified.
  667.      *
  668.      * @param mtime the last modified time
  669.      */
  670.     public void setLastModifiedDate(final Date mtime) {
  671.         this.mtime = mtime.getTime();
  672.     }

  673.     /**
  674.      * Sets the access permissions on the entry.
  675.      *
  676.      * @param mode the access permissions
  677.      */
  678.     public void setMode(final int mode) {
  679.         this.mode = mode & 07777;
  680.         this.permissions = PERMISSION.find(mode);
  681.     }

  682.     /**
  683.      * Sets the name of the entry.
  684.      *
  685.      * @param name the name
  686.      */
  687.     public final void setName(String name) {
  688.         this.originalName = name;
  689.         if (name != null) {
  690.             if (isDirectory() && !name.endsWith("/")) {
  691.                 name += "/";
  692.             }
  693.             if (name.startsWith("./")) {
  694.                 name = name.substring(2);
  695.             }
  696.         }
  697.         this.name = name;
  698.     }

  699.     /**
  700.      * Sets the number of hard links.
  701.      *
  702.      * @param nlink the number of hard links
  703.      */
  704.     public void setNlink(final int nlink) {
  705.         this.nlink = nlink;
  706.     }

  707.     /**
  708.      * Sets the offset within the archive.
  709.      *
  710.      * @param offset the offset
  711.      */
  712.     public void setOffset(final long offset) {
  713.         this.offset = offset;
  714.     }

  715.     /**
  716.      * Sets the path of the entry.
  717.      *
  718.      * @param simpleName the simple name
  719.      */
  720.     protected void setSimpleName(final String simpleName) {
  721.         this.simpleName = simpleName;
  722.     }

  723.     /**
  724.      * Sets the size of the entry.
  725.      *
  726.      * @param size the size
  727.      */
  728.     public void setSize(final long size) {
  729.         this.size = size;
  730.     }

  731.     /**
  732.      * Sets the type of the entry.
  733.      *
  734.      * @param type the type
  735.      */
  736.     public void setType(final TYPE type) {
  737.         this.type = type;
  738.     }

  739.     /**
  740.      * Sets the user id.
  741.      *
  742.      * @param uid the user id
  743.      */
  744.     public void setUserId(final int uid) {
  745.         this.uid = uid;
  746.     }

  747.     /**
  748.      * Sets the tape volume.
  749.      *
  750.      * @param volume the volume
  751.      */
  752.     public void setVolume(final int volume) {
  753.         this.volume = volume;
  754.     }

  755.     @Override
  756.     public String toString() {
  757.         return getName();
  758.     }

  759.     /**
  760.      * Update entry with information from next tape segment header.
  761.      */
  762.     void update(final byte[] buffer) {
  763.         header.volume = DumpArchiveUtil.convert32(buffer, 16);
  764.         header.count = DumpArchiveUtil.convert32(buffer, 160);

  765.         header.holes = 0;

  766.         for (int i = 0; i < 512 && i < header.count; i++) {
  767.             if (buffer[164 + i] == 0) {
  768.                 header.holes++;
  769.             }
  770.         }

  771.         System.arraycopy(buffer, 164, header.cdata, 0, 512);
  772.     }
  773. }