diff options
-rw-r--r-- | include/compress.h | 51 | ||||
-rw-r--r-- | include/frag_reader.h | 45 | ||||
-rw-r--r-- | include/fstree.h | 240 | ||||
-rw-r--r-- | include/id_table.h | 79 | ||||
-rw-r--r-- | include/meta_reader.h | 134 | ||||
-rw-r--r-- | include/meta_writer.h | 80 | ||||
-rw-r--r-- | include/table.h | 21 | ||||
-rw-r--r-- | include/util.h | 40 |
8 files changed, 690 insertions, 0 deletions
diff --git a/include/compress.h b/include/compress.h index ad193e8..5bb689b 100644 --- a/include/compress.h +++ b/include/compress.h @@ -11,15 +11,66 @@ typedef struct compressor_t compressor_t; +/** + * @struct compressor_t + * + * @brief Encapsultes a compressor with a simple interface to compress or + * uncompress/extract blocks of data + */ struct compressor_t { + /** + * @brief Compress or uncompress a chunk of data + * + * @param cmp A pointer to the compressor object + * @param in A pointer to the input buffer + * @param size The number of bytes in the input buffer to process + * @param out A pointer to the output buffer + * @param outsize The number of bytes available in the output buffer + * + * @return On success, the number of bytes written to the output + * buffer, -1 on failure, 0 if the output buffer was too small. + * The compressor also returns 0 if the compressed result ends + * up larger than the original input. + */ ssize_t (*do_block)(compressor_t *cmp, const uint8_t *in, size_t size, uint8_t *out, size_t outsize); + /** + * @brief Destroy a compressor object and free up all memory it uses + * + * @param cmp A pointer to the compressor object + */ void (*destroy)(compressor_t *stream); }; +/** + * @brief Check if a given compressor is available + * + * @memberof compressor_t + * + * This function checks if a given compressor is available, since some + * compressors may not be supported yet, or simply disabled in the + * compile configuration. + * + * @param id A SquashFS compressor id + * + * @return true if the given compressor is available + */ bool compressor_exists(E_SQFS_COMPRESSOR id); +/** + * @brief Create a compressor object + * + * @memberof compressor_t + * + * @param id A SquashFS compressor id + * @param compress true if the resulting object should compress data, false + * if it should actually extract already compressed blocks. + * @param block_size The configured block size for the SquashFS image. May be + * of interest to some compressors. + * + * @return A pointer to a new compressor object or NULL on failure. + */ compressor_t *compressor_create(E_SQFS_COMPRESSOR id, bool compress, size_t block_size); diff --git a/include/frag_reader.h b/include/frag_reader.h index 6495f5b..8a3d4cc 100644 --- a/include/frag_reader.h +++ b/include/frag_reader.h @@ -8,6 +8,11 @@ #include <stdint.h> #include <stddef.h> +/** + * @struct frag_reader_t + * + * @brief A simple interface for accessing fragments in a SquashFS image + */ typedef struct { sqfs_fragment_t *tbl; size_t num_fragments; @@ -20,11 +25,51 @@ typedef struct { uint8_t buffer[]; } frag_reader_t; +/** + * @brief Create a fragment reader + * + * @memberof frag_reader_t + * + * This function internally reads and decodes the fragment table from the + * image and prints error messages to stderr on the way if it fails. + * + * @param super A pointer to the SquashFS super block read from the image + * @param fd A file descriptor of opened the SquashFS image + * @param cmp A pointer to a compressor object to be used for reading meta + * data blocks from the image. + * + * @return A pointer to a new fragment reader or NULL on failure + */ frag_reader_t *frag_reader_create(sqfs_super_t *super, int fd, compressor_t *cmp); +/** + * @brief Destroy a fragment reader and free all memory it uses + * + * @memberof frag_reader_t + * + * @param f A pointer to a fragment reader object + */ void frag_reader_destroy(frag_reader_t *f); +/** + * @brief Read tail-end packed data from a fragment + * + * @memberof frag_reader_t + * + * This function internally takes care of loading and uncompressing the + * fragment block (which is skipped if it has already been loaded earlier). + * It prints error messages to stderr on the way if it fails, including such + * errors as trying to index beyond the fragment table. + * + * @param f A pointer to a fragment reader object + * @param index A fragment index such as stored in an inode + * @param offset A byte offset into the fragment block addressed by the index + * @param buffer A pointer to a destination buffer to copy decoded data to + * @param size The number of bytes to copy into the destination buffer + * + * @return Zero on success, -1 on failure + */ int frag_reader_read(frag_reader_t *f, size_t index, size_t offset, void *buffer, size_t size); diff --git a/include/fstree.h b/include/fstree.h index 5a8e422..f2ed67f 100644 --- a/include/fstree.h +++ b/include/fstree.h @@ -13,46 +13,197 @@ typedef struct file_info_t file_info_t; typedef struct dir_info_t dir_info_t; typedef struct fstree_t fstree_t; +/** + * @struct file_info_t + * + * @brief Additional meta data stored in a @ref tree_node_t for regular files + */ struct file_info_t { char *input_file; + + /** + * @brief Linked list pointer for aggregating fragments + * + * When writing out data blocks, files that don't have a multiple of + * the block size have their tail ends gathered in a fragment block. + * A linked list is used to keep track of which files share the same + * fragment block. + */ file_info_t *frag_next; + + /** + * @brief Total size of the file in bytes + */ uint64_t size; + + /** + * @brief Absolute position of the first data block + */ uint64_t startblock; + + /** + * @brief Fragment index + * + * If the size is not a multiple of the block size, this holds an + * index into the fragment table. + */ uint32_t fragment; + + /** + * @brief Byte offset into the fragment block + * + * If the size is not a multiple of the block size, this holds an + * offset into the fragment block. + */ uint32_t fragment_offset; + + /** + * @brief Stores the compressed file block sizes + * + * For each full data block, stores the compressed size. Bit number + * 24 is set if the block is stored uncompressed. + */ uint32_t blocksizes[]; }; +/** + * @struct dir_info_t + * + * @brief Additional meta data stored in a @ref tree_node_t for directories + */ struct dir_info_t { + /** + * @brief Pointer to the head of the linked list of children + */ tree_node_t *children; + + /** + * @brief Size of the directory + * + * Computed and updated on the fly while writing directory + * meta data to disk. + */ uint64_t size; + + /** + * @brief Start block offset, relative to directory table start + * + * Offset of the compressed meta data block where the directory + * listing is stored in the SquashFS image. + */ uint64_t start_block; + + /** + * @brief Byte offset into the uncompressed meta data block + * + * Points at where in the meta data block the directory listing begins. + */ uint32_t block_offset; + + /** + * @brief Set to true for implicitly generated directories + */ bool created_implicitly; }; +/** + * @struct tree_node_t + * + * @brief A node in a file system tree + */ struct tree_node_t { + /** + * @brief Linked list pointer to the next node in the same directory + */ tree_node_t *next; + + /** + * @brief Pointer to directory node that this node is in + * + * This is NULL only for the root node + */ tree_node_t *parent; + + /** + * @brief Pointer into the payload area where the node name is stored + * + * For the root node, this points to an empty string + */ char *name; + uint32_t uid; uint32_t gid; uint16_t mode; + /** + * @brief SquashFS inode refernce number + * + * This is computed and stored here on the fly when writing inodes + * generated from tree nodes to the SquashFS image. + * + * An inode reference is the 32 bit offset of the compressed meta data + * block, shifted left by 16 and ored with a 13 bit offset into the + * uncompressed meta data block. + */ uint64_t inode_ref; + + /** + * @brief inode number + * + * This is computed and stored here on the fly when writing inodes + * generated from tree nodes to the SquashFS image. + */ uint32_t inode_num; + + /** + * @brief SquashFS inode type used for this tree node + * + * This is computed and stored here on the fly when writing inodes + * generated from tree nodes to the SquashFS image. It can't be + * easily determined in advance since it depends also on the size + * of the node, which means for directories the size of the directory + * entries once written to disk. + * + * All code that actually processes tree nodes should use the mode + * field instead (mode & S_IFMT gives us the node type). It is stored + * here when generating inodes since we need it later on to generate + * directory entries. + */ int type; union { + /** + * @brief Pointer into payload area storing directory meta data + */ dir_info_t *dir; + + /** + * @brief Pointer into payload area storing file meta data + */ file_info_t *file; + + /** + * @brief Pointer into payload area storing symlink target + */ char *slink_target; + + /** + * @brief A device number for device special files + */ uint64_t devno; } data; + /** + * @brief Additional data stored in the tree node + */ uint8_t payload[]; }; +/** + * @struct fstree_t + * + * @brief Encapsulates a file system tree + */ struct fstree_t { uint32_t default_uid; uint32_t default_gid; @@ -63,21 +214,110 @@ struct fstree_t { tree_node_t *root; }; +/** + * @brief Initialize an fstree object + * + * @memberof fstree_t + * + * Initializing means copying over the default values and creating a root node. + * On error, an error message is written to stderr. + * + * @param fs A pointer to an uninitialized fstree object + * @param block_size The data block size for regular files + * @param mtime Default modification time stamp to use on all nodes + * @param default_mode Default permission bits to use on implicitly created + * directories. + * @param default_uid Default UID to set on implicitly created directories. + * @param default_gid Default GID to set on implicitly created directories. + * + * @return Zero on success, -1 on failure + */ int fstree_init(fstree_t *fs, size_t block_size, uint32_t mtime, uint16_t default_mode, uint32_t default_uid, uint32_t default_gid); +/** + * @brief Clean up an fstree object and free all memory it uses + * + * @memberof fstree_t + * + * This function also recursively frees all tree nodes. + * + * @param fs A pointer to an fstree object + */ void fstree_cleanup(fstree_t *fs); +/** + * @brief Add a generic node to an fstree object + * + * @memberof fstree_t + * + * The new node is inserted by path. If some components of the path don't + * exist, they are created as directories with default permissions, like + * mkdir -p would, and marked as implcitily created. A subsequent call that + * tries to create an existing tree node will fail, except if the target + * is an implicitly created directory node and the call tries to create it + * as a directory. This will simply overwrite the permissions and ownership. + * The implicitly created flag is then cleared and subsequent attempts to + * create this directory again will also fail. + * + * This function does not print anything to stderr, instead it sets an + * appropriate errno value. + * + * @param fs A pointer to an fstree object + * @param path The path of the new object to insert + * @param mode Specifies both access permission and what kind of node + * to create + * @param uid The user id that owns the new node + * @param gid The group id that owns the new node + * @param extra_len An additional number of bytes to allocate for payload data + * + * @return A pointer to the new tree node or NULL on failure + */ tree_node_t *fstree_add(fstree_t *fs, const char *path, uint16_t mode, uint32_t uid, uint32_t gid, size_t extra_len); +/** + * @brief A wrappter around @ref fstree_add for regular files + * + * @memberof fstree_t + * + * This function internally computes the number of extra payload bytes + * requiered and sets up the payload pointers propperly, as they are + * different than for other types of nodes. + * + * @return A pointer to the new tree node or NULL on failure + */ tree_node_t *fstree_add_file(fstree_t *fs, const char *path, uint16_t mode, uint32_t uid, uint32_t gid, uint64_t filesz, const char *input); +/** + * @brief Load an fstree from a text file describing it + * + * @memberof fstree_t + * + * This function parses the file format accepted by gensquashfs and restores + * a file system tree from it. + * + * On failure, an error report with filename and line number is written + * to stderr. + * + * @param fs A pointer to an fstree object that is already initialized + * prior to calling this function. + * @param filename The path to the input file to process. + * + * @return Zero on success, -1 on failure. + */ int fstree_from_file(fstree_t *fs, const char *filename); +/** + * @brief Lexicographically sort all directory contents + * + * @memberof fstree_t + * + * @param fs A pointer to an fstree object + */ void fstree_sort(fstree_t *fs); #endif /* FSTREE_H */ diff --git a/include/id_table.h b/include/id_table.h index 600eb64..788519c 100644 --- a/include/id_table.h +++ b/include/id_table.h @@ -7,21 +7,100 @@ #include "compress.h" +/** + * @struct id_table_t + * + * @brief Encapsulates the ID table used by SquashFS + */ typedef struct { + /** + * @brief Array of unique 32 bit IDs + */ uint32_t *ids; + + /** + * @brief Number of 32 bit IDs stored in the array + */ size_t num_ids; + + /** + * @brief Actual size of the array, i.e. maximum available + */ size_t max_ids; } id_table_t; +/** + * @brief Initialize an ID table + * + * @memberof id_table_t + * + * @note This function internally prints error message to stderr on failure + * + * @param tbl A pointer to an uninitialized ID table + * + * @return Zero on success, -1 on failure + */ int id_table_init(id_table_t *tbl); +/** + * @brief Cleanup and free an ID table + * + * @memberof id_table_t + * + * @param tbl A pointer to an ID table + */ void id_table_cleanup(id_table_t *tbl); +/** + * @brief Resolve a 32 bit to a 16 bit table index + * + * @memberof id_table_t + * + * @note This function internally prints error message to stderr on failure + * + * @param tbl A pointer to an ID table + * @param id A 32 bit ID to resolve + * @param out Returns the 16 bit table index + * + * @return Zero on success, -1 on failure + */ int id_table_id_to_index(id_table_t *tbl, uint32_t id, uint16_t *out); +/** + * @brief Write an ID table to a SquashFS image + * + * @memberof id_table_t + * + * @note This function internally prints error message to stderr on failure + * + * @param tbl A pointer to an ID table + * @param outfd A file descriptor to write the data to + * @param super A pointer to the SquashFS super block to store the ID table + * offset in + * @param cmp A pointer to the compressor to use for compressing the ID + * table meta data blocks + * + * @return Zero on success, -1 on failure + */ int id_table_write(id_table_t *tbl, int outfd, sqfs_super_t *super, compressor_t *cmp); +/** + * @brief Read an ID table from a SquashFS image + * + * @memberof id_table_t + * + * @note This function internally prints error message to stderr on failure + * + * @param tbl A pointer to an ID table + * @param fd A file descriptor to read the data from + * @param super A pointer to the SquashFS super block that tells us where + * the ID table is stored + * @param cmp A pointer to the compressor to use for extracting the ID + * table meta data blocks + * + * @return Zero on success, -1 on failure + */ int id_table_read(id_table_t *tbl, int fd, sqfs_super_t *super, compressor_t *cmp); diff --git a/include/meta_reader.h b/include/meta_reader.h index 3085383..78c0db3 100644 --- a/include/meta_reader.h +++ b/include/meta_reader.h @@ -5,32 +5,166 @@ #include "compress.h" #include "squashfs.h" +/** + * @struct meta_reader_t + * + * @brief Abstracts away I/O on SquashFS meta data + */ typedef struct { + /** + * @brief The location of the current block in the file + */ uint64_t block_offset; + + /** + * @brief The location of the next block after the current one + */ uint64_t next_block; + + /** + * @brief A byte offset into the uncompressed data of the current block + */ size_t offset; + + /** + * @brief The underlying file descriptor to read from + */ int fd; + + /** + * @brief A pointer to the compressor to use for extracting data + */ compressor_t *cmp; + + /** + * @brief The raw data read from the input file + */ uint8_t data[SQFS_META_BLOCK_SIZE]; + + /** + * @brief The uncompressed data read from the input file + */ uint8_t scratch[SQFS_META_BLOCK_SIZE]; } meta_reader_t; +/** + * @brief Create a meta data reader + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * @param fd The underlying file descriptor to read from + * @param cmp A pointer to a compressor to use for extracting the data + * + * @return A pointer to a meta data reader, NULL on failure + */ meta_reader_t *meta_reader_create(int fd, compressor_t *cmp); +/** + * @brief Destroy a meta data reader and free all memory used by it + * + * @memberof meta_reader_t + * + * @param m A pointer to a meta data reader + */ void meta_reader_destroy(meta_reader_t *m); +/** + * @brief Seek to a specific meta data block and offset + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * @param m A pointer to a meta data reader + * @param block_start The absolute position of the 16 bit header right before + * the compressed data block + * @param offset A byte offset into the data block + * + * @return Zero on success, -1 on failure + */ int meta_reader_seek(meta_reader_t *m, uint64_t block_start, size_t offset); +/** + * @brief Read a chunk of data from a meta data block + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * If the end of the block is reached, this function transparently tries to + * interpret the data after the current block as a further meta data block, + * i.e. it can transparently read across multiple meta data blocks. + * + * @param m A pointer to a meta data reader + * @param data A pointer to a memory block to write the data to + * @param size The number of bytes to read + * + * @return Zero on success, -1 on failure + */ int meta_reader_read(meta_reader_t *m, void *data, size_t size); +/** + * @brief Read an inode from a meta data block + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * This function is a conveniance wrapper around @ref meta_reader_read that + * reads and decodes an entire SquashFS inode. It first reads the common inode + * header, interprets it and reads the additional, type dependend data. + * + * @param ir A pointer to a meta data reader + * @param super A pointer to the SquashFS super block + * @param block_start A byte offset relative to the inode table start where + * the meta data containing the inode starts + * @param offset A byte offset into the uncompressed meta data block + * where the inode is stored + * + * @return A pointer to the decoded inode or NULL on failure. Can be freed + * with a single free call. + */ sqfs_inode_generic_t *meta_reader_read_inode(meta_reader_t *ir, sqfs_super_t *super, uint64_t block_start, size_t offset); +/** + * @brief Read a directory header from a meta data block + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * This function is a conveniance wrapper around @ref meta_reader_read that + * reads and decodes a SquashFS directory header. + * + * @param m A pointer to a meta data reader + * @param hdr A pointer to a directory header structure to write the decoded + * data to + * + * @return Zero on success, -1 on failure + */ int meta_reader_read_dir_header(meta_reader_t *m, sqfs_dir_header_t *hdr); +/** + * @brief Read a directory entry from a meta data block + * + * @memberof meta_reader_t + * + * @note This function internally prints error message to stderr on failure + * + * This function is a conveniance wrapper around @ref meta_reader_read that + * reads and decodes a SquashFS directory entry. + * + * @param m A pointer to a meta data reader + * + * @return A pointer to a directory entry or NULL on failure. Can be freed + * with a single free call. + */ sqfs_dir_entry_t *meta_reader_read_dir_ent(meta_reader_t *m); #endif /* META_READER_H */ diff --git a/include/meta_writer.h b/include/meta_writer.h index 7f1be9a..9dcc22d 100644 --- a/include/meta_writer.h +++ b/include/meta_writer.h @@ -5,21 +5,101 @@ #include "compress.h" #include "squashfs.h" +/** + * @struct meta_writer_t + * + * @brief Abstracts away I/O on SquashFS meta data + */ typedef struct { + /** + * @brief A byte offset into the uncompressed data of the current block + */ size_t offset; + + /** + * @brief The location of the current block in the file + */ size_t block_offset; + + /** + * @brief The underlying file descriptor to write to + */ int outfd; + + /** + * @brief A pointer to the compressor to use for compressing the data + */ compressor_t *cmp; + + /** + * @brief The raw data chunk that data is appended to + */ uint8_t data[SQFS_META_BLOCK_SIZE + 2]; + + /** + * @brief Scratch buffer for compressing data + */ uint8_t scratch[SQFS_META_BLOCK_SIZE + 2]; } meta_writer_t; +/** + * @brief Create a meta data writer + * + * @memberof meta_writer_t + * + * @note This function internally prints error message to stderr on failure + * + * @param fd The underlying file descriptor to write from + * @param cmp A pointer to a compressor to use for compressing the data + * + * @return A pointer to a meta data writer, NULL on failure + */ meta_writer_t *meta_writer_create(int fd, compressor_t *cmp); +/** + * @brief Destroy a meta data writer and free all memory used by it + * + * @memberof meta_writer_t + * + * @param m A pointer to a meta data reader + */ void meta_writer_destroy(meta_writer_t *m); +/** + * @brief Flush the currently unfinished meta data block to disk + * + * @memberof meta_writer_t + * + * @note This function internally prints error message to stderr on failure + * + * If data has been collected in the block buffer but it is not complete yet, + * this function tries to compress it and write it out anyway and reset the + * internal counters. + * + * @param m A pointer to a meta data reader + * + * @return Zero on success, -1 on failure + */ int meta_writer_flush(meta_writer_t *m); +/** + * @brief Append data to the current meta data block + * + * @memberof meta_writer_t + * + * @note This function internally prints error message to stderr on failure + * + * This function appends the input data to an internal meta data buffer. If + * the internal buffer is full, it is compressed and written to disk using + * @ref meta_writer flush, i.e. the function allows for transparent writing + * across meta data blocks. + * + * @param m A pointer to a meta data reader + * @param data A pointer to the data block to append + * @param size The number of bytes to read from the data blob + * + * @return Zero on success, -1 on failure + */ int meta_writer_append(meta_writer_t *m, const void *data, size_t size); #endif /* META_WRITER_H */ diff --git a/include/table.h b/include/table.h index 3eb5655..ed2cb4a 100644 --- a/include/table.h +++ b/include/table.h @@ -8,6 +8,27 @@ #include <stdint.h> #include <stddef.h> +/** + * @brief Convenience function for writing meta data to a SquashFS image + * + * @note This function internally prints error message to stderr on failure + * + * This function internally creates a meta data writer and writes 'count' + * blocks of data from 'data' to it, each 'entsize' bytes in size. For each + * meta data block, it remembers the 64 bit start address, writes out all + * addresses to the and returns the location where the address list starts. + * For instance, the fragment table and ID table are stored in this format. + * + * @param outfd The file descriptor to write to + * @param super A pointer to the SquashFS super block + * @param data A pointer to the data to write out + * @param entsize The size of each data record + * @param count The number of data records to write out + * @param startblock Returns the location of the lookup table + * @param cmp A pointer to the compressor to use for the meta data writer + * + * @return Zero on success, -1 on failure + */ int sqfs_write_table(int outfd, sqfs_super_t *super, const void *data, size_t entsize, size_t count, uint64_t *startblock, compressor_t *cmp); diff --git a/include/util.h b/include/util.h index 1ec551c..4857539 100644 --- a/include/util.h +++ b/include/util.h @@ -4,14 +4,54 @@ #include <sys/types.h> +/** + * @brief Turn a file path to a more usefull form + * + * Removes all preceeding and trailing slashes, shortens all sequences of + * slashes to a single slash and returns failure state if one of the path + * components is '..' or '.'. + * + * @param filename A pointer to the path to work on + * + * @return Zero on success, -1 on failure + */ int canonicalize_name(char *filename); +/** + * @brief Write data to a file + * + * This is a wrapper around the Unix write() system call. It retries the write + * if it is interrupted by a signal or only part of the data was written. + */ ssize_t write_retry(int fd, void *data, size_t size); +/** + * @brief Read data from a file + * + * This is a wrapper around the Unix read() system call. It retries the read + * if it is interrupted by a signal or less than the desired size was read. + */ ssize_t read_retry(int fd, void *buffer, size_t size); +/** + * @brief A common implementation of the '--version' command line argument + * + * Prints out version information. The program name is extracted from the + * BSD style __progname global variable. + */ void print_version(void); +/** + * @brief Create a directory and all its parents + * + * This is a wrapper around mkdir() that behaves like 'mkdir -p'. It tries to + * create every component of the given path and treats already existing entries + * as success. + * + * @param path A path to create + * + * @return Zero on success, -1 on failure + */ int mkdir_p(const char *path); #endif /* UTIL_H */ |