diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | include/sqfs/block_processor.h | 197 | ||||
-rw-r--r-- | include/sqfs/compress.h | 265 | ||||
-rw-r--r-- | include/sqfs/data.h | 23 | ||||
-rw-r--r-- | include/sqfs/dir.h | 228 | ||||
-rw-r--r-- | include/sqfs/error.h | 70 | ||||
-rw-r--r-- | include/sqfs/id_table.h | 88 | ||||
-rw-r--r-- | include/sqfs/inode.h | 312 | ||||
-rw-r--r-- | include/sqfs/io.h | 92 | ||||
-rw-r--r-- | include/sqfs/meta_reader.h | 142 | ||||
-rw-r--r-- | include/sqfs/meta_writer.h | 143 | ||||
-rw-r--r-- | include/sqfs/predef.h | 7 | ||||
-rw-r--r-- | include/sqfs/super.h | 231 | ||||
-rw-r--r-- | include/sqfs/table.h | 56 | ||||
-rw-r--r-- | include/sqfs/xattr.h | 286 |
15 files changed, 2033 insertions, 111 deletions
@@ -72,6 +72,10 @@ If you work on the git tree, you need to bootstrap the build system first: ./autogen.sh +If Doxygen is available, a reference manual can be built as follows: + + make doxygen-doc + ## Structure of the Source Code The main functionality of the package is split up into a number libraries. diff --git a/include/sqfs/block_processor.h b/include/sqfs/block_processor.h index 70cff09..ed38be6 100644 --- a/include/sqfs/block_processor.h +++ b/include/sqfs/block_processor.h @@ -22,54 +22,127 @@ #include "sqfs/predef.h" -enum { - /* only calculate checksum, do NOT compress the data */ +/** + * @file block_processor.h + * + * @brief Contains declarations for the data block processor. + */ + +/** + * @struct sqfs_block_processor_t + * + * @brief Encapsulates a thread pool based block processor. + * + * The actual implementation may even be non-threaded, depending on the + * operating system and compile configuration. + * + * Either way, the instantiated object processes data blocks that can be + * enqueued through @ref sqfs_block_processor_enqueue. The completed blocks + * (compressed and checksumed) are dequeued in the same order and a callback + * is called for each one. + */ + +/** + * @enum E_SQFS_BLK_FLAGS + * + * @brief Generic flags that tell the processor what to do with a block and + * flags that the processor sets when it is done with a block. + */ +typedef enum { + /** + * @brief Only calculate checksum, do NOT compress the data. + */ SQFS_BLK_DONT_COMPRESS = 0x0001, - /* set by compressor worker if the block was actually compressed */ + /** + * @brief Set by compressor worker if the block was actually compressed. + */ SQFS_BLK_IS_COMPRESSED = 0x0002, - /* do not calculate block checksum */ + /** + * @brief Do not calculate block checksum. + */ SQFS_BLK_DONT_CHECKSUM = 0x0004, - /* set by compressor worker if compression failed */ + /** + * @brief Set by compressor worker if compression failed. + */ SQFS_BLK_COMPRESS_ERROR = 0x0008, - /* first user setable block flag */ + /** + * @brief First user setable block flag. + */ SQFS_BLK_USER = 0x0080 -}; +} E_SQFS_BLK_FLAGS; +/** + * @struct sqfs_block_t + * + * @brief Encapsulates a chunk of data to be processed by the block processor. + */ struct sqfs_block_t { - /* used internally, ignored and overwritten when enqueueing blocks */ + /** + * @brief Used internally, existing value is ignored and overwritten + * when enqueueing a block. + */ sqfs_block_t *next; + + /** + * @brief Used internally, existing value is ignored and overwritten + * when enqueueing a block. + */ uint32_t sequence_number; - /* Size of the data area */ + /** + * @brief Size of the data area. + */ uint32_t size; - /* checksum of the input data */ + /** + * @brief Checksum of the input data. + */ uint32_t checksum; - /* user settable file block index */ + /** + * @brief User settable file block index. + * + * Can be used for purposes like indexing the block size table. + */ uint32_t index; - /* user pointer associated with the block */ + /** + * @brief Arbitary user pointer associated with the block. + */ void *user; - /* user settable flag field */ + /** + * @brief User settable flag field. + * + * A combination of @ref E_SQFS_BLK_FLAGS and custom, user + * settable flags. + */ uint32_t flags; - /* raw data to be processed */ + /** + * @brief Raw data to be processed. + */ uint8_t data[]; }; -/* - Gets called for each processed block. May be called from a different thread - than the one that calls enqueue, but only from one thread at a time. - Guaranteed to be called on blocks in the order that they are submitted - to enqueue. - - A non-zero return value is interpreted as fatal error. +/** + * @brief Signature of a callback function that can is called for each block. + * + * Gets called for each processed block. May be called from a different thread + * than the one that calls enqueue or from the same thread, but only from one + * thread at a time. + * + * Guaranteed to be called on blocks in the order that they are submitted + * to enqueue. + * + * @param user The user pointer passed to @ref sqfs_block_processor_create. + * @param blk The finished block. + * + * @return A non-zero return value is interpreted as fatal error. */ typedef int (*sqfs_block_cb)(void *user, sqfs_block_t *blk); @@ -77,6 +150,24 @@ typedef int (*sqfs_block_cb)(void *user, sqfs_block_t *blk); extern "C" { #endif +/** + * @brief Create a block processor. + * + * @memberof sqfs_block_processor_t + * + * @param max_block_size The maximum size of a data block. Required for the + * internal scratch buffer used for compressing data. + * @param cmp A pointer to a compressor. If multiple worker threads are used, + * the deep copy function of the compressor is used to create + * several instances that don't interfere with each other. + * @param num_workers The number of worker threads to create. + * @param user An arbitrary user pointer to pass to the block callback. + * @param callback A function to call for each finished data block. + * + * @return A pointer to a block processor object on success, NULL on allocation + * failure or on failure to create the worker threads and + * synchronisation primitives. + */ SQFS_API sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, sqfs_compressor_t *cmp, @@ -84,31 +175,61 @@ sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, void *user, sqfs_block_cb callback); +/** + * @brief Destroy a block processor and free all memory used by it. + * + * @memberof sqfs_block_processor_t + * + * @param proc A pointer to a block processor object. + */ SQFS_API void sqfs_block_processor_destroy(sqfs_block_processor_t *proc); -/* - Add a block to be processed. Returns non-zero on error and prints a message - to stderr. - - The function takes over ownership of the submitted block. It is freed with - a after processing and calling the block callback. - - Even on failure, the workers may still be running and - sqfs_block_processor_finish must be called before cleaning up. -*/ +/** + * @brief Add a block to be processed. + * + * @memberof sqfs_block_processor_t + * + * The function takes over ownership of the submitted block. It is freed after + * processing and calling the block callback. + * + * @note Even on failure, the workers may still be running and you should still + * call @ref sqfs_block_processor_finish before cleaning up. + * + * @param proc A pointer to a block processor object. + * @param block A poitner to a block to enqueue. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. Depending on + * the implementation used, the failure return value can actually come + * directly from the block callback. + */ SQFS_API int sqfs_block_processor_enqueue(sqfs_block_processor_t *proc, sqfs_block_t *block); -/* - Wait for the compressor workers to finish. Returns zero on success, non-zero - if an internal error occoured or one of the block callbacks returned a - non-zero value. +/** + * @brief Wait for the workers to finish all in-flight data blocks. + * + * @memberof sqfs_block_processor_t + * + * @param proc A pointer to a block processor object. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. The failure + * return value can either be an error encountered during enqueueing, + * processing or a failure return status from the block callback. */ SQFS_API int sqfs_block_processor_finish(sqfs_block_processor_t *proc); -/* - Convenience function to process a data block. Returns 0 on success, - prints to stderr on failure. +/** + * @brief Convenience function to process a data block. + * + * This function actually contains the implementation of what each worker in + * the block processor actually does to the data blocks. + * + * @param block A pointer to a data block. + * @param cmp A pointer to a compressor to use. + * @param scratch A pointer to a scratch buffer to user for compressing. + * @param scratch_size The available size in the scratch buffer. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. */ SQFS_API int sqfs_block_process(sqfs_block_t *block, sqfs_compressor_t *cmp, uint8_t *scratch, size_t scratch_size); diff --git a/include/sqfs/compress.h b/include/sqfs/compress.h index eaec2b5..3d99617 100644 --- a/include/sqfs/compress.h +++ b/include/sqfs/compress.h @@ -23,88 +23,259 @@ #include "sqfs/predef.h" #include "sqfs/super.h" -/* Encapsultes a compressor with a simple interface to compress or - uncompress/extract blocks of data. */ +/** + * @file compress.h + * + * @brief Contains declarations to everything related to data compression. + */ + +/** + * @interface sqfs_compressor_t + * + * @brief Encapsultes a compressor with a simple interface to compress or + * extract chunks of data. + */ struct sqfs_compressor_t { + /** + * @brief Destroy a compressor and free all memory used by it. + * + * @param cmp A pointer to a compressor object. + */ void (*destroy)(sqfs_compressor_t *cmp); - /* Write compressor options to the output file if necessary. - Returns the number of bytes written or -1 on failure. - Internally prints error messages to stderr. */ + /** + * @brief Write compressor options to disk if non-default settings + * have been used. + * + * The options are stored in an uncompressed meta data block directly + * after the super block. + * + * @param cmp A pointer to a compressor object. + * @param file A file to write to. + * + * @return The number of bytes written on success, 0 means default + * settings are used. A negative value is an @ref E_SQFS_ERROR + * identifier. + */ int (*write_options)(sqfs_compressor_t *cmp, sqfs_file_t *file); - /* Read compressor options to the input file. - Returns zero on success, -1 on failure. - Internally prints error messages to stderr. */ + /** + * @brief Read compressor options from disk. + * + * @param cmp A pointer to a compressor object. + * @param file A file to read from. + * + * @return Zero on success or an @ref E_SQFS_ERROR value. + */ int (*read_options)(sqfs_compressor_t *cmp, sqfs_file_t *file); - /* - Compress or uncompress a chunk of data. - - Returns the number of bytes written to the output buffer, -1 on - failure or 0 if the output buffer was too small. - The compressor also returns 0 if the compressed result ends - up larger than the original input. - - Internally prints compressor specific error messages to stderr. - */ + /** + * @brief Compress or uncompress a chunk of data. + * + * @param cmp A pointer to a compressor object. + * @param in A pointer to the input buffer to read from. + * @param size The number of bytes to read from the input and compress. + * @param out The destination buffer to write the result to. + * @param outsize The available space in the destination buffer. + * + * @return The number of bytes written to the buffer, a negative + * value is an @ref E_SQFS_ERROR value. The value 0 means + * the output buffer was too small when extracting or that + * the result is larger than the input when compressing. + */ ssize_t (*do_block)(sqfs_compressor_t *cmp, const uint8_t *in, size_t size, uint8_t *out, size_t outsize); - /* create another compressor just like this one, i.e. - with the exact same settings */ + /** + * @brief Create an exact copt of agiven compressor + * + * @param cmp A pointer to a compressor object. + * + * @return A deep copy of the given compressor. + */ sqfs_compressor_t *(*create_copy)(sqfs_compressor_t *cmp); }; +/** + * @struct sqfs_compressor_config_t + * + * @brief Configuration parameters for instantiating a compressor backend. + */ struct sqfs_compressor_config_t { + /** + * @brief An @ref E_SQFS_COMPRESSOR identifier + */ uint16_t id; + + /** + * @brief A combination of @ref SQFS_COMP_FLAG flags. + */ uint16_t flags; + + /** + * @brief The intended data block size. + */ uint32_t block_size; + /** + * @brief Backend specific options for fine tuing. + */ union { + /** + * @brief Options for the zlib compressor. + */ struct { + /** + * @brief Compression level. Value between 1 and 9. + * + * Default is 9, i.e. best compression. + */ uint16_t level; + + /** + * @brief Deflate window size. Value between 8 and 15. + * + * Default is 15, i.e. 32k window. + */ uint16_t window_size; } gzip; + /** + * @brief Options for the zstd compressor. + */ struct { + /** + * @brief Compression level. Value between 1 and 22. + * + * Default is 15. + */ uint16_t level; } zstd; + /** + * @brief Options for the lzo compressor. + */ struct { + /** + * @brief Which variant of lzo should be used. + * + * An @ref SQFS_LZO_ALGORITHM value. Default is + * @ref SQFS_LZO1X_999, i.e. best compression. + */ uint16_t algorithm; + + /** + * @brief Compression level for @ref SQFS_LZO1X_999. + * + * If the selected algorithm is @ref SQFS_LZO1X_999, + * this can be a value between 0 and 9. For all other + * algorithms it has to be 0. + * + * Defaults to 9, i.e. best compression. + */ uint16_t level; } lzo; + /** + * @brief Options for the xz compressor. + */ struct { + /** + * @brief LZMA dictionary size. + * + * This value must either be a power of two or the sumo + * of two consecutive powers of two. + * + * Default is setting this to the same as the + * block size. + */ uint32_t dict_size; } xz; } opt; }; +/** + * @enum SQFS_COMP_FLAG + * + * @brief Flags for configuring the compressor. + */ typedef enum { + /** + * @brief For LZ4, set this to use high compression mode. + */ SQFS_COMP_FLAG_LZ4_HC = 0x0001, SQFS_COMP_FLAG_LZ4_ALL = 0x0001, + /** + * @brief For LZMA, set this to select the x86 BCJ filter. + */ SQFS_COMP_FLAG_XZ_X86 = 0x0001, + + /** + * @brief For LZMA, set this to select the PowerPC BCJ filter. + */ SQFS_COMP_FLAG_XZ_POWERPC = 0x0002, + + /** + * @brief For LZMA, set this to select the Itanium BCJ filter. + */ SQFS_COMP_FLAG_XZ_IA64 = 0x0004, + + /** + * @brief For LZMA, set this to select the ARM BCJ filter. + */ SQFS_COMP_FLAG_XZ_ARM = 0x0008, + + /** + * @brief For LZMA, set this to select the ARM Thumb BCJ filter. + */ SQFS_COMP_FLAG_XZ_ARMTHUMB = 0x0010, + + /** + * @brief For LZMA, set this to select the Sparc BCJ filter. + */ SQFS_COMP_FLAG_XZ_SPARC = 0x0020, SQFS_COMP_FLAG_XZ_ALL = 0x003F, + /** + * @brief For zlib deflate, set this to try the default strategy. + */ SQFS_COMP_FLAG_GZIP_DEFAULT = 0x0001, + + /** + * @brief For zlib deflate, set this to try the "filtered" strategy. + */ SQFS_COMP_FLAG_GZIP_FILTERED = 0x0002, + + /** + * @brief For zlib deflate, set this to try the huffman only strategy. + */ SQFS_COMP_FLAG_GZIP_HUFFMAN = 0x0004, + + /** + * @brief For zlib deflate, set this to try the RLE strategy. + */ SQFS_COMP_FLAG_GZIP_RLE = 0x0008, + + /** + * @brief For zlib deflate, set this to try the fixed strategy. + */ SQFS_COMP_FLAG_GZIP_FIXED = 0x0010, SQFS_COMP_FLAG_GZIP_ALL = 0x001F, + /** + * @brief Set this if the compressor should actually extract + * instead of compress data. + */ SQFS_COMP_FLAG_UNCOMPRESS = 0x8000, SQFS_COMP_FLAG_GENERIC_ALL = 0x8000, } SQFS_COMP_FLAG; +/** + * @enum SQFS_LZO_ALGORITHM + * + * @brief The available LZO algorithms. + */ typedef enum { SQFS_LZO1X_1 = 0, SQFS_LZO1X_1_11 = 1, @@ -137,17 +308,71 @@ typedef enum { extern "C" { #endif +/** + * @brief Initialize a compressor configuration + * + * The detail configuration options are all initialized to the defaults for + * the compressor in question. + * + * @param cfg A pointer to a compressor configuration to initialize + * @param id The compressor id to set. + * @param block_size The block size to set. + * @param flags The compressor flags to set. + * + * @return Zero on success, an @ref E_SQFS_ERROR value if some of the options + * don't make sense (e.g. unknown flags are used). + */ SQFS_API int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, E_SQFS_COMPRESSOR id, size_t block_size, uint16_t flags); +/** + * @brief Check if a specified compressor implementation is available. + * + * @param id An @ref E_SQFS_COMPRESSOR identifier. + * + * @return true if the implementation is available and can be instantiated + * through @ref sqfs_compressor_create. + */ SQFS_API bool sqfs_compressor_exists(E_SQFS_COMPRESSOR id); +/** + * @brief Create an instance of a compressor implementation. + * + * @param cfg A pointer to a compressor configuration. + * + * @return A pointer to a compressor object on success, NULL on allocation + * failure or if initializing the compressor failed. + */ SQFS_API sqfs_compressor_t *sqfs_compressor_create(const sqfs_compressor_config_t *cfg); +/** + * @brief Get the name of a compressor backend from its ID. + * + * This function will even resolve compressor names that are not built in, so + * use @ref sqfs_compressor_exists to check if a compressor is actually + * available. + * + * @param id An @ref E_SQFS_COMPRESSOR identifier. + * + * @return A string holding the name of the compressor, NULL if the compressor + * ID is not known. + */ SQFS_API const char *sqfs_compressor_name_from_id(E_SQFS_COMPRESSOR id); +/** + * @brief Get the compressor ID using just the name of the backend. + * + * This function will even resolve compressor names that are not built in, so + * use @ref sqfs_compressor_exists to check if a compressor is actually + * available. + * + * @param name The name of the compressor backend. + * @param out Returns the coresponding @ref E_SQFS_COMPRESSOR identifier. + * + * @return Zero on success, -1 if the backend is unknown. + */ SQFS_API int sqfs_compressor_id_from_name(const char *name, E_SQFS_COMPRESSOR *out); diff --git a/include/sqfs/data.h b/include/sqfs/data.h index 818d427..70f285f 100644 --- a/include/sqfs/data.h +++ b/include/sqfs/data.h @@ -22,15 +22,38 @@ #include "sqfs/predef.h" +/** + * @file data.h + * + * @brief Contains on-disk data structures for data block management + * and helper macros. + */ + #define SQFS_META_BLOCK_SIZE 8192 #define SQFS_IS_BLOCK_COMPRESSED(size) (((size) & (1 << 24)) == 0) #define SQFS_ON_DISK_BLOCK_SIZE(size) ((size) & ((1 << 24) - 1)) #define SQFS_IS_SPARSE_BLOCK(size) (SQFS_ON_DISK_BLOCK_SIZE(size) == 0) +/** + * @struct sqfs_fragment_t + * + * @brief Data structure that makes up the fragment table entries. + */ struct sqfs_fragment_t { + /** + * @brief Location of the fragment block on-disk. + */ uint64_t start_offset; + + /** + * @brief Size of the fragment block in bytes. + */ uint32_t size; + + /** + * @brief Unused. Always initialize this to 0. + */ uint32_t pad0; }; diff --git a/include/sqfs/dir.h b/include/sqfs/dir.h index 6bcb278..19c1289 100644 --- a/include/sqfs/dir.h +++ b/include/sqfs/dir.h @@ -22,26 +22,150 @@ #include "sqfs/predef.h" +/** + * @file dir.h + * + * @brief Contains on-disk data structures for the directory table and + * declarations for the @ref sqfs_dir_writer_t. + */ + +/** + * @struct sqfs_dir_writer_t + * + * @brief Abstracts generating of directory entries + * + * SquashFS stores directory entries and inodes seperated from each other. The + * inodes are stored in a series of meta data blocks before another series of + * meta data blocks that contain the directory entries. Directory inodes point + * to meta data block (and offset) where its contents are listed and the + * entries in turn point back to the inodes that represent them. + * + * There are some rules to this. Directory entries have to be written in + * ASCIIbetical ordering. Up to 256 entries are preceeded by a header. The + * entries use delta encoding for inode numbers and block locations relative to + * the header, so every time the inodes cross a meta data block boundary, if + * the difference in inode number gets too large, or if the entry count would + * exceed 256, a new header has to be emitted. Even if the inode pointed to is + * an extended type, the entry in the header still has to indicate the base + * type. + * + * In addtion to that, extended directory inodes can contain an index for + * faster lookup. The index points to each header and requires a new header to + * be emitted if the entries cross a block boundary. + * + * The dir writer takes care of all of this and provides a simple interface for + * adding entries. Internally it fills data into a meta data writer and + * generates an index that it can, on request, write to another meta data + * writer used for inodes. + */ + #define SQFS_MAX_DIR_ENT 256 +/** + * @struct sqfs_dir_header_t + * + * @brief On-disk data structure of a directory header + * + * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores + * directories on disk. + */ struct sqfs_dir_header_t { + /** + * @brief The number of @ref sqfs_dir_entry_t entries that are + * following. + * + * This value is stored off by one and the total count must not + * exceed 256. + */ uint32_t count; + + /** + * @brief The location of the meta data block containing the inodes for + * the entries that follow, relative to the start of the inode + * table. + */ uint32_t start_block; + + /** + * @brief The inode number of the first entry. + */ uint32_t inode_number; }; +/** + * @struct sqfs_dir_entry_t + * + * @brief On-disk data structure of a directory entry. Many of these + * follow a single @ref sqfs_dir_header_t. + * + * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores + * directories on disk. + */ struct sqfs_dir_entry_t { + /** + * @brief An offset into the uncompressed meta data block containing + * the coresponding inode. + */ uint16_t offset; + + /** + * @brief Signed difference of the inode number from the one + * in the @ref sqfs_dir_header_t. + */ int16_t inode_diff; + + /** + * @brief The @ref E_SQFS_INODE_TYPE value for the inode that this + * entry represents. + */ uint16_t type; + + /** + * @brief The size of the entry name + * + * This value is stored off-by-one. + */ uint16_t size; + + /** + * @brief The name of the directory entry (no trailing null-byte). + */ uint8_t name[]; }; +/** + * @struct sqfs_dir_index_t + * + * @brief On-disk data structure of a directory index. A series of those + * can follow an @ref sqfs_inode_dir_ext_t. + * + * See @ref sqfs_dir_writer_t for an overview on how SquashFS stores + * directories on disk. + */ struct sqfs_dir_index_t { + /** + * @brief Linear byte offset into the decompressed directory listing. + */ uint32_t index; + + /** + * @brief Location of the meta data block, relative to the directory + * table start. + */ uint32_t start_block; + + /** + * @brief Size of the name of the first entry after the header. + * + * This value is stored off-by-one. + */ uint32_t size; + + /** + * @brief Name of the name of the first entry after the header. + * + * No trailing null-byte. + */ uint8_t name[]; }; @@ -49,25 +173,129 @@ struct sqfs_dir_index_t { extern "C" { #endif +/** + * @brief Create a directory writer. + * + * @memberof sqfs_dir_writer_create + * + * @param dm A pointer to a meta data writer that the generated directory + * entries should be written to. + * + * @return A pointer to a directory writer on success, NULL on + * allocation failure. + */ SQFS_API sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm); +/** + * @brief Destroy a directory writer and free all its memory. + * + * @memberof sqfs_dir_writer_create + * + * @param writer A pointer to a directory writer object. + */ SQFS_API void sqfs_dir_writer_destroy(sqfs_dir_writer_t *writer); +/** + * @brief Begin writing a directory, i.e. reset and initialize all internal + * state neccessary. + * + * @memberof sqfs_dir_writer_create + * + * @param writer A pointer to a directory writer object. + * + * @return Zero on success, a @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer); +/** + * @brief Add add a directory entry. + * + * @memberof sqfs_dir_writer_create + * + * @param writer A pointer to a directory writer object. + * @param name The name of the directory entry. + * @param inode_num The inode number of the entry. + * @param inode_ref A reference to the inode, i.e. the meta data block offset + * is stored in bits 16 to 48 and the lower 16 bit hold an + * offset into the block. + * @param mode A file mode, i.e. type and permission bits from which the entry + * type is derived internally. + * + * @return Zero on success, a @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, uint32_t inode_num, uint64_t inode_ref, mode_t mode); +/** + * @brief Finish writing a directory listing and write everything out to the + * meta data writer. + * + * @memberof sqfs_dir_writer_create + * + * @param writer A pointer to a directory writer object. + * + * @return Zero on success, a @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_dir_writer_end(sqfs_dir_writer_t *writer); +/** + * @brief Get the total, uncompressed size of the last written + * directory in bytes. + * + * @memberof sqfs_dir_writer_create + * + * Call this function after @ref sqfs_dir_writer_end to get the uncompressed + * size of the directory listing that is required for the directory inodes. + * And also to determine which kind of directory inode to create. + * + * @param writer A pointer to a directory writer object. + * + * @return The size of the entire, uncompressed listing in bytes. + */ SQFS_API size_t sqfs_dir_writer_get_size(sqfs_dir_writer_t *writer); +/** + * @brief Get the location of the last written directory. + * + * @memberof sqfs_dir_writer_create + * + * Call this function after @ref sqfs_dir_writer_end to get the location of + * the directory listing that is required for the directory inodes. + * + * @param writer A pointer to a directory writer object. + * + * @return A meta data reference, i.e. bits 16 to 48 contain the block start + * and the lower 16 bit an offset into the uncompressed block. + */ SQFS_API uint64_t sqfs_dir_writer_get_dir_reference(sqfs_dir_writer_t *writer); +/** + * @brief Get the size of the index of the last written directory. + * + * @memberof sqfs_dir_writer_create + * + * Call this function after @ref sqfs_dir_writer_end to get the size of + * the directory index that is required for extended directory inodes. + * + * @param writer A pointer to a directory writer object. + * + * @return The number of index entries. + */ SQFS_API size_t sqfs_dir_writer_get_index_size(sqfs_dir_writer_t *writer); +/** + * @brief Write the index of the index of the last written directory to + * a meta data writer after the extended directory inode. + * + * @memberof sqfs_dir_writer_create + * + * @param writer A pointer to a directory writer object. + * @param im A pointer to a meta data writer to write the index to. + * + * @return Zero on success, a @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_dir_writer_write_index(sqfs_dir_writer_t *writer, sqfs_meta_writer_t *im); diff --git a/include/sqfs/error.h b/include/sqfs/error.h index a755c0b..a9a0acf 100644 --- a/include/sqfs/error.h +++ b/include/sqfs/error.h @@ -20,18 +20,88 @@ #ifndef SQFS_ERROR_H #define SQFS_ERROR_H +/** + * @file error.h + * + * @brief Contains the @ref E_SQFS_ERROR enumerator. + */ + +/** + * @enum E_SQFS_ERROR + * + * @brief Error codes that can be returned by various libsquashfs functions. + */ typedef enum { + /** + * @brief Allocation using malloc or calloc failed (returned NULL). + */ SQFS_ERROR_ALLOC = -1, + + /** + * @brief Generic I/O error if a file read or write operation failed. + */ SQFS_ERROR_IO = -2, + + /** + * @brief Generic compressor error returned if compressing data failed + * (some kind of internal error) or extracting failed (typically + * means the data is corrupted). + */ SQFS_ERROR_COMRPESSOR = -3, + + /** + * @brief An internal error of the "this wasn't supposed to happen" + * kind that cannot easily be mapped to something usefull. + */ SQFS_ERROR_INTERNAL = -4, + + /** + * @brief Attempted to read an on-disk data structure that appears to + * be corrupted, i.e. contains obvious non-sense values. + */ SQFS_ERROR_CORRUPTED = -5, + + /** + * @brief Attempted to use an unsupported feature (e.g. an unknown + * compressor or xattr type). + */ SQFS_ERROR_UNSUPPORTED = -6, + + /** + * @brief Attempted to read a data structure into memory would + * overflow the addressable memory. Usually indicates a + * corrupted or maliciously manipulated SquashFS filesystem. + */ SQFS_ERROR_OVERFLOW = -7, + + /** + * @brief Attempted to perform an out-of-bounds read. If this happens + * when following a reference stored in a data structure, it + * usually indicates a corrupted or maliciously manipulated + * SquashFS filesystem. + */ SQFS_ERROR_OUT_OF_BOUNDS = -8, + /** + * @brief Specific error when reading the super block. + * + * Could not find the magic. + */ SFQS_ERROR_SUPER_MAGIC = -9, + + /** + * @brief Specific error when reading the super block. + * + * The version indicated be the filesystem is not supported. + */ SFQS_ERROR_SUPER_VERSION = -10, + + /** + * @brief Specific error when reading or initializing the super block. + * + * The block size specified is either not a power of 2, or outside the + * legal range (4k to 1M). + */ SQFS_ERROR_SUPER_BLOCK_SIZE = -11, } E_SQFS_ERROR; diff --git a/include/sqfs/id_table.h b/include/sqfs/id_table.h index 87e13f5..6c39cf2 100644 --- a/include/sqfs/id_table.h +++ b/include/sqfs/id_table.h @@ -22,30 +22,104 @@ #include "sqfs/predef.h" +/** + * @file id_table.h + * + * @brief Contains declarations for the @ref sqfs_id_table_t data structure. + */ + +/** + * @struct sqfs_id_table_t + * + * @brief A simple data structure that encapsulates ID to index mapping for + * user and group IDs. + * + * SquashFS does not store user and group IDs in inodes directly. Instead, it + * collects the unique 32 bit IDs in a table with at most 64k entries and + * stores a 16 bit index into the inode. This allows SquashFS to only have 16 + * bit UID/GID entries in the inodes but actually have 32 bit UIDs/GIDs under + * the hood (at least 64k selected ones). + */ + #ifdef __cplusplus extern "C" { #endif -/* Prints error message to stderr on failure. */ +/** + * @brief Create an ID table object. + * + * @memberof sqfs_id_table_t + * + * @return A pointer to an ID table object, NULL on allocation failure. + */ SQFS_API sqfs_id_table_t *sqfs_id_table_create(void); +/** + * @brief Destroy an ID table object and free all memory used by it. + * + * @memberof sqfs_id_table_t + * + * @param tbl A pointer to an ID table object. + */ SQFS_API void sqfs_id_table_destroy(sqfs_id_table_t *tbl); -/* Resolve a 32 bit to a 16 bit table index. - Returns 0 on success. Internally prints errors to stderr. */ +/** + * @brief Resolve a 32 bit ID to a unique 16 bit index. + * + * @memberof sqfs_id_table_t + * + * @param tbl A pointer to an ID table object. + * @param id The ID to resolve. + * @param out Returns the unique table index. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ SQFS_API int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, uint32_t id, uint16_t *out); -/* Write an ID table to a SquashFS image. - Returns 0 on success. Internally prints error message to stderr. */ +/** + * @brief Write an ID table to disk. + * + * @memberof sqfs_id_table_t + * + * @param tbl A pointer to an ID table object. + * @param file The underlying file to append the table to. + * @param super A pointer to a super block in which to store the ID table + * start location. + * @param cmp A compressor to use to compress the ID table. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ SQFS_API int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); -/* Read an ID table from a SquashFS image. - Returns 0 on success. Internally prints error messages to stderr. */ +/** + * @brief Read an ID table from disk. + * + * @memberof sqfs_id_table_t + * + * @param tbl A pointer to an ID table object. + * @param file The underlying file to read the table from. + * @param super A pointer to a super block from which to get + * the ID table location. + * @param cmp A compressor to use to extract compressed table blocks. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ SQFS_API int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); +/** + * @brief Resolve a 16 bit index to a 32 bit ID. + * + * @memberof sqfs_id_table_t + * + * @param tbl A pointer to an ID table object. + * @param index The table index to resolve. + * @param out Returns the underlying 32 bit ID that the index maps to. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ SQFS_API int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, uint16_t index, uint32_t *out); diff --git a/include/sqfs/inode.h b/include/sqfs/inode.h index d0c49a1..0019360 100644 --- a/include/sqfs/inode.h +++ b/include/sqfs/inode.h @@ -22,6 +22,17 @@ #include "sqfs/predef.h" +/** + * @file inode.h + * + * @brief Contains on-disk data structures used for inodes. + */ + +/** + * @enum E_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, @@ -39,91 +50,389 @@ typedef enum { SQFS_INODE_EXT_SOCKET = 14, } E_SQFS_INODE_TYPE; +/** + * @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 E_SQFS_INODE_TYPE value. + */ uint16_t type; + + /** + * @brief Mode filed holding permission bits only. The type is derived + * from the type field. + */ uint16_t mode; + + /** + * @brief An index into the ID table where the owner UID is located. + */ uint16_t uid_idx; + + /** + * @brief An index into the ID table where the owner GID is located. + */ uint16_t 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). + */ uint32_t mod_time; + + /** + * @brief Unique inode number + */ uint32_t 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. + */ uint32_t nlink; + + /** + * @brief Device number. + */ uint32_t 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. + */ uint32_t nlink; + + /** + * @brief Device number. + */ uint32_t devno; + + /** + * @brief Extended attribute index. + */ uint32_t 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. + */ uint32_t 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. + */ uint32_t nlink; + + /** + * @brief Extended attribute index. + */ uint32_t 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. + */ uint32_t nlink; + + /** + * @brief Size of the symlink target in bytes + */ uint32_t target_size; + /*uint8_t 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. + */ uint32_t nlink; + + /** + * @brief Size of the symlink target in bytes + */ uint32_t target_size; + /*uint8_t target[];*/ + + /** + * @brief Extended attribute index. + */ uint32_t 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. + */ uint32_t blocks_start; + + /** + * @brief Index into the fragment table or 0xFFFFFFFF if unused. + */ uint32_t fragment_index; + + /** + * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF + * if unused. + */ uint32_t fragment_offset; + + /** + * @brief Total, uncompressed size of the file in bytes. + */ uint32_t file_size; + /*uint32_t 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. + */ uint64_t blocks_start; + + /** + * @brief Total, uncompressed size of the file in bytes. + */ uint64_t file_size; + + /** + * @brief If the file is sparse, holds the number of bytes not written + * to disk because of the omitted sparse blocks. + */ uint64_t sparse; + + /** + * @brief Number of hard links to this node. + */ uint32_t nlink; + + /** + * @brief Index into the fragment table or 0xFFFFFFFF if unused. + */ uint32_t fragment_idx; + + /** + * @brief Offset into the uncompressed fragment block or 0xFFFFFFFF + * if unused. + */ uint32_t fragment_offset; + + /** + * @brief Extended attribute index. + */ uint32_t xattr_idx; + /*uint32_t 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. + */ uint32_t start_block; + + /** + * @brief Number of hard links to this node. + */ uint32_t nlink; + + /** + * @brief Combined size of all directory entries and headers in bytes. + */ uint16_t size; + + /** + * @brief Offset into the uncompressed start block where the header can + * be found. + */ uint16_t offset; + + /** + * @brief Inode number of the parent directory containing + * this directory inode. + */ uint32_t 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. + */ uint32_t nlink; + + /** + * @brief Combined size of all directory entries and headers in bytes. + */ uint32_t size; + + /** + * @brief Offset from the directory table start to the location of the + * meta data block containing the first directory header. + */ uint32_t start_block; + + /** + * @brief Inode number of the parent directory containing + * this directory inode. + */ uint32_t parent_inode; + + /** + * @brief Number of directory index entries following the inode + * + * This number is stored off-by one and counts the number of + * @ref sqfs_dir_index_t entries following the inode. + */ uint16_t inodex_count; + + /** + * @brief Offset into the uncompressed start block where the header can + * be found. + */ uint16_t offset; + + /** + * @brief Extended attribute index. + */ uint32_t 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 A pointer into the extra field holding the symlink target. + * + * @param This string is not null terminated. The helper functions rely + * entirely on the length stored in the symlink inode. + */ char *slink_target; + + /** + * @brief A pointer into the extra field holding file blocks sizes. + * + * For file inodes, holds the consecutive block sizes. Bit number 24 is + * set if the block is stored uncompressed. If it the size is zero, + * the block is sparse. + */ uint32_t *block_sizes; + + /** + * @brief For file inodes, stores the number of blocks used. + */ size_t num_file_blocks; + /** + * @brief Type specific inode data. + */ union { sqfs_inode_dev_t dev; sqfs_inode_dev_ext_t dev_ext; @@ -137,6 +446,9 @@ struct sqfs_inode_generic_t { sqfs_inode_dir_ext_t dir_ext; } data; + /** + * @brief Holds type specific extra data, such as symlink target. + */ uint8_t extra[]; }; diff --git a/include/sqfs/io.h b/include/sqfs/io.h index 59739ec..9288010 100644 --- a/include/sqfs/io.h +++ b/include/sqfs/io.h @@ -22,25 +22,101 @@ #include "sqfs/predef.h" +/** + * @file io.h + * + * @brief Contains the @ref sqfs_file_t interface for abstracting file I/O + */ + +/** + * @enum E_SQFS_FILE_OPEN_FLAGS + * + * @brief Flags for @ref sqfs_open_file + */ typedef enum { + /** + * @brief If set, access the file for reading only + * + * If not set, the file is expected to have a zero size after opening + * which can be grown with successive writes to end of the file. + * + * Opening an existing file with this flag cleared results in failure, + * unless the @ref SQFS_FILE_OPEN_OVERWRITE flag is also set. + */ SQFS_FILE_OPEN_READ_ONLY = 0x01, + /** + * @brief If the read only flag is not set, overwrite any + * existing file. + * + * If the file alrady exists, it is truncated to zero bytes size and + * overwritten. + */ SQFS_FILE_OPEN_OVERWRITE = 0x02, SQFS_FILE_OPEN_ALL_FLAGS = 0x03, } E_SQFS_FILE_OPEN_FLAGS; +/** + * @interface sqfs_file_t + * + * @brief Abstracts file I/O to make it easy to embedd SquashFS. + */ struct sqfs_file_t { + /** + * @brief Close the file and destroy the interface implementation. + * + * @param file A pointer to the file object. + */ void (*destroy)(sqfs_file_t *file); + /** + * @brief Read a chunk of data from an absolute position. + * + * @param file A pointer to the file object. + * @param offset An absolute offset to read data from. + * @param buffer A pointer to a buffer to copy the data to. + * @param size The number of bytes to read from the file. + * + * @return Zero on success, an @ref E_SQFS_ERROR identifier on failure + * that the data structures in libsquashfs that use this return + * directly to the caller. + */ int (*read_at)(sqfs_file_t *file, uint64_t offset, void *buffer, size_t size); + /** + * @brief Write a chunk of data at an absolute position. + * + * @param file A pointer to the file object. + * @param offset An absolute offset to write data to. + * @param buffer A pointer to a buffer to write to the file. + * @param size The number of bytes to write from the buffer. + * + * @return Zero on success, an @ref E_SQFS_ERROR identifier on failure + * that the data structures in libsquashfs that use this return + * directly to the caller. + */ int (*write_at)(sqfs_file_t *file, uint64_t offset, const void *buffer, size_t size); + /** + * @brief Get the number of bytes currently stored in the file. + * + * @param file A pointer to the file object. + */ uint64_t (*get_size)(sqfs_file_t *file); + /** + * @brief Extend or shrink a file to a specified size. + * + * @param file A pointer to the file object. + * @param size The new capacity of the file in bytes. + * + * @return Zero on success, an @ref E_SQFS_ERROR identifier on failure + * that the data structures in libsquashfs that use this return + * directly to the caller. + */ int (*truncate)(sqfs_file_t *file, uint64_t size); }; @@ -48,6 +124,22 @@ struct sqfs_file_t { extern "C" { #endif +/** + * @brief Open a file through the operating systems filesystem API + * + * This function internally creates an instance of a reference implementation + * of the @ref sqfs_file_t interface that uses the operating systems native + * API for file I/O. + * + * On Unix-like systems, if the open call fails, this function makes sure to + * preserves the value in errno indicating the underlying problem. + * + * @param filename The name of the file to open. + * @param flags A set of @ref E_SQFS_FILE_OPEN_FLAGS. + * + * @return A pointer to a file object on success, NULL on allocation failure, + * failure to open the file or if an unknown flag was set. + */ SQFS_API sqfs_file_t *sqfs_open_file(const char *filename, int flags); #ifdef __cplusplus diff --git a/include/sqfs/meta_reader.h b/include/sqfs/meta_reader.h index 1a9e869..989e857 100644 --- a/include/sqfs/meta_reader.h +++ b/include/sqfs/meta_reader.h @@ -22,46 +22,162 @@ #include "sqfs/predef.h" +/** + * @file meta_reader.h + * + * @brief Contains declarations for the @ref sqfs_meta_reader_t. + */ + +/** + * @struct sqfs_meta_reader_t + * + * @brief Abstracts reading of meta data blocks. + * + * SquashFS stores meta data by dividing it into fixed size (8k) chunks + * that are written to disk with a small header indicating the on-disk + * size and whether it is compressed or not. + * + * Data written to meta data blocks doesn't have to be alligned, i.e. + * SquashFS doesn't care if an object is written across two blocks. + * + * The main task of the meta data read is to provide a simple read and seek + * functions that transparently take care of fetching and uncompressing blocks + * from disk and reading transparently across block boarders if required. + */ + #ifdef __cplusplus extern "C" { #endif -/* Create a meta data reader using a given compressor to extract data. - Internally prints error message to stderr on failure. - - Start offset and limit can be specified to do bounds checking against - a subregion of the filesystem image. -*/ +/** + * @brief Create a meta data reader + * + * @memberof sqfs_meta_reader_t + * + * @param file A pointer to a file object to read from + * @param cmp A compressor to use for unpacking compressed meta data blocks + * @param start A lower limit for the blocks to be read. Every seek to an offset + * below that is interpreted as an out-of-bounds read. + * @param limit An upper limit for the blocks to read. Every seek to an offset + * afer that is interpreted as an out-of-bounds read. + * + * @return A pointer to a meta data reader on success, NULL on + * allocation failure. + */ SQFS_API sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, sqfs_compressor_t *cmp, uint64_t start, uint64_t limit); +/** + * @brief Destroy a meta data reader and free all memory used by it. + * + * @memberof sqfs_meta_reader_t + * + * @param m A pointer to a meta data reader. + */ SQFS_API void sqfs_meta_reader_destroy(sqfs_meta_reader_t *m); -/* Returns 0 on success. Internally prints to stderr on failure */ +/** + * @brief Seek to a specific meta data block and offset. + * + * @memberof sqfs_meta_reader_t + * + * The underlying block is fetched from disk and uncompressed, unless it + * already is the current block. + * + * @param m A pointer to a meta data reader. + * @param block_start Absolute position where the block header can be found. + * @param offset A byte offset into the uncompressed block. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, uint64_t block_start, size_t offset); +/** + * @brief Get the current position that the next read will read from. + * + * @memberof sqfs_meta_reader_t + * + * @param m A pointer to a meta data reader. + * @param block_start Absolute position where the current block is. + * @param offset A byte offset into the uncompressed block. + */ SQFS_API void sqfs_meta_reader_get_position(sqfs_meta_reader_t *m, uint64_t *block_start, size_t *offset); -/* Returns 0 on success. Internally prints to stderr on failure */ +/** + * @brief Read a chunk of data from a meta data reader. + * + * @memberof sqfs_meta_reader_t + * + * If the meta data reader reaches the end of the current block before filling + * the destination buffer, it transparently reads the next block from disk and + * uncompresses it. + * + * @param m A pointer to a meta data reader. + * @param data A pointer to copy the data to. + * @param size The numbre of bytes to read. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size); -/* Returns 0 on success. Internally prints to stderr on failure */ +/** + * @brief Read and decode a directory header from a meta data reader. + * + * @memberof sqfs_meta_reader_t + * + * This is a convenience function on to of @ref sqfs_meta_reader_read that + * reads and decodes a directory header from a meta data block. + * + * @param m A pointer to a meta data reader. + * @param hdr A pointer to a directory header to fill. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, sqfs_dir_header_t *hdr); -/* Entry can be freed with a single free() call. - The function internally prints to stderr on failure */ +/** + * @brief Read and decode a directory header from a meta data reader. + * + * @memberof sqfs_meta_reader_t + * + * This is a convenience function on to of @ref sqfs_meta_reader_read that + * reads and decodes a directory entry. + * + * @param m A pointer to a meta data reader. + * @param ent Returns a pointer to a directory entry. Can be released with a + * single free() call. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, sqfs_dir_entry_t **ent); -/* Inode can be freed with a single free() call. - The function internally prints error message to stderr on failure. */ +/** + * @brief Read and decode an inode from a meta data reader. + * + * @memberof sqfs_meta_reader_t + * + * This is a convenience function on to of @ref sqfs_meta_reader_seek and + * @ref sqfs_meta_reader_read that reads and decodes an inode. + * + * @param ir A pointer to a meta data reader. + * @param super A pointer to the super block, required for figuring out the + * size of file inodes. + * @param block_start The meta data block to seek to for reading the inode. + * @param offset A byte offset within the uncompressed block where the + * inode is. + * @param out Returns a pointer to an inode. Can be released with a + * single free() call. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, sqfs_super_t *super, uint64_t block_start, size_t offset, diff --git a/include/sqfs/meta_writer.h b/include/sqfs/meta_writer.h index b3da355..c0d5716 100644 --- a/include/sqfs/meta_writer.h +++ b/include/sqfs/meta_writer.h @@ -22,42 +22,159 @@ #include "sqfs/predef.h" +/** + * @file meta_writer.h + * + * @brief Contains declarations for the @ref sqfs_meta_writer_t. + */ + +/** + * @struct sqfs_meta_writer_t + * + * @brief Abstracts generating of meta data blocks, either in memory or + * directly on disk. + * + * SquashFS stores meta data by dividing it into fixed size (8k) chunks + * that are written to disk with a small header indicating the on-disk + * size and whether it is compressed or not. + * + * Data written to meta data blocks doesn't have to be alligned, i.e. + * SquashFS doesn't care if an object is written across two blocks. + * + * The main task of the meta data writer is to provide a simple append + * function that transparently takes care of chopping data up into blocks, + * compressing the blocks and pre-pending a header. + */ + #ifdef __cplusplus extern "C" { #endif -/* Create a meta data reader using a given compressor to compress data. - Internally prints error message to stderr on failure. - If keep_in_mem is true, the blocks are collected in memory and must - be explicitly flushed to disk using meta_write_write_to_file. -*/ +/** + * @brief Create a meta data writer. + * + * @memberof sqfs_meta_writer_t + * + * @note The meta writer internally keeps references to the pointers passed + * to this function, so don't destroy them before destroying the + * meta writer. + * + * @param file An output file to write the data to. + * @param cmp A compressor to use. + * @param keep_in_memory Set to true to store the compressed blocks + * internally until they are explicilty told + * to write them to disk. + * + * @return A pointer to a meta writer on success, NULL on allocation failure. + */ SQFS_API sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, sqfs_compressor_t *cmp, bool keep_in_mem); +/** + * @brief Destroy a meta data writer and free all memory used by it. + * + * @memberof sqfs_meta_writer_t + * + * @param m A pointer to a meta data writer. + */ SQFS_API void sqfs_meta_writer_destroy(sqfs_meta_writer_t *m); -/* Compress and flush the currently unfinished block to disk. Returns 0 on - success, internally prints error message to stderr on failure */ +/** + * @brief Finish the current block, even if it isn't full yet. + * + * @memberof sqfs_meta_writer_t + * + * This function forces the meta writer to compress and store the block it + * is currently writing to, even if it isn't full yet, and either write it + * out to disk (or append it to the in memory chain if told to keep blocks + * in memory). + * + * @param m A pointer to a meta data writer. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_writer_flush(sqfs_meta_writer_t *m); -/* Returns 0 on success. Prints error message to stderr on failure. */ +/** + * @brief Finish the current block, even if it isn't full yet. + * + * @memberof sqfs_meta_writer_t + * + * This function forces reads a speicifed number of bytes from a given data + * block and appends it to the meta data block that the writer is currently + * working on. If the block becomes full, it is compressed, written to disk + * and a new block is started that the remaining data is written to. + * + * @param m A pointer to a meta data writer. + * @param data A pointer to a chunk of data to append to the writer. + * @param size The number of data bytes to append. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, size_t size); -/* Query the current block start position and offset within the block */ +/** + * @brief Query the current block start position and offset within the block + * + * @memberof sqfs_meta_writer_t + * + * Get the byte offset relative to the first block that the current block will + * start at once it is written to disk and get the byte offset within this + * block that the next call to @ref sqfs_meta_writer_append will start writing + * data at. + * + * @param m A pointer to a meta data writer. + * @param block_start Returns the offset of the current block from the first. + * @param offset Returns an offset into the current block where the next write + * starts. + */ SQFS_API void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, uint64_t *block_start, uint32_t *offset); -/* Reset all internal state, including the current block start position. */ +/** + * @brief Reset all internal state, including the current block start position. + * + * @memberof sqfs_meta_writer_t + * + * This functions forces the meta data writer to forget everything that + * happened since it was created, so it can be recycled. + * + * The data written is not lost, unless it was kept in memory and never written + * out to disk. + */ SQFS_API void sqfs_meta_writer_reset(sqfs_meta_writer_t *m); -/* If created with keep_in_mem true, write the collected blocks to disk. - Does not flush the current block. Writes error messages to stderr and - returns non-zero on failure. */ +/** + * @brief Write all blocks collected in memory to disk + * + * @memberof sqfs_meta_writer_t + * + * If the meta writer was created with the flag set to store blocks in + * memory instead of writing them to disk, calling this function forces + * the meta writer to write out all blocks it collected so far. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m); +/** + * @brief A convenience function for encoding and writing an inode + * + * @memberof sqfs_meta_writer_t + * + * The SquashFS inode table is essentially a series of meta data blocks + * containing variable sized inodes. This function takes a generic inode + * structure, encodes it and writes the result to a meta data writer + * using @ref sqfs_meta_writer_append internally. + * + * @param iw A pointer to a meta data writer. + * @param n A pointer to an inode. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *iw, sqfs_inode_generic_t *n); diff --git a/include/sqfs/predef.h b/include/sqfs/predef.h index e98ae34..7aac1a8 100644 --- a/include/sqfs/predef.h +++ b/include/sqfs/predef.h @@ -20,6 +20,13 @@ #ifndef SQFS_PREDEF_H #define SQFS_PREDEF_H +/** + * @file predef.h + * + * @brief Includes forward declarations of data structures, + * macros and integer types. + */ + #include <sys/types.h> #include <stdbool.h> #include <stddef.h> diff --git a/include/sqfs/super.h b/include/sqfs/super.h index 4556d3d..5e6c00a 100644 --- a/include/sqfs/super.h +++ b/include/sqfs/super.h @@ -22,34 +22,172 @@ #include "sqfs/predef.h" +/** + * @file super.h + * + * @brief Contains on-disk data structures, identifiers and functions for the + * SquashFS super block. + */ + #define SQFS_MAGIC 0x73717368 #define SQFS_VERSION_MAJOR 4 #define SQFS_VERSION_MINOR 0 #define SQFS_DEVBLK_SIZE 4096 #define SQFS_DEFAULT_BLOCK_SIZE 131072 +/** + * @struct sqfs_super_t + * + * @brief The SquashFS super block, located at the beginning of the file system + * to describe the layout of the filesystem. + */ struct sqfs_super_t { + /** + * @brief Magic number. Must be set to SQFS_MAGIC. + */ uint32_t magic; + + /** + * @brief Total number of inodes. + */ uint32_t inode_count; + + /** + * @brief Last time the filesystem was modified. + * + * 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). + */ uint32_t modification_time; + + /** + * @brief The data block size in bytes. + * + * Must be a power of 2, no less than 4k and not larger than 1M. + */ uint32_t block_size; + + /** + * @brief The number of fragment blocks in the data area. + */ uint32_t fragment_entry_count; + + /** + * @brief Identifies the compressor that has been used. + * + * Valid identifiers are in the @ref E_SQFS_COMPRESSOR enum. + */ uint16_t compression_id; + + /** + * @brief The log2 of the block_size field for sanity checking + * + * Must be no less than 12 and not larger than 20. + */ uint16_t block_log; + + /** + * @brief A combination of @ref E_SQFS_SUPER_FLAGS flags + * + * Most of the flags that can be set here are informative only. + */ uint16_t flags; + + /** + * @brief The total number of unique user or group IDs. + */ uint16_t id_count; + + /** + * @brief Must be @ref SQFS_VERSION_MAJOR + */ uint16_t version_major; + + /** + * @brief Must be @ref SQFS_VERSION_MINOR + */ uint16_t version_minor; + + /** + * @brief A reference to the root inode + * + * The bits 16 to 48 hold an offset that is added to inode_table_start + * to get the location of the meta data block containing the inode. + * The lower 16 bits hold a byte offset into the uncompressed block. + */ uint64_t root_inode_ref; + + /** + * @brief Total size of the file system in bytes, not counting padding + */ uint64_t bytes_used; + + /** + * @brief On-disk location of the ID table + * + * This value must point to a location after the directory table and + * (if present) after the export and fragment tables, but before the + * xattr table. + */ uint64_t id_table_start; + + /** + * @brief On-disk location of the extended attribute table (if present) + * + * See @ref sqfs_xattr_reader_t for an overview on how SquashFS stores + * extended attributes on disk. + * + * This value must either point to a location after the ID table, or + * it must be set to 0xFFFFFFFF to indicate the table is not present. + */ uint64_t xattr_id_table_start; + + /** + * @brief On-disk location of the first meta data block containing + * the inodes + * + * This value must point to a location before the directory table. + */ uint64_t inode_table_start; + + /** + * @brief On-disk location of the first meta data block containing + * the directory entries + * + * This value must point to a location after the inode table but + * before the fragment, export, ID and xattr tables. + */ uint64_t directory_table_start; + + /** + * @brief On-disk location of the fragment table (if present) + * + * This value must either point to a location after the directory + * table, but before the export, ID and xattr tables, or it must be + * set to 0xFFFFFFFF to indicate that the table is not present. + */ uint64_t fragment_table_start; + + /** + * @brief On-disk location of the export table (if present) + * + * This value must either point to a location after directory table + * (and if present after the fragment table), but before the ID table, + * or it must be set to 0xFFFFFFFF to indicate that the table is not + * present. + */ uint64_t export_table_start; }; +/** + * @enum E_SQFS_COMPRESSOR + * + * @brief Set in @ref sqfs_super_t to identify the compresser used by the + * filesystem. + * + * Most of the flags that can be set are informative only. + */ typedef enum { SQFS_COMP_GZIP = 1, SQFS_COMP_LZMA = 2, @@ -62,17 +200,72 @@ typedef enum { SQFS_COMP_MAX = 6, } E_SQFS_COMPRESSOR; +/** + * @enum E_SQFS_SUPER_FLAGS + * + * @brief Flags that can be set in @ref sqfs_super flags field. + */ typedef enum { + /** + * @brief Set to indicate that meta data blocks holding the inodes are + * stored uncompressed. + */ SQFS_FLAG_UNCOMPRESSED_INODES = 0x0001, + + /** + * @brief Set to indicate that all data blocks are stored uncompressed. + */ SQFS_FLAG_UNCOMPRESSED_DATA = 0x0002, + + /** + * @brief Set to indicate that all fragment blocks are stored + * uncompressed. + */ SQFS_FLAG_UNCOMPRESSED_FRAGMENTS = 0x0008, + + /** + * @brief Set to indicate that there are no fragment blocks. + */ SQFS_FLAG_NO_FRAGMENTS = 0x0010, + + /** + * @brief Set to indicate that fragments have been generated for all + * files that are not a multiple of the block size in size. + */ SQFS_FLAG_ALWAYS_FRAGMENTS = 0x0020, + + /** + * @brief Set to indicate that data blocks have not been deduplicated. + */ SQFS_FLAG_DUPLICATES = 0x0040, + + /** + * @brief Set to indicate that an NFS export table is present. + */ SQFS_FLAG_EXPORTABLE = 0x0080, + + /** + * @brief Set to indicate that meta data blocks holding extended + * attributes are stored uncompressed. + */ SQFS_FLAG_UNCOMPRESSED_XATTRS = 0x0100, + + /** + * @brief Set to indicate that the filesystem does not + * contain extended attributes. + */ SQFS_FLAG_NO_XATTRS = 0x0200, + + /** + * @brief Set to indicate that a single, uncompressed meta data block + * with compressor options follows the super block. + */ SQFS_FLAG_COMPRESSOR_OPTIONS = 0x0400, + + /** + * @brief Set to indicate that meta data blocks holding the IDs are + * stored uncompressed. + */ SQFS_FLAG_UNCOMPRESSED_IDS = 0x0800, } E_SQFS_SUPER_FLAGS; @@ -80,15 +273,47 @@ typedef enum { extern "C" { #endif -/* Returns 0 on success. Prints error messages to stderr on failure. */ +/** + * @brief Initialize the SquashFS super block. + * + * @memberof sqfs_super_t + * + * @param super A pointer to a super block structure. + * @param block_size The uncompressed size of the data blocks in bytes. + * @param mtime The modification time stamp to set. + * @param compressor The compressor ID to set. + * + * @return Zero on success, an @ref E_SQFS_ERROR value if one of the + * fields does not hold a valid value. + */ SQFS_API int sqfs_super_init(sqfs_super_t *super, size_t block_size, uint32_t mtime, E_SQFS_COMPRESSOR compressor); -/* Returns 0 on success. Prints error messages to stderr on failure. */ +/** + * @brief Encode and write a SquashFS super block to disk. + * + * @memberof sqfs_super_t + * + * @param super A pointer to the super block structure to write. + * @param file A file object through which to access the filesystem image. + * + * @return Zero on success, an @ref E_SQFS_ERROR value if one of the + * fields does not hold a valid value. + */ SQFS_API int sqfs_super_write(sqfs_super_t *super, sqfs_file_t *file); -/* Returns 0 on success. Prints error messages to stderr on failure. */ +/** + * @brief Read a SquashFS super block from disk, decode it and check the fields + * + * @memberof sqfs_super_t + * + * @param super A pointer to the super block structure to fill. + * @param file A file object through which to access the filesystem image. + * + * @return Zero on success, an @ref E_SQFS_ERROR value if one of the + * fields does not hold a valid value. + */ SQFS_API int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file); #ifdef __cplusplus diff --git a/include/sqfs/table.h b/include/sqfs/table.h index 0788538..c3c1205 100644 --- a/include/sqfs/table.h +++ b/include/sqfs/table.h @@ -22,25 +22,61 @@ #include "sqfs/predef.h" +/** + * @file table.h + * + * @brief Contains helper functions for reading or writing tables. + */ + #ifdef __cplusplus extern "C" { #endif -/* - Convenience function for writing meta data to a SquashFS image - - This function internally creates a meta data writer and writes the given - 'data' blob with 'table_size' bytes to disk, neatly partitioned into meta - data blocks. For each meta data block, it remembers the 64 bit start address, - writes out all addresses to an uncompressed list and returns the location - where the address list starts in 'start'. - - Returns 0 on success. Internally prints error messages to stderr. +/** + * @brief Write a table to disk. + * + * This function takes an in-memory array, breaks it down into meta data + * blocks, compresses and writes those blocks to the given output file and + * then writes a raw list of 64 bit absolute locations of each meta data + * block. The position of the location list is returned through a pointer. + * + * @param file The output file to write to. + * @param cmp A compressor to use for compressing the meta data blocks. + * @param data A pointer to a array to divide into blocks and write to disk. + * @param table_size The size of the input array in bytes. + * @param start Returns the absolute position of the location list. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. */ SQFS_API int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, const void *data, size_t table_size, uint64_t *start); +/** + * @brief Read a table from a SquashFS filesystem. + * + * This function takes an absolute position and an array size as input. It + * then computes the number of meta data blocks required to store this array + * and reads that many 64 bit integers from the given start location. Each + * integer is interpreted as the location of a meta data block containing the + * respective array chunk. + * + * The entire data encoded in that way is read and uncompressed into memory. + * + * @param file An input file to read from. + * @param cmp A compressor to use for uncompressing the meta data block. + * @param table_size The size of the entire array in bytes. + * @param location The absolute position of the location list. + * @param lower_limit The lowest "sane" position at which to expect a meta + * data block. Anything less than that is interpreted + * as an out-of-bounds read. + * @param upper_limit The highest "sane" position at which to expect a meta + * data block. Anything after that is interpreted as an + * out-of-bounds read. + * @param out Returns a pointer to the table in memory. + * + * @return Zero on success, an @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, size_t table_size, uint64_t location, uint64_t lower_limit, uint64_t upper_limit, diff --git a/include/sqfs/xattr.h b/include/sqfs/xattr.h index 2a25cb5..71e4ffa 100644 --- a/include/sqfs/xattr.h +++ b/include/sqfs/xattr.h @@ -22,6 +22,18 @@ #include "sqfs/predef.h" +/** + * @file xattr.h + * + * @brief Contains on-disk data structures for storing extended attributes and + * declarations for the @ref sqfs_xattr_reader_t. + */ + +/** + * @enum E_SQFS_XATTR_TYPE + * + * Used by @ref sqfs_xattr_entry_t to encodes the xattr prefix. + */ typedef enum { SQFS_XATTR_USER = 0, SQFS_XATTR_TRUSTED = 1, @@ -31,65 +43,325 @@ typedef enum { SQFS_XATTR_PREFIX_MASK = 0xFF, } E_SQFS_XATTR_TYPE; +/** + * @struct sqfs_xattr_entry_t + * + * @brief On-disk data structure that holds a single xattr key + * + * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended + * attributes on disk. + */ struct sqfs_xattr_entry_t { + /** + * @brief Encodes the prefix of the key + * + * A @ref E_SQFS_XATTR_TYPE value. If the @ref SQFS_XATTR_FLAG_OOL is + * set, the value that follows is not actually a string but a 64 bit + * reference to the location where the value is actually stored. + */ uint16_t type; + + /** + * @brief The size in bytes of the suffix string that follows + */ uint16_t size; uint8_t key[]; }; +/** + * @struct sqfs_xattr_value_t + * + * @brief On-disk data structure that holds a single xattr value + * + * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended + * attributes on disk. + */ struct sqfs_xattr_value_t { + /** + * @brief The exact size in bytes of the value that follows + */ uint32_t size; uint8_t value[]; }; +/** + * @struct sqfs_xattr_id_t + * + * @brief On-disk data structure that describes a set of key-value pairs + * + * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended + * attributes on disk. + */ struct sqfs_xattr_id_t { + /** + * @brief Location of the first key-value pair + * + * This is a reference, i.e. the bits 16 to 48 hold an offset that is + * added to xattr_table_start from @ref sqfs_xattr_id_table_t to get + * the location of a meta data block that contains the first key-value + * pair. The lower 16 bits store an offset into the uncompressed meta + * data block. + */ uint64_t xattr; + + /** + * @brief Number of consecutive key-value pairs + */ uint32_t count; + + /** + * @brief Total size of the uncompressed key-value pairs in bytes, + * including data structures used to encode them. + */ uint32_t size; }; +/** + * @struct sqfs_xattr_id_table_t + * + * @brief On-disk data structure that the super block points to + * + * Indicates the locations of the xattr key-value pairs and descriptor array. + * See @ref sqfs_xattr_reader_t for an overview how SquashFS stores extended + * attributes on disk. + */ struct sqfs_xattr_id_table_t { + /** + * @brief The location of the first meta data block holding the key + * value pairs. + */ uint64_t xattr_table_start; + + /** + * @brief The total number of descriptors (@ref sqfs_xattr_id_t) + */ uint32_t xattr_ids; + + /** + * @brief Unused, alwayas set this to 0 when writing! + */ uint32_t unused; + + /** + * @brief Holds the locations of the meta data blocks that contain the + * @ref sqfs_xattr_id_t descriptor array. + */ + uint64_t locations[]; }; +/** + * @struct sqfs_xattr_reader_t + * + * @brief Abstracts read access to extended attributes in a SquashFS filesystem + * + * SquashFS stores extended attributes using multiple levels of indirection. + * First of all, the key-value pairs of each inode (that has extended + * attributes) are deduplicated and stored consecutively in meta data blocks. + * Furthermore, a value can be stored out-of-band, i.e. it holds a reference to + * another location from which the value has to be read. + * + * For each unique set of key-value pairs, a descriptor object is generated + * that holds the location of the first pair, the number of pairs and the total + * size used on disk. The array of descriptor objects is stored in multiple + * meta data blocks. Each inode that has extended attributes holds a 32 bit + * index into the descriptor array. + * + * The third layer of indirection is yet another table that points to the + * locations of the previous two tables. Its location is in turn stored in + * the super block. + * + * The sqfs_xattr_reader_t data structure takes care of the low level details + * of loading and parsing the data. + * + * After creating an instance using @ref sqfs_xattr_reader_create, simply call + * @ref sqfs_xattr_reader_load_locations to load and parse all of the location + * tables. Then use @ref sqfs_xattr_reader_get_desc to resolve a 32 bit index + * from an inode to a descriptor structure. Use @ref sqfs_xattr_reader_seek_kv + * to point the reader to the start of the key-value pairs and the call + * @ref sqfs_xattr_reader_read_key and @ref sqfs_xattr_reader_read_value + * consecutively to read and decode each key-value pair. + */ + #ifdef __cplusplus extern "C" { #endif -/* Get a prefix string from the ID or NULL if unknown */ +/** + * @brief Resolve an xattr identifier to the coresponding prefix + * + * Like many file systems, SquashFS stores xattrs be cutting off the common + * prefix of the key string and storing an enumerator instead to save memory. + * + * This function takes an @ref E_SQFS_XATTR_TYPE identifier and returns the + * coresponding prefix string, including the '.' at the end that seperates + * the prefix from the rest of the key. + */ SQFS_API const char *sqfs_get_xattr_prefix(E_SQFS_XATTR_TYPE id); -/* Get id from xattr key prefix or -1 if not supported */ +/** + * @brief Resolve an xattr prefix into an identifier + * + * Like many file systems, SquashFS stores xattrs be cutting off the common + * prefix of the key string and storing an enumerator instead to save memory. + * + * This function takes a key and finds the enumerator value that represents + * its prefix. + * + * This function will return a failure value to indicate that the given prefix + * isn't supported. Another function called @ref sqfs_has_xattr is provided to + * explicitly check if a prefix is supported. + * + * @return On success an @ref E_SQFS_XATTR_TYPE, -1 if it isn't supported. + */ SQFS_API int sqfs_get_xattr_prefix_id(const char *key); -/* Check if a given xattr key can be encoded in squashfs at all. */ +/** + * @brief Check if a given xattr key can actually be encoded in SquashFS + * + * Like many file systems, SquashFS stores xattrs be cutting off the common + * prefix of the key string and storing an enumerator instead to save memory. + * + * However, this means if new prefixes are introduced, they are not immediately + * supported since SquashFS may not have an enumerator defined for them. + * + * This function checks if the @ref sqfs_get_xattr_prefix_id function can + * translate the prefix of the given key into a coresponding enumerator. + * + * If it returns false, this means either that SquashFS doesn't support this + * prefix, or it has recently been added but the version of libsquashfs you + * are using doesn't support it. + */ SQFS_API bool sqfs_has_xattr(const char *key); +/** + * @brief Load the locations of the xattr meta data blocks into memory + * + * @memberof sqfs_xattr_reader_t + * + * This function must be called explicitly after an xattr reader has been + * created to load the actual location table from disk. + * + * @return Zero on success, a negative @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_xattr_reader_load_locations(sqfs_xattr_reader_t *xr); +/** + * @brief Destroy an xattr reader and free all memory used by it + * + * @memberof sqfs_xattr_reader_t + * + * @param xr A pointer to an xattr reader instance + */ SQFS_API void sqfs_xattr_reader_destroy(sqfs_xattr_reader_t *xr); +/** + * @brief Create an xattr reader + * + * @memberof sqfs_xattr_reader_t + * + * This function creates an object that abstracts away read only access to + * the extended attributes in a SquashFS filesystem. + * + * After creating a reader and before using it, call + * @ref sqfs_xattr_reader_load_locations to load and parse the location + * information required to look up xattr key-value pairs. + * + * All pointers passed to this function are stored internally for later use. + * Do not destroy any of the pointed to objects before destroying the xattr + * reader. + * + * @param file A pointer to a file object that contains the SquashFS filesystem + * @param super A pointer to the SquashFS super block required to find the + * location tables. + * @param cmp A pointer to a compressor used to uncompress the loaded meta data + * blocks. + * + * @return A pointer to a new xattr reader instance on success, NULL on + * allocation failure. + */ SQFS_API sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_file_t *file, sqfs_super_t *super, sqfs_compressor_t *cmp); +/** + * @brief Resolve an xattr index from an inode to an xattr description + * + * @memberof sqfs_xattr_reader_t + * + * This function takes an xattr index from an extended inode type and resolves + * it to a descriptor that points to location of the key-value pairs and + * indicates how many key-value pairs to read from there. + * + * @param xr A pointer to an xattr reader instance + * @param idx The xattr index to resolve + * @param desc Used to return the description + * + * @return Zero on success, a negative @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, uint32_t idx, sqfs_xattr_id_t *desc); +/** + * @brief Resolve an xattr index from an inode to an xattr description + * + * @memberof sqfs_xattr_reader_t + * + * This function takes an xattr descriptor object and seeks to the meta data + * block containing the key-value pairs. The individual pairs can then be read + * using consecutive calls to @ref sqfs_xattr_reader_read_key and + * @ref sqfs_xattr_reader_read_value. + * + * @param xr A pointer to an xattr reader instance + * @param desc The descriptor holding the location of the key-value pairs + * + * @return Zero on success, a negative @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, const sqfs_xattr_id_t *desc); +/** + * @brief Read the next xattr key + * + * @memberof sqfs_xattr_reader_t + * + * After setting the start position using @ref sqfs_xattr_reader_seek_kv, this + * function reads and decodes an xattr key and advances the internal position + * indicator to the location after the key. The value can then be read using + * using @ref sqfs_xattr_reader_read_value. After reading the value, the next + * key can be read by calling this function again. + * + * @param xr A pointer to an xattr reader instance + * @param key_out Used to return the decoded key. The underlying memory can be + * released using a single free() call. + * + * @return Zero on success, a negative @ref E_SQFS_ERROR value on failure. + */ +SQFS_API +int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, + sqfs_xattr_entry_t **key_out); + +/** + * @brief Read the xattr value belonging to the last read key + * + * @memberof sqfs_xattr_reader_t + * + * After calling @ref sqfs_xattr_reader_read_key, this function can read and + * decode the asociated value. The internal location indicator is then advanced + * past the key to the next value, so @ref sqfs_xattr_reader_read_key can be + * called again to read the next key. + * + * @param xr A pointer to an xattr reader instance. + * @param key A pointer to the decoded key object. + * @param val_out Used to return the decoded value. The underlying memory can + * be released using a single free() call. + * + * @return Zero on success, a negative @ref E_SQFS_ERROR value on failure. + */ SQFS_API int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, const sqfs_xattr_entry_t *key, sqfs_xattr_value_t **val_out); -SQFS_API -int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, - sqfs_xattr_entry_t **key_out); - #ifdef __cplusplus } #endif |