aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sqfs/frag_table.h158
-rw-r--r--include/sqfs/predef.h1
-rw-r--r--lib/sqfs/Makemodule.am1
-rw-r--r--lib/sqfs/frag_table.c195
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;
+}