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 }