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.cpio;
020
021import java.io.File;
022import java.io.IOException;
023import java.nio.charset.Charset;
024import java.nio.file.Files;
025import java.nio.file.LinkOption;
026import java.nio.file.Path;
027import java.nio.file.attribute.FileTime;
028import java.util.Date;
029import java.util.Objects;
030
031import org.apache.commons.compress.archivers.ArchiveEntry;
032import org.apache.commons.compress.utils.ExactMath;
033import org.apache.commons.compress.utils.TimeUtils;
034
035/**
036 * A cpio archive consists of a sequence of files. There are several types of
037 * headers defined in two categories of new and old format. The headers are
038 * recognized by magic numbers:
039 *
040 * <ul>
041 * <li>"070701" ASCII for new portable format</li>
042 * <li>"070702" ASCII for new portable format with CRC</li>
043 * <li>"070707" ASCII for old ascii (also known as Portable ASCII, odc or old
044 * character format</li>
045 * <li>070707 binary for old binary</li>
046 * </ul>
047 *
048 * <p>The old binary format is limited to 16 bits for user id, group
049 * id, device, and inode numbers. It is limited to 4 gigabyte file
050 * sizes.
051 *
052 * The old ASCII format is limited to 18 bits for the user id, group
053 * id, device, and inode numbers. It is limited to 8 gigabyte file
054 * sizes.
055 *
056 * The new ASCII format is limited to 4 gigabyte file sizes.
057 *
058 * CPIO 2.5 knows also about tar, but it is not recognized here.</p>
059 *
060 *
061 * <h2>OLD FORMAT</h2>
062 *
063 * <p>Each file has a 76 (ascii) / 26 (binary) byte header, a variable
064 * length, NUL terminated file name, and variable length file data. A
065 * header for a file name "TRAILER!!!" indicates the end of the
066 * archive.</p>
067 *
068 * <p>All the fields in the header are ISO 646 (approximately ASCII)
069 * strings of octal numbers, left padded, not NUL terminated.</p>
070 *
071 * <pre>
072 * FIELDNAME        NOTES
073 * c_magic          The integer value octal 070707.  This value can be used to deter-
074 *                  mine whether this archive is written with little-endian or big-
075 *                  endian integers.
076 * c_dev            Device that contains a directory entry for this file
077 * c_ino            I-node number that identifies the input file to the file system
078 * c_mode           The mode specifies both the regular permissions and the file type.
079 * c_uid            Numeric User ID of the owner of the input file
080 * c_gid            Numeric Group ID of the owner of the input file
081 * c_nlink          Number of links that are connected to the input file
082 * c_rdev           For block special and character special entries, this field
083 *                  contains the associated device number.  For all other entry types,
084 *                  it should be set to zero by writers and ignored by readers.
085 * c_mtime[2]       Modification time of the file, indicated as the number of seconds
086 *                  since the start of the epoch, 00:00:00 UTC January 1, 1970.  The
087 *                  four-byte integer is stored with the most-significant 16 bits
088 *                  first followed by the least-significant 16 bits.  Each of the two
089 *                  16 bit values are stored in machine-native byte order.
090 * c_namesize       Length of the path name, including the terminating null byte
091 * c_filesize[2]    Length of the file in bytes. This is the length of the data
092 *                  section that follows the header structure. Must be 0 for
093 *                  FIFOs and directories
094 *
095 * All fields are unsigned short fields with 16-bit integer values
096 * apart from c_mtime and c_filesize which are 32-bit integer values
097 * </pre>
098 *
099 * <p>If necessary, the file name and file data are padded with a NUL byte to an even length</p>
100 *
101 * <p>Special files, directories, and the trailer are recorded with
102 * the h_filesize field equal to 0.</p>
103 *
104 * <p>In the ASCII version of this format, the 16-bit entries are represented as 6-byte octal numbers,
105 * and the 32-bit entries are represented as 11-byte octal numbers. No padding is added.</p>
106 *
107 * <h3>NEW FORMAT</h3>
108 *
109 * <p>Each file has a 110 byte header, a variable length, NUL
110 * terminated file name, and variable length file data. A header for a
111 * file name "TRAILER!!!" indicates the end of the archive. All the
112 * fields in the header are ISO 646 (approximately ASCII) strings of
113 * hexadecimal numbers, left padded, not NUL terminated.</p>
114 *
115 * <pre>
116 * FIELDNAME        NOTES
117 * c_magic[6]       The string 070701 for new ASCII, the string 070702 for new ASCII with CRC
118 * c_ino[8]
119 * c_mode[8]
120 * c_uid[8]
121 * c_gid[8]
122 * c_nlink[8]
123 * c_mtim[8]
124 * c_filesize[8]    must be 0 for FIFOs and directories
125 * c_maj[8]
126 * c_min[8]
127 * c_rmaj[8]        only valid for chr and blk special files
128 * c_rmin[8]        only valid for chr and blk special files
129 * c_namesize[8]    count includes terminating NUL in pathname
130 * c_check[8]       0 for "new" portable format; for CRC format
131 *                  the sum of all the bytes in the file
132 * </pre>
133 *
134 * <p>New ASCII Format The "new" ASCII format uses 8-byte hexadecimal
135 * fields for all numbers and separates device numbers into separate
136 * fields for major and minor numbers.</p>
137 *
138 * <p>The pathname is followed by NUL bytes so that the total size of
139 * the fixed header plus pathname is a multiple of four. Likewise, the
140 * file data is padded to a multiple of four bytes.</p>
141 *
142 * <p>This class uses mutable fields and is not considered to be
143 * threadsafe.</p>
144 *
145 * <p>Based on code from the jRPM project (http://jrpm.sourceforge.net).</p>
146 *
147 * <p>The MAGIC numbers and other constants are defined in {@link CpioConstants}</p>
148 *
149 * <p>
150 * N.B. does not handle the cpio "tar" format
151 * </p>
152 * @NotThreadSafe
153 * @see <a href="https://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt">https://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt</a>
154 */
155public class CpioArchiveEntry implements CpioConstants, ArchiveEntry {
156
157    // Header description fields - should be same throughout an archive
158
159    /**
160     * See {@link #CpioArchiveEntry(short)} for possible values.
161     */
162    private final short fileFormat;
163
164    /** The number of bytes in each header record; depends on the file format */
165    private final int headerSize;
166
167    /** The boundary to which the header and data elements are aligned: 0, 2 or 4 bytes */
168    private final int alignmentBoundary;
169
170    // Header fields
171
172    private long chksum;
173
174    /** Number of bytes in the file */
175    private long filesize;
176
177    private long gid;
178
179    private long inode;
180
181    private long maj;
182
183    private long min;
184
185    private long mode;
186
187    private long mtime;
188
189    private String name;
190
191    private long nlink;
192
193    private long rmaj;
194
195    private long rmin;
196
197    private long uid;
198
199    /**
200     * Creates a CpioArchiveEntry with a specified name for a
201     * specified file. The format of this entry will be the new
202     * format.
203     *
204     * @param inputFile
205     *            The file to gather information from.
206     * @param entryName
207     *            The name of this entry.
208     */
209    public CpioArchiveEntry(final File inputFile, final String entryName) {
210        this(FORMAT_NEW, inputFile, entryName);
211    }
212
213    /**
214     * Creates a CpioArchiveEntry with a specified name for a
215     * specified file. The format of this entry will be the new
216     * format.
217     *
218     * @param inputPath
219     *            The file to gather information from.
220     * @param entryName
221     *            The name of this entry.
222     * @param options options indicating how symbolic links are handled.
223     * @throws IOException if an I/O error occurs
224     * @since 1.21
225     */
226    public CpioArchiveEntry(final Path inputPath, final String entryName, final LinkOption... options) throws IOException {
227        this(FORMAT_NEW, inputPath, entryName, options);
228    }
229
230    /**
231     * Creates a CpioArchiveEntry with a specified format.
232     *
233     * @param format
234     *            The cpio format for this entry.
235     * <p>
236     * Possible format values are:
237     * <pre>
238     * CpioConstants.FORMAT_NEW
239     * CpioConstants.FORMAT_NEW_CRC
240     * CpioConstants.FORMAT_OLD_BINARY
241     * CpioConstants.FORMAT_OLD_ASCII
242     * </pre>
243     */
244    public CpioArchiveEntry(final short format) {
245        switch (format) {
246        case FORMAT_NEW:
247            this.headerSize = 110;
248            this.alignmentBoundary = 4;
249            break;
250        case FORMAT_NEW_CRC:
251            this.headerSize = 110;
252            this.alignmentBoundary = 4;
253            break;
254        case FORMAT_OLD_ASCII:
255            this.headerSize = 76;
256            this.alignmentBoundary = 0;
257            break;
258        case FORMAT_OLD_BINARY:
259            this.headerSize = 26;
260            this.alignmentBoundary = 2;
261            break;
262        default:
263            throw new IllegalArgumentException("Unknown header type " + format);
264        }
265        this.fileFormat = format;
266    }
267
268    /**
269     * Creates a CpioArchiveEntry with a specified name for a
270     * specified file.
271     *
272     * @param format
273     *            The cpio format for this entry.
274     * @param inputFile
275     *            The file to gather information from.
276     * @param entryName
277     *            The name of this entry.
278     * <p>
279     * Possible format values are:
280     * <pre>
281     * CpioConstants.FORMAT_NEW
282     * CpioConstants.FORMAT_NEW_CRC
283     * CpioConstants.FORMAT_OLD_BINARY
284     * CpioConstants.FORMAT_OLD_ASCII
285     * </pre>
286     *
287     * @since 1.1
288     */
289    public CpioArchiveEntry(final short format, final File inputFile,
290                            final String entryName) {
291        this(format, entryName, inputFile.isFile() ? inputFile.length() : 0);
292        if (inputFile.isDirectory()){
293            setMode(C_ISDIR);
294        } else if (inputFile.isFile()){
295            setMode(C_ISREG);
296        } else {
297            throw new IllegalArgumentException("Cannot determine type of file "
298                                               + inputFile.getName());
299        }
300        // TODO set other fields as needed
301        setTime(inputFile.lastModified() / 1000);
302    }
303
304    /**
305     * Creates a CpioArchiveEntry with a specified name for a
306     * specified path.
307     *
308     * @param format
309     *            The cpio format for this entry.
310     * @param inputPath
311     *            The file to gather information from.
312     * @param entryName
313     *            The name of this entry.
314     * <p>
315     * Possible format values are:
316     * <pre>
317     * CpioConstants.FORMAT_NEW
318     * CpioConstants.FORMAT_NEW_CRC
319     * CpioConstants.FORMAT_OLD_BINARY
320     * CpioConstants.FORMAT_OLD_ASCII
321     * </pre>
322     * @param options options indicating how symbolic links are handled.
323     *
324     * @throws IOException if an I/O error occurs
325     * @since 1.21
326     */
327    public CpioArchiveEntry(final short format, final Path inputPath, final String entryName, final LinkOption... options)
328        throws IOException {
329        this(format, entryName, Files.isRegularFile(inputPath, options) ? Files.size(inputPath) : 0);
330        if (Files.isDirectory(inputPath, options)) {
331            setMode(C_ISDIR);
332        } else if (Files.isRegularFile(inputPath, options)) {
333            setMode(C_ISREG);
334        } else {
335            throw new IllegalArgumentException("Cannot determine type of file " + inputPath);
336        }
337        // TODO set other fields as needed
338        setTime(Files.getLastModifiedTime(inputPath, options));
339    }
340
341    /**
342     * Creates a CpioArchiveEntry with a specified name.
343     *
344     * @param format
345     *            The cpio format for this entry.
346     * @param name
347     *            The name of this entry.
348     * <p>
349     * Possible format values are:
350     * <pre>
351     * CpioConstants.FORMAT_NEW
352     * CpioConstants.FORMAT_NEW_CRC
353     * CpioConstants.FORMAT_OLD_BINARY
354     * CpioConstants.FORMAT_OLD_ASCII
355     * </pre>
356     *
357     * @since 1.1
358     */
359    public CpioArchiveEntry(final short format, final String name) {
360        this(format);
361        this.name = name;
362    }
363
364    /**
365     * Creates a CpioArchiveEntry with a specified name.
366     *
367     * @param format
368     *            The cpio format for this entry.
369     * @param name
370     *            The name of this entry.
371     * @param size
372     *            The size of this entry
373     * <p>
374     * Possible format values are:
375     * <pre>
376     * CpioConstants.FORMAT_NEW
377     * CpioConstants.FORMAT_NEW_CRC
378     * CpioConstants.FORMAT_OLD_BINARY
379     * CpioConstants.FORMAT_OLD_ASCII
380     * </pre>
381     *
382     * @since 1.1
383     */
384    public CpioArchiveEntry(final short format, final String name,
385                            final long size) {
386        this(format, name);
387        this.setSize(size);
388    }
389
390    /**
391     * Creates a CpioArchiveEntry with a specified name. The format of
392     * this entry will be the new format.
393     *
394     * @param name
395     *            The name of this entry.
396     */
397    public CpioArchiveEntry(final String name) {
398        this(FORMAT_NEW, name);
399    }
400
401    /**
402     * Creates a CpioArchiveEntry with a specified name. The format of
403     * this entry will be the new format.
404     *
405     * @param name
406     *            The name of this entry.
407     * @param size
408     *            The size of this entry
409     */
410    public CpioArchiveEntry(final String name, final long size) {
411        this(name);
412        this.setSize(size);
413    }
414
415    /**
416     * Checks if the method is allowed for the defined format.
417     */
418    private void checkNewFormat() {
419        if ((this.fileFormat & FORMAT_NEW_MASK) == 0) {
420            throw new UnsupportedOperationException();
421        }
422    }
423
424    /**
425     * Checks if the method is allowed for the defined format.
426     */
427    private void checkOldFormat() {
428        if ((this.fileFormat & FORMAT_OLD_MASK) == 0) {
429            throw new UnsupportedOperationException();
430        }
431    }
432
433    /* (non-Javadoc)
434     * @see Object#equals(Object)
435     */
436    @Override
437    public boolean equals(final Object obj) {
438        if (this == obj) {
439            return true;
440        }
441        if (obj == null || getClass() != obj.getClass()) {
442            return false;
443        }
444        final CpioArchiveEntry other = (CpioArchiveEntry) obj;
445        if (name == null) {
446            return other.name == null;
447        }
448        return name.equals(other.name);
449    }
450
451    /**
452     * Gets the alignment boundary for this CPIO format
453     *
454     * @return Returns the aligment boundary (0, 2, 4) in bytes
455     */
456    public int getAlignmentBoundary() {
457        return this.alignmentBoundary;
458    }
459
460    /**
461     * Gets the checksum.
462     * Only supported for the new formats.
463     *
464     * @return Returns the checksum.
465     * @throws UnsupportedOperationException if the format is not a new format
466     */
467    public long getChksum() {
468        checkNewFormat();
469        return this.chksum & 0xFFFFFFFFL;
470    }
471
472    /**
473     * Gets the number of bytes needed to pad the data to the alignment boundary.
474     *
475     * @return the number of bytes needed to pad the data (0,1,2,3)
476     */
477    public int getDataPadCount() {
478        if (this.alignmentBoundary == 0) {
479            return 0;
480        }
481        final long size = this.filesize;
482        final int remain = (int) (size % this.alignmentBoundary);
483        if (remain > 0) {
484            return this.alignmentBoundary - remain;
485        }
486        return 0;
487    }
488
489    /**
490     * Gets the device id.
491     *
492     * @return Returns the device id.
493     * @throws UnsupportedOperationException
494     *             if this method is called for a CpioArchiveEntry with a new
495     *             format.
496     */
497    public long getDevice() {
498        checkOldFormat();
499        return this.min;
500    }
501
502    /**
503     * Gets the major device id.
504     *
505     * @return Returns the major device id.
506     * @throws UnsupportedOperationException
507     *             if this method is called for a CpioArchiveEntry with an old
508     *             format.
509     */
510    public long getDeviceMaj() {
511        checkNewFormat();
512        return this.maj;
513    }
514
515    /**
516     * Gets the minor device id
517     *
518     * @return Returns the minor device id.
519     * @throws UnsupportedOperationException if format is not a new format
520     */
521    public long getDeviceMin() {
522        checkNewFormat();
523        return this.min;
524    }
525
526    /**
527     * Gets the format for this entry.
528     *
529     * @return Returns the format.
530     */
531    public short getFormat() {
532        return this.fileFormat;
533    }
534
535    /**
536     * Gets the group id.
537     *
538     * @return Returns the group id.
539     */
540    public long getGID() {
541        return this.gid;
542    }
543
544    /**
545     * Gets the number of bytes needed to pad the header to the alignment boundary.
546     *
547     * @deprecated This method doesn't properly work for multi-byte encodings. And
548     *             creates corrupt archives. Use {@link #getHeaderPadCount(Charset)}
549     *             or {@link #getHeaderPadCount(long)} in any case.
550     * @return the number of bytes needed to pad the header (0,1,2,3)
551     */
552    @Deprecated
553    public int getHeaderPadCount(){
554        return getHeaderPadCount(null);
555    }
556
557    /**
558     * Gets the number of bytes needed to pad the header to the alignment boundary.
559     *
560     * @param charset
561     *             The character set used to encode the entry name in the stream.
562     * @return the number of bytes needed to pad the header (0,1,2,3)
563     * @since 1.18
564     */
565    public int getHeaderPadCount(final Charset charset) {
566        if (name == null) {
567            return 0;
568        }
569        if (charset == null) {
570            return getHeaderPadCount(name.length());
571        }
572        return getHeaderPadCount(name.getBytes(charset).length);
573    }
574
575    /**
576     * Gets the number of bytes needed to pad the header to the alignment boundary.
577     *
578     * @param nameSize
579     *            The length of the name in bytes, as read in the stream.
580     *            Without the trailing zero byte.
581     * @return the number of bytes needed to pad the header (0,1,2,3)
582     *
583     * @since 1.18
584     */
585    public int getHeaderPadCount(final long nameSize) {
586        if (this.alignmentBoundary == 0) {
587            return 0;
588        }
589        int size = this.headerSize + 1; // Name has terminating null
590        if (name != null) {
591            size = ExactMath.add(size, nameSize);
592        }
593        final int remain = size % this.alignmentBoundary;
594        if (remain > 0) {
595            return this.alignmentBoundary - remain;
596        }
597        return 0;
598    }
599
600    /**
601     * Gets the header size for this CPIO format
602     *
603     * @return Returns the header size in bytes.
604     */
605    public int getHeaderSize() {
606        return this.headerSize;
607    }
608
609    /**
610     * Sets the inode.
611     *
612     * @return Returns the inode.
613     */
614    public long getInode() {
615        return this.inode;
616    }
617
618    @Override
619    public Date getLastModifiedDate() {
620        return new Date(1000 * getTime());
621    }
622
623    /**
624     * Gets the mode of this entry (e.g. directory, regular file).
625     *
626     * @return Returns the mode.
627     */
628    public long getMode() {
629        return mode == 0 && !CPIO_TRAILER.equals(name) ? C_ISREG : mode;
630    }
631
632    /**
633     * Gets the name.
634     *
635     * <p>This method returns the raw name as it is stored inside of the archive.</p>
636     *
637     * @return Returns the name.
638     */
639    @Override
640    public String getName() {
641        return this.name;
642    }
643
644    /**
645     * Gets the number of links.
646     *
647     * @return Returns the number of links.
648     */
649    public long getNumberOfLinks() {
650        return nlink == 0 ? isDirectory() ? 2 : 1 : nlink;
651    }
652
653    /**
654     * Gets the remote device id.
655     *
656     * @return Returns the remote device id.
657     * @throws UnsupportedOperationException
658     *             if this method is called for a CpioArchiveEntry with a new
659     *             format.
660     */
661    public long getRemoteDevice() {
662        checkOldFormat();
663        return this.rmin;
664    }
665
666    /**
667     * Gets the remote major device id.
668     *
669     * @return Returns the remote major device id.
670     * @throws UnsupportedOperationException
671     *             if this method is called for a CpioArchiveEntry with an old
672     *             format.
673     */
674    public long getRemoteDeviceMaj() {
675        checkNewFormat();
676        return this.rmaj;
677    }
678
679    /**
680     * Gets the remote minor device id.
681     *
682     * @return Returns the remote minor device id.
683     * @throws UnsupportedOperationException
684     *             if this method is called for a CpioArchiveEntry with an old
685     *             format.
686     */
687    public long getRemoteDeviceMin() {
688        checkNewFormat();
689        return this.rmin;
690    }
691
692    /**
693     * Gets the filesize.
694     *
695     * @return Returns the filesize.
696     * @see org.apache.commons.compress.archivers.ArchiveEntry#getSize()
697     */
698    @Override
699    public long getSize() {
700        return this.filesize;
701    }
702
703    /**
704     * Gets the time in seconds.
705     *
706     * @return Returns the time.
707     */
708    public long getTime() {
709        return this.mtime;
710    }
711
712    /**
713     * Gets the user id.
714     *
715     * @return Returns the user id.
716     */
717    public long getUID() {
718        return this.uid;
719    }
720
721    /* (non-Javadoc)
722     * @see Object#hashCode()
723     */
724    @Override
725    public int hashCode() {
726        return Objects.hash(name);
727    }
728
729    /**
730     * Checks if this entry represents a block device.
731     *
732     * @return TRUE if this entry is a block device.
733     */
734    public boolean isBlockDevice() {
735        return CpioUtil.fileType(mode) == C_ISBLK;
736    }
737
738    /**
739     * Checks if this entry represents a character device.
740     *
741     * @return TRUE if this entry is a character device.
742     */
743    public boolean isCharacterDevice() {
744        return CpioUtil.fileType(mode) == C_ISCHR;
745    }
746
747    /**
748     * Checks if this entry represents a directory.
749     *
750     * @return TRUE if this entry is a directory.
751     */
752    @Override
753    public boolean isDirectory() {
754        return CpioUtil.fileType(mode) == C_ISDIR;
755    }
756
757    /**
758     * Checks if this entry represents a network device.
759     *
760     * @return TRUE if this entry is a network device.
761     */
762    public boolean isNetwork() {
763        return CpioUtil.fileType(mode) == C_ISNWK;
764    }
765
766    /**
767     * Checks if this entry represents a pipe.
768     *
769     * @return TRUE if this entry is a pipe.
770     */
771    public boolean isPipe() {
772        return CpioUtil.fileType(mode) == C_ISFIFO;
773    }
774
775    /**
776     * Checks if this entry represents a regular file.
777     *
778     * @return TRUE if this entry is a regular file.
779     */
780    public boolean isRegularFile() {
781        return CpioUtil.fileType(mode) == C_ISREG;
782    }
783
784    /**
785     * Checks if this entry represents a socket.
786     *
787     * @return TRUE if this entry is a socket.
788     */
789    public boolean isSocket() {
790        return CpioUtil.fileType(mode) == C_ISSOCK;
791    }
792
793    /**
794     * Checks if this entry represents a symbolic link.
795     *
796     * @return TRUE if this entry is a symbolic link.
797     */
798    public boolean isSymbolicLink() {
799        return CpioUtil.fileType(mode) == C_ISLNK;
800    }
801
802    /**
803     * Sets the checksum. The checksum is calculated by adding all bytes of a
804     * file to transfer (crc += buf[pos] &amp; 0xFF).
805     *
806     * @param chksum
807     *            The checksum to set.
808     */
809    public void setChksum(final long chksum) {
810        checkNewFormat();
811        this.chksum = chksum & 0xFFFFFFFFL;
812    }
813
814    /**
815     * Sets the device id.
816     *
817     * @param device
818     *            The device id to set.
819     * @throws UnsupportedOperationException
820     *             if this method is called for a CpioArchiveEntry with a new
821     *             format.
822     */
823    public void setDevice(final long device) {
824        checkOldFormat();
825        this.min = device;
826    }
827
828    /**
829     * Sets major device id.
830     *
831     * @param maj
832     *            The major device id to set.
833     */
834    public void setDeviceMaj(final long maj) {
835        checkNewFormat();
836        this.maj = maj;
837    }
838
839    /**
840     * Sets the minor device id
841     *
842     * @param min
843     *            The minor device id to set.
844     */
845    public void setDeviceMin(final long min) {
846        checkNewFormat();
847        this.min = min;
848    }
849
850    /**
851     * Sets the group id.
852     *
853     * @param gid
854     *            The group id to set.
855     */
856    public void setGID(final long gid) {
857        this.gid = gid;
858    }
859
860    /**
861     * Sets the inode.
862     *
863     * @param inode
864     *            The inode to set.
865     */
866    public void setInode(final long inode) {
867        this.inode = inode;
868    }
869
870    /**
871     * Sets the mode of this entry (e.g. directory, regular file).
872     *
873     * @param mode
874     *            The mode to set.
875     */
876    public void setMode(final long mode) {
877        final long maskedMode = mode & S_IFMT;
878        switch ((int) maskedMode) {
879        case C_ISDIR:
880        case C_ISLNK:
881        case C_ISREG:
882        case C_ISFIFO:
883        case C_ISCHR:
884        case C_ISBLK:
885        case C_ISSOCK:
886        case C_ISNWK:
887            break;
888        default:
889            throw new IllegalArgumentException(
890                                               "Unknown mode. "
891                                               + "Full: " + Long.toHexString(mode)
892                                               + " Masked: " + Long.toHexString(maskedMode));
893        }
894
895        this.mode = mode;
896    }
897
898    /**
899     * Sets the name.
900     *
901     * @param name
902     *            The name to set.
903     */
904    public void setName(final String name) {
905        this.name = name;
906    }
907
908    /**
909     * Sets the number of links.
910     *
911     * @param nlink
912     *            The number of links to set.
913     */
914    public void setNumberOfLinks(final long nlink) {
915        this.nlink = nlink;
916    }
917
918    /**
919     * Sets the remote device id.
920     *
921     * @param device
922     *            The remote device id to set.
923     * @throws UnsupportedOperationException
924     *             if this method is called for a CpioArchiveEntry with a new
925     *             format.
926     */
927    public void setRemoteDevice(final long device) {
928        checkOldFormat();
929        this.rmin = device;
930    }
931
932    /**
933     * Sets the remote major device id.
934     *
935     * @param rmaj
936     *            The remote major device id to set.
937     * @throws UnsupportedOperationException
938     *             if this method is called for a CpioArchiveEntry with an old
939     *             format.
940     */
941    public void setRemoteDeviceMaj(final long rmaj) {
942        checkNewFormat();
943        this.rmaj = rmaj;
944    }
945
946    /**
947     * Sets the remote minor device id.
948     *
949     * @param rmin
950     *            The remote minor device id to set.
951     * @throws UnsupportedOperationException
952     *             if this method is called for a CpioArchiveEntry with an old
953     *             format.
954     */
955    public void setRemoteDeviceMin(final long rmin) {
956        checkNewFormat();
957        this.rmin = rmin;
958    }
959
960    /**
961     * Sets the filesize.
962     *
963     * @param size
964     *            The filesize to set.
965     */
966    public void setSize(final long size) {
967        if (size < 0 || size > 0xFFFFFFFFL) {
968            throw new IllegalArgumentException("Invalid entry size <" + size + ">");
969        }
970        this.filesize = size;
971    }
972
973    /**
974     * Sets the time.
975     *
976     * @param time
977     *            The time to set.
978     */
979    public void setTime(final FileTime time) {
980        this.mtime = TimeUtils.toUnixTime(time);
981    }
982
983    /**
984     * Sets the time in seconds.
985     *
986     * @param time
987     *            The time to set.
988     */
989    public void setTime(final long time) {
990        this.mtime = time;
991    }
992
993    /**
994     * Sets the user id.
995     *
996     * @param uid
997     *            The user id to set.
998     */
999    public void setUID(final long uid) {
1000        this.uid = uid;
1001    }
1002}