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