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