diff options
author | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2020-01-23 22:59:59 +0100 |
---|---|---|
committer | David Oberhollenzer <david.oberhollenzer@sigma-star.at> | 2020-01-24 16:15:47 +0100 |
commit | 1d15ee609baf00459571b8d549bdd467dffa2b02 (patch) | |
tree | d1838cef04129353fd19152554aa927f18e818de | |
parent | b4c822960aaf5b4349895cc480e959b502f6855e (diff) |
Add a fragment table primitive to libsquashfs
Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
-rw-r--r-- | include/sqfs/frag_table.h | 158 | ||||
-rw-r--r-- | include/sqfs/predef.h | 1 | ||||
-rw-r--r-- | lib/sqfs/Makemodule.am | 1 | ||||
-rw-r--r-- | lib/sqfs/frag_table.c | 195 |
4 files changed, 355 insertions, 0 deletions
diff --git a/include/sqfs/frag_table.h b/include/sqfs/frag_table.h new file mode 100644 index 0000000..12ddd40 --- /dev/null +++ b/include/sqfs/frag_table.h @@ -0,0 +1,158 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frag_table.h - This file is part of libsquashfs + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#ifndef SQFS_FRAG_TABLE_H +#define SQFS_FRAG_TABLE_H + +#include "sqfs/predef.h" + +/** + * @file frag_table.h + * + * @brief Contains declarations for the @ref sqfs_frag_table_t data structure. + */ + +/** + * @struct sqfs_frag_table_t + * + * @brief Abstracts reading, writing and management of the fragment table. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Create a fragment table. + * + * @memberof sqfs_frag_table_t + * + * @param flags Currently must be set to 0, otherwise this function fails. + * + * @return A pointer to a new fragment table object on success, NULL on failure. + */ +SQFS_API sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags); + +/** + * @brief Destroy a fragment table and release all associated memory. + * + * @memberof sqfs_frag_table_t + */ +SQFS_API void sqfs_frag_table_destroy(sqfs_frag_table_t *tbl); + +/** + * @brief Load a fragment table from a SquashFS image. + * + * @memberof sqfs_frag_table_t + * + * @param tbl The fragment table object to load into. + * @param file The file to load the table from. + * @param super The super block that describes the location + * and size of the table. + * @param cmp The compressor to use for uncompressing the table. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ +SQFS_API int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, + const sqfs_super_t *super, + sqfs_compressor_t *cmp); + +/** + * @brief Write a fragment table to a SquashFS image. + * + * @memberof sqfs_frag_table_t + * + * The data from the table is compressed and appended to the given file. The + * information about the tables size and location are stored in the given super + * block. Super block flags are updated as well. + * + * @param tbl The fragment table object to store on disk. + * @param file The file to append the table to. + * @param super The super block that should be updated with the location + * and size of the table. + * @param cmp The compressor to use for compressing the table. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure. + */ +SQFS_API int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp); + +/** + * @brief Resolve a fragment block index to its description. + * + * @memberof sqfs_frag_table_t + * + * @param tbl A pointer to the fragmen table object. + * @param index The index into the table. + * @param out Returns the data from the table on success. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure (e.g. index is + * out of bounds). + */ +SQFS_API int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_fragment_t *out); + +/** + * @brief Append a table entry to a fragment table. + * + * @memberof sqfs_frag_table_t + * + * @param tbl A pointer to the fragmen table object. + * @param location The absolute on-disk location of the new fragment block. + * @param out The size of the block. Has bit 24 set if compressed. + * @param index If not NULL, returns the allocated table index. + * + * @return Zero on success, an @ref E_SQFS_ERROR on failure (e.g. allocation + * failure). + */ +SQFS_API int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, + sqfs_u32 size, sqfs_u32 *index); + +/** + * @brief Modify an existing entry in a fragment table. + * + * @memberof sqfs_frag_table_t + * + * @param tbl A pointer to the fragmen table object. + * @param index The fragment table index. + * @param location The absolute on-disk location of the fragment block. + * @param out The size of the block. Has bit 24 set if compressed. + * + * @return Zero on success, an @ref E_SQFS_ERROR on + * failure (e.g. out of bounds). + */ +SQFS_API int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_u64 location, sqfs_u32 size); + +/** + * @brief Get the number of entries stored in a fragment table. + * + * @memberof sqfs_frag_table_t + * + * @param tbl A pointer to the fragmen table object. + * + * @return The number of entries currently stored in the table. + */ +SQFS_API size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl); + +#ifdef __cplusplus +} +#endif + +#endif /* SQFS_FRAG_TABLE_H */ diff --git a/include/sqfs/predef.h b/include/sqfs/predef.h index ac2def9..234b015 100644 --- a/include/sqfs/predef.h +++ b/include/sqfs/predef.h @@ -82,6 +82,7 @@ typedef struct sqfs_tree_node_t sqfs_tree_node_t; typedef struct sqfs_data_reader_t sqfs_data_reader_t; typedef struct sqfs_block_hooks_t sqfs_block_hooks_t; typedef struct sqfs_xattr_writer_t sqfs_xattr_writer_t; +typedef struct sqfs_frag_table_t sqfs_frag_table_t; typedef struct sqfs_fragment_t sqfs_fragment_t; typedef struct sqfs_dir_header_t sqfs_dir_header_t; diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am index e3ed94a..7d44e59 100644 --- a/lib/sqfs/Makemodule.am +++ b/lib/sqfs/Makemodule.am @@ -25,6 +25,7 @@ libsquashfs_la_SOURCES += lib/sqfs/data_writer/common.c libsquashfs_la_SOURCES += lib/sqfs/data_writer/fileapi.c libsquashfs_la_SOURCES += lib/sqfs/str_table.c lib/sqfs/str_table.h libsquashfs_la_SOURCES += lib/sqfs/alloc.c lib/sqfs/util.h +libsquashfs_la_SOURCES += lib/sqfs/frag_table.c include/sqfs/frag_table.h libsquashfs_la_CPPFLAGS = $(AM_CPPFLAGS) libsquashfs_la_LDFLAGS = $(AM_LDFLAGS) libsquashfs_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) diff --git a/lib/sqfs/frag_table.c b/lib/sqfs/frag_table.c new file mode 100644 index 0000000..3373a3c --- /dev/null +++ b/lib/sqfs/frag_table.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frag_table.c + * + * Copyright (C) 2019 David Oberhollenzer <goliath@infraroot.at> + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/frag_table.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "compat.h" + +#include <stdlib.h> +#include <string.h> + +struct sqfs_frag_table_t { + size_t capacity; + size_t used; + sqfs_fragment_t *table; +}; + +sqfs_frag_table_t *sqfs_frag_table_create(sqfs_u32 flags) +{ + sqfs_frag_table_t *tbl; + + if (flags != 0) + return NULL; + + tbl = calloc(1, sizeof(*tbl)); + if (tbl == NULL) + return NULL; + + return tbl; +} + +void sqfs_frag_table_destroy(sqfs_frag_table_t *tbl) +{ + free(tbl->table); + free(tbl); +} + +int sqfs_frag_table_read(sqfs_frag_table_t *tbl, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 location, lower, upper; + void *raw = NULL; + size_t size; + int err; + + free(tbl->table); + tbl->table = NULL; + tbl->capacity = 0; + tbl->used = 0; + + if (super->flags & SQFS_FLAG_NO_FRAGMENTS) + return 0; + + if (super->fragment_table_start == 0xFFFFFFFFFFFFFFFFUL) + return 0; + + if (super->fragment_entry_count == 0) + return 0; + + if (super->fragment_table_start >= super->bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + /* location must be after inode & directory table, + but before the ID table */ + if (super->fragment_table_start < super->directory_table_start) + return SQFS_ERROR_CORRUPTED; + + if (super->fragment_table_start >= super->id_table_start) + return SQFS_ERROR_CORRUPTED; + + location = super->fragment_table_start; + lower = super->directory_table_start; + upper = super->id_table_start; + + if (super->export_table_start < super->id_table_start) + upper = super->export_table_start; + + if (SZ_MUL_OV(super->fragment_entry_count, sizeof(sqfs_fragment_t), + &size)) { + return SQFS_ERROR_OVERFLOW; + } + + err = sqfs_read_table(file, cmp, size, location, lower, upper, &raw); + if (err) { + free(raw); + return err; + } + + tbl->table = raw; + tbl->capacity = super->fragment_entry_count; + tbl->used = super->fragment_entry_count; + return 0; +} + +int sqfs_frag_table_write(sqfs_frag_table_t *tbl, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + size_t i; + int err; + + if (tbl->used == 0) { + super->fragment_table_start = 0xFFFFFFFFFFFFFFFF; + super->flags |= SQFS_FLAG_NO_FRAGMENTS; + super->flags &= ~SQFS_FLAG_ALWAYS_FRAGMENTS; + super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + return 0; + } + + err = sqfs_write_table(file, cmp, tbl->table, + sizeof(tbl->table[0]) * tbl->used, + &super->fragment_table_start); + if (err) + return err; + + super->fragment_entry_count = tbl->used; + super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; + super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; + super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + + for (i = 0; i < tbl->used; ++i) { + if (SQFS_IS_BLOCK_COMPRESSED(le32toh(tbl->table[i].size))) { + super->flags &= ~SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + break; + } + } + + return 0; +} + +int sqfs_frag_table_lookup(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_fragment_t *out) +{ + if (index >= tbl->used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + out->start_offset = le64toh(tbl->table[index].start_offset); + out->size = le32toh(tbl->table[index].size); + out->pad0 = le32toh(tbl->table[index].pad0); + return 0; +} + +int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, + sqfs_u32 size, sqfs_u32 *index) +{ + size_t new_sz, total; + void *new; + + if (tbl->used == tbl->capacity) { + new_sz = tbl->capacity ? tbl->capacity * 2 : 128; + + if (SZ_MUL_OV(new_sz, sizeof(tbl->table[0]), &total)) + return SQFS_ERROR_OVERFLOW; + + new = realloc(tbl->table, total); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + tbl->capacity = new_sz; + tbl->table = new; + } + + if (index != NULL) + *index = tbl->used; + + memset(tbl->table + tbl->used, 0, sizeof(tbl->table[0])); + tbl->table[tbl->used].start_offset = htole64(location); + tbl->table[tbl->used].size = htole32(size); + + tbl->used += 1; + return 0; +} + +int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_u64 location, sqfs_u32 size) +{ + if (index >= tbl->used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + tbl->table[index].start_offset = htole64(location); + tbl->table[index].size = htole32(size); + return 0; +} + +size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) +{ + return tbl->used; +} |