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 constructor documenation 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 * Creates 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 * Creates a CPIOArchiveEntry with a specified name. The format of
230 * this entry 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, name);
237 }
238
239 /**
240 * Creates a CPIOArchiveEntry with a specified name.
241 *
242 * @param format
243 * The cpio format for this entry.
244 * @param name
245 * The name of this entry.
246 * <br/>
247 * Possible format values are:
248 * <p>
249 * CpioConstants.FORMAT_NEW<br/>
250 * CpioConstants.FORMAT_NEW_CRC<br/>
251 * CpioConstants.FORMAT_OLD_BINARY<br/>
252 * CpioConstants.FORMAT_OLD_ASCII<br/>
253 *
254 * @since Apache Commons Compress 1.1
255 */
256 public CpioArchiveEntry(final short format, final String name) {
257 this(format);
258 this.name = name;
259 }
260
261 /**
262 * Creates a CPIOArchiveEntry with a specified name. The format of
263 * this entry will be the new format.
264 *
265 * @param name
266 * The name of this entry.
267 * @param size
268 * The size of this entry
269 */
270 public CpioArchiveEntry(final String name, final long size) {
271 this(name);
272 this.setSize(size);
273 }
274
275 /**
276 * Creates a CPIOArchiveEntry with a specified name.
277 *
278 * @param format
279 * The cpio format for this entry.
280 * @param name
281 * The name of this entry.
282 * @param size
283 * The size of this entry
284 * <br/>
285 * Possible format values are:
286 * <p>
287 * CpioConstants.FORMAT_NEW<br/>
288 * CpioConstants.FORMAT_NEW_CRC<br/>
289 * CpioConstants.FORMAT_OLD_BINARY<br/>
290 * CpioConstants.FORMAT_OLD_ASCII<br/>
291 *
292 * @since Apache Commons Compress 1.1
293 */
294 public CpioArchiveEntry(final short format, final String name,
295 final long size) {
296 this(format, name);
297 this.setSize(size);
298 }
299
300 /**
301 * Creates a CPIOArchiveEntry with a specified name for a
302 * specified file. The format of this entry will be the new
303 * format.
304 *
305 * @param inputFile
306 * The file to gather information from.
307 * @param entryName
308 * The name of this entry.
309 */
310 public CpioArchiveEntry(File inputFile, String entryName) {
311 this(FORMAT_NEW, inputFile, entryName);
312 }
313
314 /**
315 * Creates a CPIOArchiveEntry with a specified name for a
316 * specified file.
317 *
318 * @param format
319 * The cpio format for this entry.
320 * @param inputFile
321 * The file to gather information from.
322 * @param entryName
323 * The name of this entry.
324 * <br/>
325 * Possible format values are:
326 * <p>
327 * CpioConstants.FORMAT_NEW<br/>
328 * CpioConstants.FORMAT_NEW_CRC<br/>
329 * CpioConstants.FORMAT_OLD_BINARY<br/>
330 * CpioConstants.FORMAT_OLD_ASCII<br/>
331 *
332 * @since Apache Commons Compress 1.1
333 */
334 public CpioArchiveEntry(final short format, File inputFile,
335 String entryName) {
336 this(format, entryName, inputFile.isFile() ? inputFile.length() : 0);
337 long mode=0;
338 if (inputFile.isDirectory()){
339 mode |= C_ISDIR;
340 } else if (inputFile.isFile()){
341 mode |= C_ISREG;
342 } else {
343 throw new IllegalArgumentException("Cannot determine type of file "
344 + inputFile.getName());
345 }
346 // TODO set other fields as needed
347 setMode(mode);
348 setTime(inputFile.lastModified() / 1000);
349 }
350
351 /**
352 * Check if the method is allowed for the defined format.
353 */
354 private void checkNewFormat() {
355 if ((this.fileFormat & FORMAT_NEW_MASK) == 0) {
356 throw new UnsupportedOperationException();
357 }
358 }
359
360 /**
361 * Check if the method is allowed for the defined format.
362 */
363 private void checkOldFormat() {
364 if ((this.fileFormat & FORMAT_OLD_MASK) == 0) {
365 throw new UnsupportedOperationException();
366 }
367 }
368
369 /**
370 * Get the checksum.
371 * Only supported for the new formats.
372 *
373 * @return Returns the checksum.
374 * @throws UnsupportedOperationException if the format is not a new format
375 */
376 public long getChksum() {
377 checkNewFormat();
378 return this.chksum;
379 }
380
381 /**
382 * Get the device id.
383 *
384 * @return Returns the device id.
385 * @throws UnsupportedOperationException
386 * if this method is called for a CPIOArchiveEntry with a new
387 * format.
388 */
389 public long getDevice() {
390 checkOldFormat();
391 return this.min;
392 }
393
394 /**
395 * Get the major device id.
396 *
397 * @return Returns the major device id.
398 * @throws UnsupportedOperationException
399 * if this method is called for a CPIOArchiveEntry with an old
400 * format.
401 */
402 public long getDeviceMaj() {
403 checkNewFormat();
404 return this.maj;
405 }
406
407 /**
408 * Get the minor device id
409 *
410 * @return Returns the minor device id.
411 * @throws UnsupportedOperationException if format is not a new format
412 */
413 public long getDeviceMin() {
414 checkNewFormat();
415 return this.min;
416 }
417
418 /**
419 * Get the filesize.
420 *
421 * @return Returns the filesize.
422 * @see org.apache.commons.compress.archivers.ArchiveEntry#getSize()
423 */
424 public long getSize() {
425 return this.filesize;
426 }
427
428 /**
429 * Get the format for this entry.
430 *
431 * @return Returns the format.
432 */
433 public short getFormat() {
434 return this.fileFormat;
435 }
436
437 /**
438 * Get the group id.
439 *
440 * @return Returns the group id.
441 */
442 public long getGID() {
443 return this.gid;
444 }
445
446 /**
447 * Get the header size for this CPIO format
448 *
449 * @return Returns the header size in bytes.
450 */
451 public int getHeaderSize() {
452 return this.headerSize;
453 }
454
455 /**
456 * Get the alignment boundary for this CPIO format
457 *
458 * @return Returns the aligment boundary (0, 2, 4) in bytes
459 */
460 public int getAlignmentBoundary() {
461 return this.alignmentBoundary;
462 }
463
464 /**
465 * Get the number of bytes needed to pad the header to the alignment boundary.
466 *
467 * @return the number of bytes needed to pad the header (0,1,2,3)
468 */
469 public int getHeaderPadCount(){
470 if (this.alignmentBoundary == 0) return 0;
471 int size = this.headerSize+this.name.length()+1; // Name has terminating null
472 int remain = size % this.alignmentBoundary;
473 if (remain > 0){
474 return this.alignmentBoundary - remain;
475 }
476 return 0;
477 }
478
479 /**
480 * Get the number of bytes needed to pad the data to the alignment boundary.
481 *
482 * @return the number of bytes needed to pad the data (0,1,2,3)
483 */
484 public int getDataPadCount(){
485 if (this.alignmentBoundary == 0) return 0;
486 long size = this.filesize;
487 int remain = (int) (size % this.alignmentBoundary);
488 if (remain > 0){
489 return this.alignmentBoundary - remain;
490 }
491 return 0;
492 }
493
494 /**
495 * Set the inode.
496 *
497 * @return Returns the inode.
498 */
499 public long getInode() {
500 return this.inode;
501 }
502
503 /**
504 * Get the mode of this entry (e.g. directory, regular file).
505 *
506 * @return Returns the mode.
507 */
508 public long getMode() {
509 return mode == 0 && !CPIO_TRAILER.equals(name) ? C_ISREG : mode;
510 }
511
512 /**
513 * Get the name.
514 *
515 * @return Returns the name.
516 */
517 public String getName() {
518 return this.name;
519 }
520
521 /**
522 * Get the number of links.
523 *
524 * @return Returns the number of links.
525 */
526 public long getNumberOfLinks() {
527 return nlink == 0 ?
528 (isDirectory() ? 2 : 1)
529 : nlink;
530 }
531
532 /**
533 * Get the remote device id.
534 *
535 * @return Returns the remote device id.
536 * @throws UnsupportedOperationException
537 * if this method is called for a CPIOArchiveEntry with a new
538 * format.
539 */
540 public long getRemoteDevice() {
541 checkOldFormat();
542 return this.rmin;
543 }
544
545 /**
546 * Get the remote major device id.
547 *
548 * @return Returns the remote major device id.
549 * @throws UnsupportedOperationException
550 * if this method is called for a CPIOArchiveEntry with an old
551 * format.
552 */
553 public long getRemoteDeviceMaj() {
554 checkNewFormat();
555 return this.rmaj;
556 }
557
558 /**
559 * Get the remote minor device id.
560 *
561 * @return Returns the remote minor device id.
562 * @throws UnsupportedOperationException
563 * if this method is called for a CPIOArchiveEntry with an old
564 * format.
565 */
566 public long getRemoteDeviceMin() {
567 checkNewFormat();
568 return this.rmin;
569 }
570
571 /**
572 * Get the time in seconds.
573 *
574 * @return Returns the time.
575 */
576 public long getTime() {
577 return this.mtime;
578 }
579
580 /** {@inheritDoc} */
581 public Date getLastModifiedDate() {
582 return new Date(1000 * getTime());
583 }
584
585 /**
586 * Get the user id.
587 *
588 * @return Returns the user id.
589 */
590 public long getUID() {
591 return this.uid;
592 }
593
594 /**
595 * Check if this entry represents a block device.
596 *
597 * @return TRUE if this entry is a block device.
598 */
599 public boolean isBlockDevice() {
600 return (this.mode & S_IFMT) == C_ISBLK;
601 }
602
603 /**
604 * Check if this entry represents a character device.
605 *
606 * @return TRUE if this entry is a character device.
607 */
608 public boolean isCharacterDevice() {
609 return (this.mode & S_IFMT) == C_ISCHR;
610 }
611
612 /**
613 * Check if this entry represents a directory.
614 *
615 * @return TRUE if this entry is a directory.
616 */
617 public boolean isDirectory() {
618 return (this.mode & S_IFMT) == C_ISDIR;
619 }
620
621 /**
622 * Check if this entry represents a network device.
623 *
624 * @return TRUE if this entry is a network device.
625 */
626 public boolean isNetwork() {
627 return (this.mode & S_IFMT) == C_ISNWK;
628 }
629
630 /**
631 * Check if this entry represents a pipe.
632 *
633 * @return TRUE if this entry is a pipe.
634 */
635 public boolean isPipe() {
636 return (this.mode & S_IFMT) == C_ISFIFO;
637 }
638
639 /**
640 * Check if this entry represents a regular file.
641 *
642 * @return TRUE if this entry is a regular file.
643 */
644 public boolean isRegularFile() {
645 return (this.mode & S_IFMT) == C_ISREG;
646 }
647
648 /**
649 * Check if this entry represents a socket.
650 *
651 * @return TRUE if this entry is a socket.
652 */
653 public boolean isSocket() {
654 return (this.mode & S_IFMT) == C_ISSOCK;
655 }
656
657 /**
658 * Check if this entry represents a symbolic link.
659 *
660 * @return TRUE if this entry is a symbolic link.
661 */
662 public boolean isSymbolicLink() {
663 return (this.mode & S_IFMT) == C_ISLNK;
664 }
665
666 /**
667 * Set the checksum. The checksum is calculated by adding all bytes of a
668 * file to transfer (crc += buf[pos] & 0xFF).
669 *
670 * @param chksum
671 * The checksum to set.
672 */
673 public void setChksum(final long chksum) {
674 checkNewFormat();
675 this.chksum = chksum;
676 }
677
678 /**
679 * Set the device id.
680 *
681 * @param device
682 * The device id to set.
683 * @throws UnsupportedOperationException
684 * if this method is called for a CPIOArchiveEntry with a new
685 * format.
686 */
687 public void setDevice(final long device) {
688 checkOldFormat();
689 this.min = device;
690 }
691
692 /**
693 * Set major device id.
694 *
695 * @param maj
696 * The major device id to set.
697 */
698 public void setDeviceMaj(final long maj) {
699 checkNewFormat();
700 this.maj = maj;
701 }
702
703 /**
704 * Set the minor device id
705 *
706 * @param min
707 * The minor device id to set.
708 */
709 public void setDeviceMin(final long min) {
710 checkNewFormat();
711 this.min = min;
712 }
713
714 /**
715 * Set the filesize.
716 *
717 * @param size
718 * The filesize to set.
719 */
720 public void setSize(final long size) {
721 if (size < 0 || size > 0xFFFFFFFFL) {
722 throw new IllegalArgumentException("invalid entry size <" + size
723 + ">");
724 }
725 this.filesize = size;
726 }
727
728 /**
729 * Set the group id.
730 *
731 * @param gid
732 * The group id to set.
733 */
734 public void setGID(final long gid) {
735 this.gid = gid;
736 }
737
738 /**
739 * Set the inode.
740 *
741 * @param inode
742 * The inode to set.
743 */
744 public void setInode(final long inode) {
745 this.inode = inode;
746 }
747
748 /**
749 * Set the mode of this entry (e.g. directory, regular file).
750 *
751 * @param mode
752 * The mode to set.
753 */
754 public void setMode(final long mode) {
755 final long maskedMode = mode & S_IFMT;
756 switch ((int) maskedMode) {
757 case C_ISDIR:
758 case C_ISLNK:
759 case C_ISREG:
760 case C_ISFIFO:
761 case C_ISCHR:
762 case C_ISBLK:
763 case C_ISSOCK:
764 case C_ISNWK:
765 break;
766 default:
767 throw new IllegalArgumentException(
768 "Unknown mode. "
769 + "Full: " + Long.toHexString(mode)
770 + " Masked: " + Long.toHexString(maskedMode));
771 }
772
773 this.mode = mode;
774 }
775
776 /**
777 * Set the name.
778 *
779 * @param name
780 * The name to set.
781 */
782 public void setName(final String name) {
783 this.name = name;
784 }
785
786 /**
787 * Set the number of links.
788 *
789 * @param nlink
790 * The number of links to set.
791 */
792 public void setNumberOfLinks(final long nlink) {
793 this.nlink = nlink;
794 }
795
796 /**
797 * Set the remote device id.
798 *
799 * @param device
800 * The remote device id to set.
801 * @throws UnsupportedOperationException
802 * if this method is called for a CPIOArchiveEntry with a new
803 * format.
804 */
805 public void setRemoteDevice(final long device) {
806 checkOldFormat();
807 this.rmin = device;
808 }
809
810 /**
811 * Set the remote major device id.
812 *
813 * @param rmaj
814 * The remote major device id to set.
815 * @throws UnsupportedOperationException
816 * if this method is called for a CPIOArchiveEntry with an old
817 * format.
818 */
819 public void setRemoteDeviceMaj(final long rmaj) {
820 checkNewFormat();
821 this.rmaj = rmaj;
822 }
823
824 /**
825 * Set the remote minor device id.
826 *
827 * @param rmin
828 * The remote minor device id to set.
829 * @throws UnsupportedOperationException
830 * if this method is called for a CPIOArchiveEntry with an old
831 * format.
832 */
833 public void setRemoteDeviceMin(final long rmin) {
834 checkNewFormat();
835 this.rmin = rmin;
836 }
837
838 /**
839 * Set the time in seconds.
840 *
841 * @param time
842 * The time to set.
843 */
844 public void setTime(final long time) {
845 this.mtime = time;
846 }
847
848 /**
849 * Set the user id.
850 *
851 * @param uid
852 * The user id to set.
853 */
854 public void setUID(final long uid) {
855 this.uid = uid;
856 }
857
858 /* (non-Javadoc)
859 * @see java.lang.Object#hashCode()
860 */
861 public int hashCode() {
862 final int prime = 31;
863 int result = 1;
864 result = prime * result + ((name == null) ? 0 : name.hashCode());
865 return result;
866 }
867
868 /* (non-Javadoc)
869 * @see java.lang.Object#equals(java.lang.Object)
870 */
871 public boolean equals(Object obj) {
872 if (this == obj) {
873 return true;
874 }
875 if (obj == null || getClass() != obj.getClass()) {
876 return false;
877 }
878 CpioArchiveEntry other = (CpioArchiveEntry) obj;
879 if (name == null) {
880 if (other.name != null) {
881 return false;
882 }
883 } else if (!name.equals(other.name)) {
884 return false;
885 }
886 return true;
887 }
888 }