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