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    }