diff options
| author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-09-09 23:11:03 +0200 | 
|---|---|---|
| committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2019-09-09 23:12:08 +0200 | 
| commit | c17de5d4c2699c6c5b4759f009ce8cb6560d2f13 (patch) | |
| tree | ca36c1af536583856360dd6e905fe7143ee5a562 /include | |
| parent | 524869a644004b2b5eae9c6cdb14a20c0e877778 (diff) | |
Add doxygen annotations to all public headers
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Diffstat (limited to 'include')
| -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 | 
14 files changed, 2029 insertions, 111 deletions
| 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 | 
