diff options
-rw-r--r-- | include/sqfs/dir_writer.h | 66 | ||||
-rw-r--r-- | lib/common/serialize_fstree.c | 2 | ||||
-rw-r--r-- | lib/sqfs/dir_writer.c | 111 |
3 files changed, 172 insertions, 7 deletions
diff --git a/include/sqfs/dir_writer.h b/include/sqfs/dir_writer.h index 81a651c..c390050 100644 --- a/include/sqfs/dir_writer.h +++ b/include/sqfs/dir_writer.h @@ -58,6 +58,30 @@ * writer used for inodes. */ +/** + * @enum SQFS_DIR_WRITER_CREATE_FLAGS + * + * @brief Flags that can be set for @ref sqfs_dir_writer_create + */ +typedef enum { + /** + * @brief Record all inode locations to create an export table. + * + * For NFS export support, SquashFS needs an extra table that maps + * inode numbers directly to on-disk locations. + * + * Since the @ref sqfs_dir_writer_t "sees" all inode numbers and + * coresponding locations and easily create such a table. + * + * If this flag is set for @ref sqfs_dir_writer_create, the result + * directory wrter collects such a table which it can then write to + * disk using @ref sqfs_dir_writer_write_export_table. + */ + SQFS_DIR_WRITER_CREATE_EXPORT_TABLE = 0x01, + + SQFS_DIR_WRITER_CREATE_ALL_FLAGS = 0x01 +} SQFS_DIR_WRITER_CREATE_FLAGS; + #ifdef __cplusplus extern "C" { #endif @@ -69,11 +93,13 @@ extern "C" { * * @param dm A pointer to a meta data writer that the generated directory * entries should be written to. + * @param flags A combination of @ref SQFS_DIR_WRITER_CREATE_FLAGS. * * @return A pointer to a directory writer on success, NULL on - * allocation failure. + * allocation failure or if flags has unknown flags set. */ -SQFS_API sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm); +SQFS_API sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, + sqfs_u32 flags); /** * @brief Destroy a directory writer and free all its memory. @@ -217,6 +243,42 @@ SQFS_API sqfs_inode_generic_t *sqfs_dir_writer_create_inode(const sqfs_dir_writer_t *writer, size_t hlinks, sqfs_u32 xattr, sqfs_u32 parent_ino); +/** + * @brief Write an export table to a SquashFS image. + * + * @memberof sqfs_dir_writer_t + * + * If the @ref sqfs_dir_writer_t was created with the + * @ref SQFS_DIR_WRITER_CREATE_EXPORT_TABLE flag set, it has an internal table + * that maps all inode numbers to inode references. After writing the fragment + * table, this function can be used to write this inode mapping table for NFS + * export support. + * + * It is safe to call this function if the writer has been created without the + * flag. In this case, it is simply a noop. + * + * In theory, the writer "sees" the entire directory tree and for each entry, + * the inode number and on-disk location, so it can build this table. The only + * inode it never sees is the root inode, so that information has to be passed + * to this function to add it to the table just before writing it to disk. + * + * @param writer A pointer to a directory writer object. + * @param file The ouput file to write the table to. + * @param cmp The compressor to use to compress the table. + * @param root_inode_num The inode number of the root inode. + * @param root_inode_ref An inode reference for the root inode. + * @param super A pointer to the super block. Location of the export table and + * the exportable flag are set. + * + * @return Zero on success, a @ref E_SQFS_ERROR value on failure. + */ +SQFS_API int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, + sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u32 root_inode_num, + sqfs_u64 root_inode_ref, + sqfs_super_t *super); + #ifdef __cplusplus } #endif diff --git a/lib/common/serialize_fstree.c b/lib/common/serialize_fstree.c index 3cfcb50..457c485 100644 --- a/lib/common/serialize_fstree.c +++ b/lib/common/serialize_fstree.c @@ -168,7 +168,7 @@ int sqfs_serialize_fstree(const char *filename, sqfs_file_t *file, goto out_im; } - dirwr = sqfs_dir_writer_create(dm); + dirwr = sqfs_dir_writer_create(dm, 0); if (dirwr == NULL) { ret = SQFS_ERROR_ALLOC; goto out_dm; diff --git a/lib/sqfs/dir_writer.c b/lib/sqfs/dir_writer.c index bdd5dd1..78256a1 100644 --- a/lib/sqfs/dir_writer.c +++ b/lib/sqfs/dir_writer.c @@ -9,6 +9,8 @@ #include "sqfs/meta_writer.h" #include "sqfs/dir_writer.h" +#include "sqfs/super.h" +#include "sqfs/table.h" #include "sqfs/inode.h" #include "sqfs/error.h" #include "sqfs/block.h" @@ -45,6 +47,10 @@ struct sqfs_dir_writer_t { size_t dir_size; size_t ent_count; sqfs_meta_writer_t *dm; + + sqfs_u64 *export_tbl; + size_t export_tbl_max; + size_t export_tbl_count; }; static int get_type(sqfs_u16 mode) @@ -86,13 +92,76 @@ static void writer_reset(sqfs_dir_writer_t *writer) writer->ent_count = 0; } -sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm) +static int add_export_table_entry(sqfs_dir_writer_t *writer, + sqfs_u32 inum, sqfs_u64 iref) +{ + size_t i, new_max; + sqfs_u64 *new; + + if (writer->export_tbl == NULL) + return 0; + + if (inum < 1) + return SQFS_ERROR_ARG_INVALID; + + new_max = writer->export_tbl_max; + + while ((inum - 1) >= new_max) { + if (SZ_MUL_OV(new_max, 2, &new_max)) + return SQFS_ERROR_ALLOC; + } + + if (new_max > writer->export_tbl_max) { + if (SZ_MUL_OV(new_max, sizeof(writer->export_tbl[0]), &new_max)) + return SQFS_ERROR_ALLOC; + + new = realloc(writer->export_tbl, new_max); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + new_max /= sizeof(writer->export_tbl[0]); + + for (i = writer->export_tbl_max; i < new_max; ++i) + new[i] = 0xFFFFFFFFFFFFFFFFUL; + + writer->export_tbl = new; + writer->export_tbl_max = new_max; + } + + writer->export_tbl[inum - 1] = iref; + + if ((inum - 1) >= writer->export_tbl_count) + writer->export_tbl_count = inum; + + return 0; +} + +sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, + sqfs_u32 flags) { - sqfs_dir_writer_t *writer = calloc(1, sizeof(*writer)); + sqfs_dir_writer_t *writer; + if (flags & ~SQFS_DIR_WRITER_CREATE_ALL_FLAGS) + return NULL; + + writer = calloc(1, sizeof(*writer)); if (writer == NULL) return NULL; + if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { + writer->export_tbl_max = 512; + + writer->export_tbl = calloc(sizeof(writer->export_tbl[0]), + writer->export_tbl_max); + if (writer->export_tbl == NULL) { + free(writer); + return NULL; + } + + memset(writer->export_tbl, 0xFF, + sizeof(writer->export_tbl[0]) * writer->export_tbl_max); + } + writer->dm = dm; return writer; } @@ -100,6 +169,7 @@ sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm) void sqfs_dir_writer_destroy(sqfs_dir_writer_t *writer) { writer_reset(writer); + free(writer->export_tbl); free(writer); } @@ -123,15 +193,19 @@ int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, sqfs_u16 mode) { dir_entry_t *ent; - int type; + int type, err; type = get_type(mode); if (type < 0) return type; - if (name[0] == '\0') + if (name[0] == '\0' || inode_num < 1) return SQFS_ERROR_ARG_INVALID; + err = add_export_table_entry(writer, inode_num, inode_ref); + if (err) + return err; + ent = alloc_flex(sizeof(*ent), 1, strlen(name)); if (ent == NULL) return SQFS_ERROR_ALLOC; @@ -358,3 +432,32 @@ sqfs_inode_generic_t return inode; } + +int sqfs_dir_writer_write_export_table(sqfs_dir_writer_t *writer, + sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u32 root_inode_num, + sqfs_u64 root_inode_ref, + sqfs_super_t *super) +{ + sqfs_u64 start; + size_t size; + int ret; + + ret = add_export_table_entry(writer, root_inode_num, root_inode_ref); + if (ret) + return 0; + + if (writer->export_tbl_count == 0) + return 0; + + size = sizeof(writer->export_tbl[0]) * writer->export_tbl_count; + + ret = sqfs_write_table(file, cmp, writer->export_tbl, size, &start); + if (ret) + return ret; + + super->export_table_start = start; + super->flags |= SQFS_FLAG_EXPORTABLE; + return 0; +} |