aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--include/sqfs/block_processor.h197
-rw-r--r--include/sqfs/compress.h265
-rw-r--r--include/sqfs/data.h23
-rw-r--r--include/sqfs/dir.h228
-rw-r--r--include/sqfs/error.h70
-rw-r--r--include/sqfs/id_table.h88
-rw-r--r--include/sqfs/inode.h312
-rw-r--r--include/sqfs/io.h92
-rw-r--r--include/sqfs/meta_reader.h142
-rw-r--r--include/sqfs/meta_writer.h143
-rw-r--r--include/sqfs/predef.h7
-rw-r--r--include/sqfs/super.h231
-rw-r--r--include/sqfs/table.h56
-rw-r--r--include/sqfs/xattr.h286
15 files changed, 2033 insertions, 111 deletions
diff --git a/README.md b/README.md
index 8efea3d..a7f7d18 100644
--- a/README.md
+++ b/README.md
@@ -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