All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.compress.archivers.cpio.CpioArchiveEntry Maven / Gradle / Ivy

Go to download

Apache Commons Compress software defines an API for working with compression and archive formats. These include: bzip2, gzip, pack200, lzma, xz, Snappy, traditional Unix Compress, DEFLATE, DEFLATE64, LZ4, Brotli, Zstandard and ar, cpio, jar, tar, zip, dump, 7z, arj.

There is a newer version: 1.27.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.commons.compress.archivers.cpio;

import java.io.File;
import java.util.Date;

import org.apache.commons.compress.archivers.ArchiveEntry;

/**
 * A cpio archive consists of a sequence of files. There are several types of
 * headers defided in two categories of new and old format. The headers are
 * recognized by magic numbers:
 * 
 * 
    *
  • "070701" ASCII for new portable format
  • *
  • "070702" ASCII for new portable format with CRC
  • *
  • "070707" ASCII for old ascii (also known as Portable ASCII, odc or old * character format
  • *
  • 070707 binary for old binary
  • *
* *

The old binary format is limited to 16 bits for user id, group * id, device, and inode numbers. It is limited to 4 gigabyte file * sizes. * * The old ASCII format is limited to 18 bits for the user id, group * id, device, and inode numbers. It is limited to 8 gigabyte file * sizes. * * The new ASCII format is limited to 4 gigabyte file sizes. * * CPIO 2.5 knows also about tar, but it is not recognized here.

* * *

OLD FORMAT

* *

Each file has a 76 (ascii) / 26 (binary) byte header, a variable * length, NUL terminated filename, and variable length file data. A * header for a filename "TRAILER!!!" indicates the end of the * archive.

* *

All the fields in the header are ISO 646 (approximately ASCII) * strings of octal numbers, left padded, not NUL terminated.

* *
 * FIELDNAME        NOTES 
 * c_magic          The integer value octal 070707.  This value can be used to deter-
 *                  mine whether this archive is written with little-endian or big-
 *                  endian integers.
 * c_dev            Device that contains a directory entry for this file 
 * c_ino            I-node number that identifies the input file to the file system 
 * c_mode           The mode specifies both the regular permissions and the file type.
 * c_uid            Numeric User ID of the owner of the input file 
 * c_gid            Numeric Group ID of the owner of the input file 
 * c_nlink          Number of links that are connected to the input file 
 * c_rdev           For block special and character special entries, this field 
 *                  contains the associated device number.  For all other entry types,
 *                  it should be set to zero by writers and ignored by readers.
 * c_mtime[2]       Modification time of the file, indicated as the number of seconds
 *                  since the start of the epoch, 00:00:00 UTC January 1, 1970.  The
 *                  four-byte integer is stored with the most-significant 16 bits
 *                  first followed by the least-significant 16 bits.  Each of the two
 *                  16 bit values are stored in machine-native byte order.
 * c_namesize       Length of the path name, including the terminating null byte 
 * c_filesize[2]    Length of the file in bytes. This is the length of the data 
 *                  section that follows the header structure. Must be 0 for 
 *                  FIFOs and directories
 *
 * All fields are unsigned short fields with 16-bit integer values
 * apart from c_mtime and c_filesize which are 32-bit integer values
 * 
* *

If necessary, the filename and file data are padded with a NUL byte to an even length

* *

Special files, directories, and the trailer are recorded with * the h_filesize field equal to 0.

* *

In the ASCII version of this format, the 16-bit entries are represented as 6-byte octal numbers, * and the 32-bit entries are represented as 11-byte octal numbers. No padding is added.

* *

NEW FORMAT

* *

Each file has a 110 byte header, a variable length, NUL * terminated filename, and variable length file data. A header for a * filename "TRAILER!!!" indicates the end of the archive. All the * fields in the header are ISO 646 (approximately ASCII) strings of * hexadecimal numbers, left padded, not NUL terminated.

* *
 * FIELDNAME        NOTES 
 * c_magic[6]       The string 070701 for new ASCII, the string 070702 for new ASCII with CRC
 * c_ino[8]
 * c_mode[8]
 * c_uid[8]
 * c_gid[8]
 * c_nlink[8]
 * c_mtim[8]
 * c_filesize[8]    must be 0 for FIFOs and directories 
 * c_maj[8]
 * c_min[8] 
 * c_rmaj[8]        only valid for chr and blk special files 
 * c_rmin[8]        only valid for chr and blk special files 
 * c_namesize[8]    count includes terminating NUL in pathname 
 * c_check[8]       0 for "new" portable format; for CRC format
 *                  the sum of all the bytes in the file
 * 
* *

New ASCII Format The "new" ASCII format uses 8-byte hexadecimal * fields for all numbers and separates device numbers into separate * fields for major and minor numbers.

* *

The pathname is followed by NUL bytes so that the total size of * the fixed header plus pathname is a multiple of four. Likewise, the * file data is padded to a multiple of four bytes.

* *

This class uses mutable fields and is not considered to be * threadsafe.

* *

Based on code from the jRPM project (http://jrpm.sourceforge.net).

* *

The MAGIC numbers and other constants are defined in {@link CpioConstants}

* *

* N.B. does not handle the cpio "tar" format *

* @NotThreadSafe * @see "http://people.freebsd.org/~kientzle/libarchive/man/cpio.5.txt" */ public class CpioArchiveEntry implements CpioConstants, ArchiveEntry { // Header description fields - should be same throughout an archive /** * See constructor documenation for possible values. */ private final short fileFormat; /** The number of bytes in each header record; depends on the file format */ private final int headerSize; /** The boundary to which the header and data elements are aligned: 0, 2 or 4 bytes */ private final int alignmentBoundary; // Header fields private long chksum = 0; /** Number of bytes in the file */ private long filesize = 0; private long gid = 0; private long inode = 0; private long maj = 0; private long min = 0; private long mode = 0; private long mtime = 0; private String name; private long nlink = 0; private long rmaj = 0; private long rmin = 0; private long uid = 0; /** * Creates a CPIOArchiveEntry with a specified format. * * @param format * The cpio format for this entry. *
* Possible format values are: *

* CpioConstants.FORMAT_NEW
* CpioConstants.FORMAT_NEW_CRC
* CpioConstants.FORMAT_OLD_BINARY
* CpioConstants.FORMAT_OLD_ASCII
* */ public CpioArchiveEntry(final short format) { switch (format) { case FORMAT_NEW: this.headerSize = 110; this.alignmentBoundary = 4; break; case FORMAT_NEW_CRC: this.headerSize = 110; this.alignmentBoundary = 4; break; case FORMAT_OLD_ASCII: this.headerSize = 76; this.alignmentBoundary = 0; break; case FORMAT_OLD_BINARY: this.headerSize = 26; this.alignmentBoundary = 2; break; default: throw new IllegalArgumentException("Unknown header type"); } this.fileFormat = format; } /** * Creates a CPIOArchiveEntry with a specified name. The format of * this entry will be the new format. * * @param name * The name of this entry. */ public CpioArchiveEntry(final String name) { this(FORMAT_NEW, name); } /** * Creates a CPIOArchiveEntry with a specified name. * * @param format * The cpio format for this entry. * @param name * The name of this entry. *
* Possible format values are: *

* CpioConstants.FORMAT_NEW
* CpioConstants.FORMAT_NEW_CRC
* CpioConstants.FORMAT_OLD_BINARY
* CpioConstants.FORMAT_OLD_ASCII
* * @since 1.1 */ public CpioArchiveEntry(final short format, final String name) { this(format); this.name = name; } /** * Creates a CPIOArchiveEntry with a specified name. The format of * this entry will be the new format. * * @param name * The name of this entry. * @param size * The size of this entry */ public CpioArchiveEntry(final String name, final long size) { this(name); this.setSize(size); } /** * Creates a CPIOArchiveEntry with a specified name. * * @param format * The cpio format for this entry. * @param name * The name of this entry. * @param size * The size of this entry *
* Possible format values are: *

* CpioConstants.FORMAT_NEW
* CpioConstants.FORMAT_NEW_CRC
* CpioConstants.FORMAT_OLD_BINARY
* CpioConstants.FORMAT_OLD_ASCII
* * @since 1.1 */ public CpioArchiveEntry(final short format, final String name, final long size) { this(format, name); this.setSize(size); } /** * Creates a CPIOArchiveEntry with a specified name for a * specified file. The format of this entry will be the new * format. * * @param inputFile * The file to gather information from. * @param entryName * The name of this entry. */ public CpioArchiveEntry(File inputFile, String entryName) { this(FORMAT_NEW, inputFile, entryName); } /** * Creates a CPIOArchiveEntry with a specified name for a * specified file. * * @param format * The cpio format for this entry. * @param inputFile * The file to gather information from. * @param entryName * The name of this entry. *
* Possible format values are: *

* CpioConstants.FORMAT_NEW
* CpioConstants.FORMAT_NEW_CRC
* CpioConstants.FORMAT_OLD_BINARY
* CpioConstants.FORMAT_OLD_ASCII
* * @since 1.1 */ public CpioArchiveEntry(final short format, File inputFile, String entryName) { this(format, entryName, inputFile.isFile() ? inputFile.length() : 0); long mode=0; if (inputFile.isDirectory()){ mode |= C_ISDIR; } else if (inputFile.isFile()){ mode |= C_ISREG; } else { throw new IllegalArgumentException("Cannot determine type of file " + inputFile.getName()); } // TODO set other fields as needed setMode(mode); setTime(inputFile.lastModified() / 1000); } /** * Check if the method is allowed for the defined format. */ private void checkNewFormat() { if ((this.fileFormat & FORMAT_NEW_MASK) == 0) { throw new UnsupportedOperationException(); } } /** * Check if the method is allowed for the defined format. */ private void checkOldFormat() { if ((this.fileFormat & FORMAT_OLD_MASK) == 0) { throw new UnsupportedOperationException(); } } /** * Get the checksum. * Only supported for the new formats. * * @return Returns the checksum. * @throws UnsupportedOperationException if the format is not a new format */ public long getChksum() { checkNewFormat(); return this.chksum; } /** * Get the device id. * * @return Returns the device id. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with a new * format. */ public long getDevice() { checkOldFormat(); return this.min; } /** * Get the major device id. * * @return Returns the major device id. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with an old * format. */ public long getDeviceMaj() { checkNewFormat(); return this.maj; } /** * Get the minor device id * * @return Returns the minor device id. * @throws UnsupportedOperationException if format is not a new format */ public long getDeviceMin() { checkNewFormat(); return this.min; } /** * Get the filesize. * * @return Returns the filesize. * @see org.apache.commons.compress.archivers.ArchiveEntry#getSize() */ public long getSize() { return this.filesize; } /** * Get the format for this entry. * * @return Returns the format. */ public short getFormat() { return this.fileFormat; } /** * Get the group id. * * @return Returns the group id. */ public long getGID() { return this.gid; } /** * Get the header size for this CPIO format * * @return Returns the header size in bytes. */ public int getHeaderSize() { return this.headerSize; } /** * Get the alignment boundary for this CPIO format * * @return Returns the aligment boundary (0, 2, 4) in bytes */ public int getAlignmentBoundary() { return this.alignmentBoundary; } /** * Get the number of bytes needed to pad the header to the alignment boundary. * * @return the number of bytes needed to pad the header (0,1,2,3) */ public int getHeaderPadCount(){ if (this.alignmentBoundary == 0) { return 0; } int size = this.headerSize+this.name.length()+1; // Name has terminating null int remain = size % this.alignmentBoundary; if (remain > 0){ return this.alignmentBoundary - remain; } return 0; } /** * Get the number of bytes needed to pad the data to the alignment boundary. * * @return the number of bytes needed to pad the data (0,1,2,3) */ public int getDataPadCount(){ if (this.alignmentBoundary == 0) { return 0; } long size = this.filesize; int remain = (int) (size % this.alignmentBoundary); if (remain > 0){ return this.alignmentBoundary - remain; } return 0; } /** * Set the inode. * * @return Returns the inode. */ public long getInode() { return this.inode; } /** * Get the mode of this entry (e.g. directory, regular file). * * @return Returns the mode. */ public long getMode() { return mode == 0 && !CPIO_TRAILER.equals(name) ? C_ISREG : mode; } /** * Get the name. * * @return Returns the name. */ public String getName() { return this.name; } /** * Get the number of links. * * @return Returns the number of links. */ public long getNumberOfLinks() { return nlink == 0 ? (isDirectory() ? 2 : 1) : nlink; } /** * Get the remote device id. * * @return Returns the remote device id. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with a new * format. */ public long getRemoteDevice() { checkOldFormat(); return this.rmin; } /** * Get the remote major device id. * * @return Returns the remote major device id. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with an old * format. */ public long getRemoteDeviceMaj() { checkNewFormat(); return this.rmaj; } /** * Get the remote minor device id. * * @return Returns the remote minor device id. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with an old * format. */ public long getRemoteDeviceMin() { checkNewFormat(); return this.rmin; } /** * Get the time in seconds. * * @return Returns the time. */ public long getTime() { return this.mtime; } /** {@inheritDoc} */ public Date getLastModifiedDate() { return new Date(1000 * getTime()); } /** * Get the user id. * * @return Returns the user id. */ public long getUID() { return this.uid; } /** * Check if this entry represents a block device. * * @return TRUE if this entry is a block device. */ public boolean isBlockDevice() { return (this.mode & S_IFMT) == C_ISBLK; } /** * Check if this entry represents a character device. * * @return TRUE if this entry is a character device. */ public boolean isCharacterDevice() { return (this.mode & S_IFMT) == C_ISCHR; } /** * Check if this entry represents a directory. * * @return TRUE if this entry is a directory. */ public boolean isDirectory() { return (this.mode & S_IFMT) == C_ISDIR; } /** * Check if this entry represents a network device. * * @return TRUE if this entry is a network device. */ public boolean isNetwork() { return (this.mode & S_IFMT) == C_ISNWK; } /** * Check if this entry represents a pipe. * * @return TRUE if this entry is a pipe. */ public boolean isPipe() { return (this.mode & S_IFMT) == C_ISFIFO; } /** * Check if this entry represents a regular file. * * @return TRUE if this entry is a regular file. */ public boolean isRegularFile() { return (this.mode & S_IFMT) == C_ISREG; } /** * Check if this entry represents a socket. * * @return TRUE if this entry is a socket. */ public boolean isSocket() { return (this.mode & S_IFMT) == C_ISSOCK; } /** * Check if this entry represents a symbolic link. * * @return TRUE if this entry is a symbolic link. */ public boolean isSymbolicLink() { return (this.mode & S_IFMT) == C_ISLNK; } /** * Set the checksum. The checksum is calculated by adding all bytes of a * file to transfer (crc += buf[pos] & 0xFF). * * @param chksum * The checksum to set. */ public void setChksum(final long chksum) { checkNewFormat(); this.chksum = chksum; } /** * Set the device id. * * @param device * The device id to set. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with a new * format. */ public void setDevice(final long device) { checkOldFormat(); this.min = device; } /** * Set major device id. * * @param maj * The major device id to set. */ public void setDeviceMaj(final long maj) { checkNewFormat(); this.maj = maj; } /** * Set the minor device id * * @param min * The minor device id to set. */ public void setDeviceMin(final long min) { checkNewFormat(); this.min = min; } /** * Set the filesize. * * @param size * The filesize to set. */ public void setSize(final long size) { if (size < 0 || size > 0xFFFFFFFFL) { throw new IllegalArgumentException("invalid entry size <" + size + ">"); } this.filesize = size; } /** * Set the group id. * * @param gid * The group id to set. */ public void setGID(final long gid) { this.gid = gid; } /** * Set the inode. * * @param inode * The inode to set. */ public void setInode(final long inode) { this.inode = inode; } /** * Set the mode of this entry (e.g. directory, regular file). * * @param mode * The mode to set. */ public void setMode(final long mode) { final long maskedMode = mode & S_IFMT; switch ((int) maskedMode) { case C_ISDIR: case C_ISLNK: case C_ISREG: case C_ISFIFO: case C_ISCHR: case C_ISBLK: case C_ISSOCK: case C_ISNWK: break; default: throw new IllegalArgumentException( "Unknown mode. " + "Full: " + Long.toHexString(mode) + " Masked: " + Long.toHexString(maskedMode)); } this.mode = mode; } /** * Set the name. * * @param name * The name to set. */ public void setName(final String name) { this.name = name; } /** * Set the number of links. * * @param nlink * The number of links to set. */ public void setNumberOfLinks(final long nlink) { this.nlink = nlink; } /** * Set the remote device id. * * @param device * The remote device id to set. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with a new * format. */ public void setRemoteDevice(final long device) { checkOldFormat(); this.rmin = device; } /** * Set the remote major device id. * * @param rmaj * The remote major device id to set. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with an old * format. */ public void setRemoteDeviceMaj(final long rmaj) { checkNewFormat(); this.rmaj = rmaj; } /** * Set the remote minor device id. * * @param rmin * The remote minor device id to set. * @throws UnsupportedOperationException * if this method is called for a CPIOArchiveEntry with an old * format. */ public void setRemoteDeviceMin(final long rmin) { checkNewFormat(); this.rmin = rmin; } /** * Set the time in seconds. * * @param time * The time to set. */ public void setTime(final long time) { this.mtime = time; } /** * Set the user id. * * @param uid * The user id to set. */ public void setUID(final long uid) { this.uid = uid; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } CpioArchiveEntry other = (CpioArchiveEntry) obj; if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy