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 }