View Javadoc
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   *   https://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  
21  import java.util.Collections;
22  import java.util.Date;
23  import java.util.EnumSet;
24  import java.util.HashSet;
25  import java.util.Set;
26  
27  import org.apache.commons.compress.archivers.ArchiveEntry;
28  
29  /**
30   * This class represents an entry in a Dump archive. It consists of the entry's header, the entry's File and any extended attributes.
31   * <p>
32   * DumpEntries that are created from the header bytes read from an archive are instantiated with the DumpArchiveEntry( byte[] ) constructor. These entries will
33   * 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
34   * to null, since they reference an archive entry not a file.
35   * <p>
36   * 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
37   * 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
38   * and the File is set to null.
39   *
40   * <p>
41   * The C structure for a Dump Entry's header is:
42   *
43   * <pre>
44   * #define TP_BSIZE    1024          // size of each file block
45   * #define NTREC       10            // number of blocks to write at once
46   * #define HIGHDENSITYTREC 32        // number of blocks to write on high-density tapes
47   * #define TP_NINDIR   (TP_BSIZE/2)  // number if indirect inodes in record
48   * #define TP_NINOS    (TP_NINDIR / sizeof (int32_t))
49   * #define LBLSIZE     16
50   * #define NAMELEN     64
51   *
52   * #define OFS_MAGIC     (int) 60011  // old format magic value
53   * #define NFS_MAGIC     (int) 60012  // new format magic value
54   * #define FS_UFS2_MAGIC (int) 0x19540119
55   * #define CHECKSUM      (int) 84446  // constant used in checksum algorithm
56   *
57   * struct  s_spcl {
58   *   int32_t c_type;             // record type (see below)
59   *   int32_t <strong>c_date</strong>;             // date of this dump
60   *   int32_t <strong>c_ddate</strong>;            // date of previous dump
61   *   int32_t c_volume;           // dump volume number
62   *   u_int32_t c_tapea;          // logical block of this record
63   *   dump_ino_t c_ino;           // number of inode
64   *   int32_t <strong>c_magic</strong>;            // magic number (see above)
65   *   int32_t c_checksum;         // record checksum
66   * #ifdef  __linux__
67   *   struct  new_bsd_inode c_dinode;
68   * #else
69   * #ifdef sunos
70   *   struct  new_bsd_inode c_dinode;
71   * #else
72   *   struct  dinode  c_dinode;   // ownership and mode of inode
73   * #endif
74   * #endif
75   *   int32_t c_count;            // number of valid c_addr entries
76   *   union u_data c_data;        // see above
77   *   char    <strong>c_label[LBLSIZE]</strong>;   // dump label
78   *   int32_t <strong>c_level</strong>;            // level of this dump
79   *   char    <strong>c_filesys[NAMELEN]</strong>; // name of dumpped file system
80   *   char    <strong>c_dev[NAMELEN]</strong>;     // name of dumpped device
81   *   char    <strong>c_host[NAMELEN]</strong>;    // name of dumpped host
82   *   int32_t c_flags;            // additional information (see below)
83   *   int32_t c_firstrec;         // first record on volume
84   *   int32_t c_ntrec;            // blocksize on volume
85   *   int32_t c_extattributes;    // additional inode info (see below)
86   *   int32_t c_spare[30];        // reserved for future uses
87   * } s_spcl;
88   *
89   * //
90   * // flag values
91   * //
92   * #define DR_NEWHEADER     0x0001  // new format tape header
93   * #define DR_NEWINODEFMT   0x0002  // new format inodes on tape
94   * #define DR_COMPRESSED    0x0080  // dump tape is compressed
95   * #define DR_METAONLY      0x0100  // only the metadata of the inode has been dumped
96   * #define DR_INODEINFO     0x0002  // [SIC] TS_END header contains c_inos information
97   * #define DR_EXTATTRIBUTES 0x8000
98   *
99   * //
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  */
174 public 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 }