From 5e2298a3a3f00e6f55ceda2b1c35192d67c9feb4 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Sun, 15 Dec 2019 15:52:01 +0100 Subject: Add ability to sqfs_dir_writer to create an export table If the dir writer is used to create the directory table, it neccessarily sees every single inode number and coresponding location for all inodes that are referenced by the filesystem tree. This means it can easily collect that information internally to create an export table later on. Signed-off-by: David Oberhollenzer --- include/sqfs/dir_writer.h | 66 ++++++++++++++++++++++++- lib/common/serialize_fstree.c | 2 +- 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; +} -- cgit v1.2.3