/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * inode.h - This file is part of libsquashfs * * Copyright (C) 2019 David Oberhollenzer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef SQFS_INODE_H #define SQFS_INODE_H #include "sqfs/predef.h" /** * @file inode.h * * @brief Contains on-disk data structures used for inodes. */ /** * @enum SQFS_INODE_TYPE * * @brief Used by @ref sqfs_inode_t to identify the inode type. */ typedef enum { SQFS_INODE_DIR = 1, SQFS_INODE_FILE = 2, SQFS_INODE_SLINK = 3, SQFS_INODE_BDEV = 4, SQFS_INODE_CDEV = 5, SQFS_INODE_FIFO = 6, SQFS_INODE_SOCKET = 7, SQFS_INODE_EXT_DIR = 8, SQFS_INODE_EXT_FILE = 9, SQFS_INODE_EXT_SLINK = 10, SQFS_INODE_EXT_BDEV = 11, SQFS_INODE_EXT_CDEV = 12, SQFS_INODE_EXT_FIFO = 13, SQFS_INODE_EXT_SOCKET = 14, } SQFS_INODE_TYPE; /** * @enum SQFS_INODE_MODE * * @brief Mode bits for the @ref sqfs_inode_t mode field. * * This is basically the same that mode bits in struct stat store on Unix-like * systems. It is duplicated here for portability with non-POSIX platforms. In * case you are not familiar with Unix file permissions, a brief description * follows. * * There are 3 fields with permissions: * - of the user that owns the file * - of the group that the file belongs to * - everybody else * * Each field holds 3 bits: X, W and R meaning execute, write and read access * respectively. There are 2 special cases: On a directory, execute means * entering the directory and accessing its contents. Read and write refere * to reading or changing the list of entries. For symlinks, the permissions * are meaningless and have all bits set. * * Besides the permissions, there are 3 more bits: * - sticky * - set group id * - set user id * * Nowadays, the later two mean executing a program makes it run as the group * or user (respectively) that owns it instead of the user that actually ran * the program. On directories, the sticky bit means that its contents can only * be deleted by the actual owner, even if others have write permissions. All * other uses of those bits are obscure, historic and differ between flavours * of Unix. * * The remaining 4 bits (adding up to a total of 16) specify the type of file: * - named pipe, aka fifo * - character device * - directory * - block device * - regular file * - symlink * - socket */ typedef enum { SQFS_INODE_OTHERS_X = 00001, SQFS_INODE_OTHERS_W = 00002, SQFS_INODE_OTHERS_R = 00004, SQFS_INODE_OTHERS_MASK = 00007, SQFS_INODE_GROUP_X = 00010, SQFS_INODE_GROUP_W = 00020, SQFS_INODE_GROUP_R = 00040, SQFS_INODE_GROUP_MASK = 00070, SQFS_INODE_OWNER_X = 00100, SQFS_INODE_OWNER_W = 00200, SQFS_INODE_OWNER_R = 00400, SQFS_INODE_OWNER_MASK = 00700, SQFS_INODE_STICKY = 01000, SQFS_INODE_SET_GID = 02000, SQFS_INODE_SET_UID = 04000, SQFS_INODE_MODE_FIFO = 0010000, SQFS_INODE_MODE_CHR = 0020000, SQFS_INODE_MODE_DIR = 0040000, SQFS_INODE_MODE_BLK = 0060000, SQFS_INODE_MODE_REG = 0100000, SQFS_INODE_MODE_LNK = 0120000, SQFS_INODE_MODE_SOCK = 0140000, SQFS_INODE_MODE_MASK = 0170000, } SQFS_INODE_MODE; /** * @struct sqfs_inode_t * * @brief Common inode structure * * This structure holds the fields common for all inodes. Depending on the type * field, a specific inode structure follows. */ struct sqfs_inode_t { /** * @brief An @ref SQFS_INODE_TYPE value. */ sqfs_u16 type; /** * @brief Mode filed holding permission bits only. The type is derived * from the type field. * * This field holds a combination of @ref SQFS_INODE_MODE flags. */ sqfs_u16 mode; /** * @brief An index into the ID table where the owner UID is located. */ sqfs_u16 uid_idx; /** * @brief An index into the ID table where the owner GID is located. */ sqfs_u16 gid_idx; /** * @brief Last modifcation time. * * This field counts seconds (not counting leap seconds) since 00:00, * Jan 1 1970 UTC. This field is unsigned, so it expires in the year * 2106 (as opposed to 2038). */ sqfs_u32 mod_time; /** * @brief Unique inode number */ sqfs_u32 inode_number; }; /** * @struct sqfs_inode_dev_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_BDEV * or @ref SQFS_INODE_CDEV. */ struct sqfs_inode_dev_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Device number. */ sqfs_u32 devno; }; /** * @struct sqfs_inode_dev_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_BDEV * or @ref SQFS_INODE_EXT_CDEV. */ struct sqfs_inode_dev_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Device number. */ sqfs_u32 devno; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_ipc_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FIFO * or @ref SQFS_INODE_SOCKET. */ struct sqfs_inode_ipc_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; }; /** * @struct sqfs_inode_ipc_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FIFO * or @ref SQFS_INODE_EXT_SOCKET. */ struct sqfs_inode_ipc_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_slink_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_SLINK. * * The declaration does not contain the flexible array member of the symlink * target because @ref sqfs_inode_generic_t would otherwies be impossible to * implement without violating the C standard. */ struct sqfs_inode_slink_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Size of the symlink target in bytes */ sqfs_u32 target_size; /*sqfs_u8 target[];*/ }; /** * @struct sqfs_inode_slink_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_SLINK. * * The declaration does not contain the flexible array member of the symlink * target because it is wedged right in between the target size and the xattr * identifier. */ struct sqfs_inode_slink_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Size of the symlink target in bytes */ sqfs_u32 target_size; /*sqfs_u8 target[];*/ /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_file_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_FILE. * * The declaration does not contain the flexible array member for the data * block sizes because @ref sqfs_inode_generic_t would otherwies be impossible * to implement without violating the C standard. * * For each data block, the inode is followed by a 32 bit integer that holds * the on-disk size of the compressed block in bytes and has bit number 24 * set if the block is stored uncompressed. * * If a block size is specified as zero, it is assumed to be an entire block * filled with zero bytes. */ struct sqfs_inode_file_t { /** * @brief Absolute position of the first data block. */ sqfs_u32 blocks_start; /** * @brief Index into the fragment table or 0xFFFFFFFF if unused. */ sqfs_u32 fragment_index; /** * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF * if unused. */ sqfs_u32 fragment_offset; /** * @brief Total, uncompressed size of the file in bytes. */ sqfs_u32 file_size; /*sqfs_u32 block_sizes[];*/ }; /** * @struct sqfs_inode_file_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_FILE. * * @copydoc sqfs_inode_file_t */ struct sqfs_inode_file_ext_t { /** * @brief Absolute position of the first data block. */ sqfs_u64 blocks_start; /** * @brief Total, uncompressed size of the file in bytes. */ sqfs_u64 file_size; /** * @brief If the file is sparse, holds the number of bytes not written * to disk because of the omitted sparse blocks. */ sqfs_u64 sparse; /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Index into the fragment table or 0xFFFFFFFF if unused. */ sqfs_u32 fragment_idx; /** * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF * if unused. */ sqfs_u32 fragment_offset; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; /*sqfs_u32 block_sizes[];*/ }; /** * @struct sqfs_inode_dir_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_DIR. */ struct sqfs_inode_dir_t { /** * @brief Offset from the directory table start to the location of the * meta data block containing the first directory header. */ sqfs_u32 start_block; /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Combined size of all directory entries and headers in bytes. */ sqfs_u16 size; /** * @brief Offset into the uncompressed start block where the header can * be found. */ sqfs_u16 offset; /** * @brief Inode number of the parent directory containing * this directory inode. */ sqfs_u32 parent_inode; }; /** * @struct sqfs_inode_dir_ext_t * * @brief Follows a @ref sqfs_inode_t if type is @ref SQFS_INODE_EXT_DIR. */ struct sqfs_inode_dir_ext_t { /** * @brief Number of hard links to this node. */ sqfs_u32 nlink; /** * @brief Combined size of all directory entries and headers in bytes. */ sqfs_u32 size; /** * @brief Offset from the directory table start to the location of the * meta data block containing the first directory header. */ sqfs_u32 start_block; /** * @brief Inode number of the parent directory containing * this directory inode. */ sqfs_u32 parent_inode; /** * @brief Number of directory index entries following the inode * * This number counts the number of @ref sqfs_dir_index_t entries * following the inode. */ sqfs_u16 inodex_count; /** * @brief Offset into the uncompressed start block where the header can * be found. */ sqfs_u16 offset; /** * @brief Extended attribute index. */ sqfs_u32 xattr_idx; }; /** * @struct sqfs_inode_generic_t * * @brief A generic inode structure that combines all others and provides * additional information. * * A few helper functions exist for working with this. For instance, * @ref sqfs_meta_reader_read_inode can read an inode from disk and assemble it * into an instance of this structure. Similarly, the * @ref sqfs_meta_writer_write_inode function can break it down into encoded, * on-disk structures and write them to disk. */ struct sqfs_inode_generic_t { /** * @brief The common fields for all inodes. */ sqfs_inode_t base; /** * @brief Maximum number of available data bytes in the payload. * * This is used for dynamically growing an inode. The actual number * of used payload bytes is stored in @ref payload_bytes_used. */ sqfs_u32 payload_bytes_available; /** * @brief Number of used data bytes in the payload. * * For file inodes, stores the number of blocks used. For extended * directory inodes, stores the number of payload bytes following * for the directory index. */ sqfs_u32 payload_bytes_used; /** * @brief Type specific inode data. */ union { sqfs_inode_dev_t dev; sqfs_inode_dev_ext_t dev_ext; sqfs_inode_ipc_t ipc; sqfs_inode_ipc_ext_t ipc_ext; sqfs_inode_slink_t slink; sqfs_inode_slink_ext_t slink_ext; sqfs_inode_file_t file; sqfs_inode_file_ext_t file_ext; sqfs_inode_dir_t dir; sqfs_inode_dir_ext_t dir_ext; } data; /** * @brief Holds type specific extra data, such as symlink target. * * For regular file inodes, this is an array of block sizes. For symlink * inodes, this is actually a string holding the target. For extended * directory inodes, this is actually a blob of tightly packed directory * index entries. */ sqfs_u32 extra[]; }; #ifdef __cplusplus extern "C" { #endif /** * @brief Get the number of file blocks in a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * * @return The number of blocks. */ static SQFS_INLINE size_t sqfs_inode_get_file_block_count(const sqfs_inode_generic_t *inode) { return inode->payload_bytes_used / sizeof(sqfs_u32); } /** * @brief Get the extended attribute index of an inode * * @memberof sqfs_inode_generic_t * * For basic inodes, this returns the inode index 0xFFFFFFFF, i.e. the * sentinel value indicating that there are no xattrs. * * @param inode A pointer to an inode. * @param out Returns the extended attribute index on success. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, sqfs_u32 *out); /** * @brief Set the extended attribute index of an inode. * * @memberof sqfs_inode_generic_t * * For basic inodes, this function promes the inodes to extended inodes if the * index is not 0xFFFFFFFF. If the index is 0xFFFFFFFF, the function tries to * demote extended inode to a basic inode after setting the index. * * @param inode A pointer to an inode. * @param index The extended attribute index. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index); /** * @brief Convert a basic inode to an extended inode. * * @memberof sqfs_inode_generic_t * * For inodes that already have an extended type, this is a no-op. * * @param inode A pointer to an inode. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_make_extended(sqfs_inode_generic_t *inode); /** * @brief Convert an extended inode to a basic inode if possible. * * @memberof sqfs_inode_generic_t * * For inodes that already have a basic type, this is a no-op. If the inode * has values set that the coresponding basic type doesn't support (e.g. it * has an xattr index set or a regular file which requires 64 bit size * counter), it is left as an extended type and success state is returned. * * @param inode A pointer to an inode. * * @return Zero on success, an @ref SQFS_ERROR_CORRUPTED if the node has * an unknown type set. */ SQFS_API int sqfs_inode_make_basic(sqfs_inode_generic_t *inode); /** * @brief Update the file size of a regular file inode. * * @memberof sqfs_inode_generic_t * * If the new size is wider than 32 bit, a basic file inode is transparently * promoted to an extended file inode. For extended inodes, if the new size * is small enough and was the only requirement for the extended type, the * node is transparently demoted to a basic file inode. * * @param inode A pointer to an inode. * @param size The new size to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size); /** * @brief Update the location of the first data block of a regular file inode. * * @memberof sqfs_inode_generic_t * * If the new location is wider than 32 bit, a basic file inode is * transparently promoted to an extended file inode. For extended inodes, * if the new size is small enough and was the only requirement for the * extended type, the node is transparently demoted to a basic file inode. * * @param inode A pointer to an inode. * @param location The new location to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, sqfs_u64 location); /** * @brief Update the file fragment location of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param index The new fragment index to set. * @param offset The new fragment offset to set. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, sqfs_u32 index, sqfs_u32 offset); /** * @brief Get the file size of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param size Returns the file size. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size); /** * @brief Get the file fragment location of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param index Returns the fragment index. * @param offset Returns the fragment offset. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, sqfs_u32 *index, sqfs_u32 *offset); /** * @brief Get the location of the first data block of a regular file inode. * * @memberof sqfs_inode_generic_t * * @param inode A pointer to an inode. * @param location Returns the location. * * @return Zero on success, @ref SQFS_ERROR_NOT_FILE if the node is * not a regular file. */ SQFS_API int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, sqfs_u64 *location); /** * @brief Unpack the a directory index structure from an inode. * * @memberof sqfs_inode_generic_t * * The generic inode contains in its payload the raw directory index (with * bytes swapped to host enian), but still with single byte alignment. This * function seeks through the blob using an integer index (not offset) and * fiddles the raw data out into a propperly aligned, external structure. * * @param inode A pointer to an inode. * @param out Returns the index entry. Can be freed with a single * @ref sqfs_free call. * @param index An index value between 0 and inodex_count. * * @return Zero on success, @ref SQFS_ERROR_OUT_OF_BOUNDS if the given index * points outside the directory index. Can return other error codes * (e.g. if the inode isn't a directory or allocation failed). */ SQFS_API int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, sqfs_dir_index_t **out, size_t index); #ifdef __cplusplus } #endif #endif /* SQFS_INODE_H */