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

org.apache.commons.compress.archivers.dump.DumpArchiveEntry 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.26.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.dump;

import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.compress.archivers.ArchiveEntry;

/**
 * This class represents an entry in a Dump archive. It consists
 * of the entry's header, the entry's File and any extended attributes.
 * 

* DumpEntries that are created from the header bytes read from * an archive are instantiated with the DumpArchiveEntry( byte[] ) * constructor. These entries will be used when extracting from * or listing the contents of an archive. These entries have their * header filled in using the header bytes. They also set the File * to null, since they reference an archive entry not a file. *

* DumpEntries can also be constructed from nothing but a name. * This allows the programmer to construct the entry by hand, for * instance when only an InputStream is available for writing to * the archive, and the header information is constructed from * other information. In this case the header fields are set to * defaults and the File is set to null. * *

* The C structure for a Dump Entry's header is: *

 * #define TP_BSIZE    1024          // size of each file block
 * #define NTREC       10            // number of blocks to write at once
 * #define HIGHDENSITYTREC 32        // number of blocks to write on high-density tapes
 * #define TP_NINDIR   (TP_BSIZE/2)  // number if indirect inodes in record
 * #define TP_NINOS    (TP_NINDIR / sizeof (int32_t))
 * #define LBLSIZE     16
 * #define NAMELEN     64
 *
 * #define OFS_MAGIC     (int)60011  // old format magic value
 * #define NFS_MAGIC     (int)60012  // new format magic value
 * #define FS_UFS2_MAGIC (int)0x19540119
 * #define CHECKSUM      (int)84446  // constant used in checksum algorithm
 *
 * struct  s_spcl {
 *   int32_t c_type;             // record type (see below)
 *   int32_t c_date;             // date of this dump
 *   int32_t c_ddate;            // date of previous dump
 *   int32_t c_volume;           // dump volume number
 *   u_int32_t c_tapea;          // logical block of this record
 *   dump_ino_t c_ino;           // number of inode
 *   int32_t c_magic;            // magic number (see above)
 *   int32_t c_checksum;         // record checksum
 * #ifdef  __linux__
 *   struct  new_bsd_inode c_dinode;
 * #else
 * #ifdef sunos
 *   struct  new_bsd_inode c_dinode;
 * #else
 *   struct  dinode  c_dinode;   // ownership and mode of inode
 * #endif
 * #endif
 *   int32_t c_count;            // number of valid c_addr entries
 *   union u_data c_data;        // see above
 *   char    c_label[LBLSIZE];   // dump label
 *   int32_t c_level;            // level of this dump
 *   char    c_filesys[NAMELEN]; // name of dumpped file system
 *   char    c_dev[NAMELEN];     // name of dumpped device
 *   char    c_host[NAMELEN];    // name of dumpped host
 *   int32_t c_flags;            // additional information (see below)
 *   int32_t c_firstrec;         // first record on volume
 *   int32_t c_ntrec;            // blocksize on volume
 *   int32_t c_extattributes;    // additional inode info (see below)
 *   int32_t c_spare[30];        // reserved for future uses
 * } s_spcl;
 *
 * //
 * // flag values
 * //
 * #define DR_NEWHEADER     0x0001  // new format tape header
 * #define DR_NEWINODEFMT   0x0002  // new format inodes on tape
 * #define DR_COMPRESSED    0x0080  // dump tape is compressed
 * #define DR_METAONLY      0x0100  // only the metadata of the inode has been dumped
 * #define DR_INODEINFO     0x0002  // [SIC] TS_END header contains c_inos information
 * #define DR_EXTATTRIBUTES 0x8000
 *
 * //
 * // extattributes inode info
 * //
 * #define EXT_REGULAR         0
 * #define EXT_MACOSFNDRINFO   1
 * #define EXT_MACOSRESFORK    2
 * #define EXT_XATTR           3
 *
 * // used for EA on tape
 * #define EXT2_GOOD_OLD_INODE_SIZE    128
 * #define EXT2_XATTR_MAGIC        0xEA020000  // block EA
 * #define EXT2_XATTR_MAGIC2       0xEA020001  // in inode EA
 * 
*

* The fields in bold are the same for all blocks. (This permitted * multiple dumps to be written to a single tape.) *

* *

* The C structure for the inode (file) information is: *

 * struct bsdtimeval {           //  **** alpha-*-linux is deviant
 *   __u32   tv_sec;
 *   __u32   tv_usec;
 * };
 *
 * #define NDADDR      12
 * #define NIADDR       3
 *
 * //
 * // This is the new (4.4) BSD inode structure
 * // copied from the FreeBSD 2.0 <ufs/ufs/dinode.h> include file
 * //
 * struct new_bsd_inode {
 *   __u16       di_mode;           // file type, standard Unix permissions
 *   __s16       di_nlink;          // number of hard links to file.
 *   union {
 *      __u16       oldids[2];
 *      __u32       inumber;
 *   }           di_u;
 *   u_quad_t    di_size;           // file size
 *   struct bsdtimeval   di_atime;  // time file was last accessed
 *   struct bsdtimeval   di_mtime;  // time file was last modified
 *   struct bsdtimeval   di_ctime;  // time file was created
 *   __u32       di_db[NDADDR];
 *   __u32       di_ib[NIADDR];
 *   __u32       di_flags;          //
 *   __s32       di_blocks;         // number of disk blocks
 *   __s32       di_gen;            // generation number
 *   __u32       di_uid;            // user id (see /etc/passwd)
 *   __u32       di_gid;            // group id (see /etc/group)
 *   __s32       di_spare[2];       // unused
 * };
 * 
*

* It is important to note that the header DOES NOT have the name of the * file. It can't since hard links mean that you may have multiple file names * for a single physical file. You must read the contents of the directory * entries to learn the mapping(s) from file name to inode. *

* *

* The C structure that indicates if a specific block is a real block * that contains data or is a sparse block that is not persisted to the * disk is:

*
 * #define TP_BSIZE    1024
 * #define TP_NINDIR   (TP_BSIZE/2)
 *
 * union u_data {
 *   char    s_addrs[TP_NINDIR]; // 1 => data; 0 => hole in inode
 *   int32_t s_inos[TP_NINOS];   // table of first inode on each volume
 * } u_data;
 * 
* * @NotThreadSafe */ public class DumpArchiveEntry implements ArchiveEntry { private String name; private TYPE type = TYPE.UNKNOWN; private int mode; private Set permissions = Collections.emptySet(); private long size; private long atime; private long mtime; private int uid; private int gid; /** * Currently unused */ private final DumpArchiveSummary summary = null; // this information is available from standard index. private final TapeSegmentHeader header = new TapeSegmentHeader(); private String simpleName; private String originalName; // this information is available from QFA index private int volume; private long offset; private int ino; private int nlink; private long ctime; private int generation; private boolean isDeleted; /** * Default constructor. */ public DumpArchiveEntry() { } /** * Constructor taking only file name. * @param name pathname * @param simpleName actual file name. */ public DumpArchiveEntry(final String name, final String simpleName) { setName(name); this.simpleName = simpleName; } /** * Constructor taking name, inode and type. * * @param name the name * @param simpleName the simple name * @param ino the ino * @param type the type */ protected DumpArchiveEntry(final String name, final String simpleName, final int ino, final TYPE type) { setType(type); setName(name); this.simpleName = simpleName; this.ino = ino; this.offset = 0; } /** * Returns the path of the entry. * @return the path of the entry. */ public String getSimpleName() { return simpleName; } /** * Sets the path of the entry. * @param simpleName the simple name */ protected void setSimpleName(final String simpleName) { this.simpleName = simpleName; } /** * Returns the ino of the entry. * @return the ino */ public int getIno() { return header.getIno(); } /** * Return the number of hard links to the entry. * @return the number of hard links */ public int getNlink() { return nlink; } /** * Set the number of hard links. * @param nlink the number of hard links */ public void setNlink(final int nlink) { this.nlink = nlink; } /** * Get file creation time. * @return the creation time */ public Date getCreationTime() { return new Date(ctime); } /** * Set the file creation time. * @param ctime the creation time */ public void setCreationTime(final Date ctime) { this.ctime = ctime.getTime(); } /** * Return the generation of the file. * @return the generation */ public int getGeneration() { return generation; } /** * Set the generation of the file. * @param generation the generation */ public void setGeneration(final int generation) { this.generation = generation; } /** * Has this file been deleted? (On valid on incremental dumps.) * @return whether the file has been deleted */ public boolean isDeleted() { return isDeleted; } /** * Set whether this file has been deleted. * @param isDeleted whether the file has been deleted */ public void setDeleted(final boolean isDeleted) { this.isDeleted = isDeleted; } /** * Return the offset within the archive * @return the offset */ public long getOffset() { return offset; } /** * Set the offset within the archive. * @param offset the offset */ public void setOffset(final long offset) { this.offset = offset; } /** * Return the tape volume where this file is located. * @return the volume */ public int getVolume() { return volume; } /** * Set the tape volume. * @param volume the volume */ public void setVolume(final int volume) { this.volume = volume; } /** * Return the type of the tape segment header. * @return the segment header */ public DumpArchiveConstants.SEGMENT_TYPE getHeaderType() { return header.getType(); } /** * Return the number of records in this segment. * @return the number of records */ public int getHeaderCount() { return header.getCount(); } /** * Return the number of sparse records in this segment. * @return the number of sparse records */ public int getHeaderHoles() { return header.getHoles(); } /** * Is this a sparse record? * @param idx index of the record to check * @return whether this is a sparse record */ public boolean isSparseRecord(final int idx) { return (header.getCdata(idx) & 0x01) == 0; } @Override public int hashCode() { return ino; } @Override public boolean equals(final Object o) { if (o == this) { return true; } else if (o == null || !o.getClass().equals(getClass())) { return false; } final DumpArchiveEntry rhs = (DumpArchiveEntry) o; if (rhs.header == null) { return false; } if (ino != rhs.ino) { return false; } // summary is always null right now, but this may change some day if ((summary == null && rhs.summary != null) // NOSONAR || (summary != null && !summary.equals(rhs.summary))) { // NOSONAR return false; } return true; } @Override public String toString() { return getName(); } /** * Populate the dump archive entry and tape segment header with * the contents of the buffer. * * @param buffer buffer to read content from */ static DumpArchiveEntry parse(final byte[] buffer) { final DumpArchiveEntry entry = new DumpArchiveEntry(); final TapeSegmentHeader header = entry.header; header.type = DumpArchiveConstants.SEGMENT_TYPE.find(DumpArchiveUtil.convert32( buffer, 0)); //header.dumpDate = new Date(1000L * DumpArchiveUtil.convert32(buffer, 4)); //header.previousDumpDate = new Date(1000L * DumpArchiveUtil.convert32( // buffer, 8)); header.volume = DumpArchiveUtil.convert32(buffer, 12); //header.tapea = DumpArchiveUtil.convert32(buffer, 16); entry.ino = header.ino = DumpArchiveUtil.convert32(buffer, 20); //header.magic = DumpArchiveUtil.convert32(buffer, 24); //header.checksum = DumpArchiveUtil.convert32(buffer, 28); final int m = DumpArchiveUtil.convert16(buffer, 32); // determine the type of the file. entry.setType(TYPE.find((m >> 12) & 0x0F)); // determine the standard permissions entry.setMode(m); entry.nlink = DumpArchiveUtil.convert16(buffer, 34); // inumber, oldids? entry.setSize(DumpArchiveUtil.convert64(buffer, 40)); long t = (1000L * DumpArchiveUtil.convert32(buffer, 48)) + (DumpArchiveUtil.convert32(buffer, 52) / 1000); entry.setAccessTime(new Date(t)); t = (1000L * DumpArchiveUtil.convert32(buffer, 56)) + (DumpArchiveUtil.convert32(buffer, 60) / 1000); entry.setLastModifiedDate(new Date(t)); t = (1000L * DumpArchiveUtil.convert32(buffer, 64)) + (DumpArchiveUtil.convert32(buffer, 68) / 1000); entry.ctime = t; // db: 72-119 - direct blocks // id: 120-131 - indirect blocks //entry.flags = DumpArchiveUtil.convert32(buffer, 132); //entry.blocks = DumpArchiveUtil.convert32(buffer, 136); entry.generation = DumpArchiveUtil.convert32(buffer, 140); entry.setUserId(DumpArchiveUtil.convert32(buffer, 144)); entry.setGroupId(DumpArchiveUtil.convert32(buffer, 148)); // two 32-bit spare values. header.count = DumpArchiveUtil.convert32(buffer, 160); header.holes = 0; for (int i = 0; (i < 512) && (i < header.count); i++) { if (buffer[164 + i] == 0) { header.holes++; } } System.arraycopy(buffer, 164, header.cdata, 0, 512); entry.volume = header.getVolume(); //entry.isSummaryOnly = false; return entry; } /** * Update entry with information from next tape segment header. */ void update(final byte[] buffer) { header.volume = DumpArchiveUtil.convert32(buffer, 16); header.count = DumpArchiveUtil.convert32(buffer, 160); header.holes = 0; for (int i = 0; (i < 512) && (i < header.count); i++) { if (buffer[164 + i] == 0) { header.holes++; } } System.arraycopy(buffer, 164, header.cdata, 0, 512); } /** * Archive entry as stored on tape. There is one TSH for (at most) * every 512k in the file. */ static class TapeSegmentHeader { private DumpArchiveConstants.SEGMENT_TYPE type; private int volume; private int ino; private int count; private int holes; private final byte[] cdata = new byte[512]; // map of any 'holes' public DumpArchiveConstants.SEGMENT_TYPE getType() { return type; } public int getVolume() { return volume; } public int getIno() { return ino; } void setIno(final int ino) { this.ino = ino; } public int getCount() { return count; } public int getHoles() { return holes; } public int getCdata(final int idx) { return cdata[idx]; } } /** * Returns the name of the entry. * *

This method returns the raw name as it is stored inside of the archive.

* * @return the name of the entry. */ @Override public String getName() { return name; } /** * Returns the unmodified name of the entry. * @return the name of the entry. */ String getOriginalName() { return originalName; } /** * Sets the name of the entry. * @param name the name */ public final void setName(String name) { this.originalName = name; if (name != null) { if (isDirectory() && !name.endsWith("/")) { name += "/"; } if (name.startsWith("./")) { name = name.substring(2); } } this.name = name; } /** * The last modified date. * @return the last modified date */ @Override public Date getLastModifiedDate() { return new Date(mtime); } /** * Is this a directory? * @return whether this is a directory */ @Override public boolean isDirectory() { return type == TYPE.DIRECTORY; } /** * Is this a regular file? * @return whether this is a regular file */ public boolean isFile() { return type == TYPE.FILE; } /** * Is this a network device? * @return whether this is a socket */ public boolean isSocket() { return type == TYPE.SOCKET; } /** * Is this a character device? * @return whether this is a character device */ public boolean isChrDev() { return type == TYPE.CHRDEV; } /** * Is this a block device? * @return whether this is a block device */ public boolean isBlkDev() { return type == TYPE.BLKDEV; } /** * Is this a fifo/pipe? * @return whether this is a fifo */ public boolean isFifo() { return type == TYPE.FIFO; } /** * Get the type of the entry. * @return the type */ public TYPE getType() { return type; } /** * Set the type of the entry. * @param type the type */ public void setType(final TYPE type) { this.type = type; } /** * Return the access permissions on the entry. * @return the access permissions */ public int getMode() { return mode; } /** * Set the access permissions on the entry. * @param mode the access permissions */ public void setMode(final int mode) { this.mode = mode & 07777; this.permissions = PERMISSION.find(mode); } /** * Returns the permissions on the entry. * @return the permissions */ public Set getPermissions() { return permissions; } /** * Returns the size of the entry. * @return the size */ @Override public long getSize() { return isDirectory() ? SIZE_UNKNOWN : size; } /** * Returns the size of the entry as read from the archive. */ long getEntrySize() { return size; } /** * Set the size of the entry. * @param size the size */ public void setSize(final long size) { this.size = size; } /** * Set the time the file was last modified. * @param mtime the last modified time */ public void setLastModifiedDate(final Date mtime) { this.mtime = mtime.getTime(); } /** * Returns the time the file was last accessed. * @return the access time */ public Date getAccessTime() { return new Date(atime); } /** * Set the time the file was last accessed. * @param atime the access time */ public void setAccessTime(final Date atime) { this.atime = atime.getTime(); } /** * Return the user id. * @return the user id */ public int getUserId() { return uid; } /** * Set the user id. * @param uid the user id */ public void setUserId(final int uid) { this.uid = uid; } /** * Return the group id * @return the group id */ public int getGroupId() { return gid; } /** * Set the group id. * @param gid the group id */ public void setGroupId(final int gid) { this.gid = gid; } public enum TYPE { WHITEOUT(14), SOCKET(12), LINK(10), FILE(8), BLKDEV(6), DIRECTORY(4), CHRDEV(2), FIFO(1), UNKNOWN(15); private int code; TYPE(final int code) { this.code = code; } public static TYPE find(final int code) { TYPE type = UNKNOWN; for (final TYPE t : TYPE.values()) { if (code == t.code) { type = t; } } return type; } } public enum PERMISSION { SETUID(04000), SETGUI(02000), STICKY(01000), USER_READ(00400), USER_WRITE(00200), USER_EXEC(00100), GROUP_READ(00040), GROUP_WRITE(00020), GROUP_EXEC(00010), WORLD_READ(00004), WORLD_WRITE(00002), WORLD_EXEC(00001); private int code; PERMISSION(final int code) { this.code = code; } public static Set find(final int code) { final Set set = new HashSet<>(); for (final PERMISSION p : PERMISSION.values()) { if ((code & p.code) == p.code) { set.add(p); } } if (set.isEmpty()) { return Collections.emptySet(); } return EnumSet.copyOf(set); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy