From cdccc69c62579b0c13b35fad0728079652b8f3c9 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 31 Jan 2023 11:21:30 +0100 Subject: Move library source into src sub-directory Signed-off-by: David Oberhollenzer --- lib/sqfs/Makemodule.am | 81 +++-- lib/sqfs/block_processor/backend.c | 335 ------------------ lib/sqfs/block_processor/block_processor.c | 358 ------------------- lib/sqfs/block_processor/frontend.c | 243 ------------- lib/sqfs/block_processor/internal.h | 115 ------- lib/sqfs/block_writer.c | 238 ------------- lib/sqfs/comp/compressor.c | 218 ------------ lib/sqfs/comp/gzip.c | 307 ----------------- lib/sqfs/comp/internal.h | 46 --- lib/sqfs/comp/lz4.c | 172 --------- lib/sqfs/comp/lzma.c | 281 --------------- lib/sqfs/comp/xz.c | 324 ----------------- lib/sqfs/comp/zstd.c | 172 --------- lib/sqfs/data_reader.c | 374 -------------------- lib/sqfs/dir_reader/dir_reader.c | 366 -------------------- lib/sqfs/dir_reader/get_path.c | 81 ----- lib/sqfs/dir_reader/internal.h | 52 --- lib/sqfs/dir_reader/read_tree.c | 288 ---------------- lib/sqfs/dir_writer.c | 460 ------------------------- lib/sqfs/frag_table.c | 204 ----------- lib/sqfs/id_table.c | 162 --------- lib/sqfs/inode.c | 380 -------------------- lib/sqfs/meta_reader.c | 191 ---------- lib/sqfs/meta_writer.c | 215 ------------ lib/sqfs/misc.c | 17 - lib/sqfs/read_inode.c | 424 ----------------------- lib/sqfs/read_super.c | 83 ----- lib/sqfs/read_table.c | 91 ----- lib/sqfs/readdir.c | 161 --------- lib/sqfs/src/block_processor/backend.c | 335 ++++++++++++++++++ lib/sqfs/src/block_processor/block_processor.c | 358 +++++++++++++++++++ lib/sqfs/src/block_processor/frontend.c | 243 +++++++++++++ lib/sqfs/src/block_processor/internal.h | 115 +++++++ lib/sqfs/src/block_writer.c | 238 +++++++++++++ lib/sqfs/src/comp/compressor.c | 218 ++++++++++++ lib/sqfs/src/comp/gzip.c | 307 +++++++++++++++++ lib/sqfs/src/comp/internal.h | 46 +++ lib/sqfs/src/comp/lz4.c | 172 +++++++++ lib/sqfs/src/comp/lzma.c | 281 +++++++++++++++ lib/sqfs/src/comp/xz.c | 324 +++++++++++++++++ lib/sqfs/src/comp/zstd.c | 172 +++++++++ lib/sqfs/src/data_reader.c | 374 ++++++++++++++++++++ lib/sqfs/src/dir_reader/dir_reader.c | 366 ++++++++++++++++++++ lib/sqfs/src/dir_reader/get_path.c | 81 +++++ lib/sqfs/src/dir_reader/internal.h | 52 +++ lib/sqfs/src/dir_reader/read_tree.c | 288 ++++++++++++++++ lib/sqfs/src/dir_writer.c | 460 +++++++++++++++++++++++++ lib/sqfs/src/frag_table.c | 204 +++++++++++ lib/sqfs/src/id_table.c | 162 +++++++++ lib/sqfs/src/inode.c | 380 ++++++++++++++++++++ lib/sqfs/src/meta_reader.c | 191 ++++++++++ lib/sqfs/src/meta_writer.c | 215 ++++++++++++ lib/sqfs/src/misc.c | 17 + lib/sqfs/src/read_inode.c | 424 +++++++++++++++++++++++ lib/sqfs/src/read_super.c | 83 +++++ lib/sqfs/src/read_table.c | 91 +++++ lib/sqfs/src/readdir.c | 161 +++++++++ lib/sqfs/src/super.c | 50 +++ lib/sqfs/src/unix/io_file.c | 196 +++++++++++ lib/sqfs/src/win32/io_file.c | 237 +++++++++++++ lib/sqfs/src/write_inode.c | 220 ++++++++++++ lib/sqfs/src/write_super.c | 39 +++ lib/sqfs/src/write_table.c | 81 +++++ lib/sqfs/src/xattr/xattr.c | 49 +++ lib/sqfs/src/xattr/xattr_reader.c | 336 ++++++++++++++++++ lib/sqfs/src/xattr/xattr_writer.c | 127 +++++++ lib/sqfs/src/xattr/xattr_writer.h | 63 ++++ lib/sqfs/src/xattr/xattr_writer_flush.c | 347 +++++++++++++++++++ lib/sqfs/src/xattr/xattr_writer_record.c | 145 ++++++++ lib/sqfs/super.c | 50 --- lib/sqfs/unix/io_file.c | 196 ----------- lib/sqfs/win32/io_file.c | 237 ------------- lib/sqfs/write_inode.c | 220 ------------ lib/sqfs/write_super.c | 39 --- lib/sqfs/write_table.c | 81 ----- lib/sqfs/xattr/xattr.c | 49 --- lib/sqfs/xattr/xattr_reader.c | 336 ------------------ lib/sqfs/xattr/xattr_writer.c | 127 ------- lib/sqfs/xattr/xattr_writer.h | 63 ---- lib/sqfs/xattr/xattr_writer_flush.c | 347 ------------------- lib/sqfs/xattr/xattr_writer_record.c | 145 -------- 81 files changed, 8287 insertions(+), 8290 deletions(-) delete mode 100644 lib/sqfs/block_processor/backend.c delete mode 100644 lib/sqfs/block_processor/block_processor.c delete mode 100644 lib/sqfs/block_processor/frontend.c delete mode 100644 lib/sqfs/block_processor/internal.h delete mode 100644 lib/sqfs/block_writer.c delete mode 100644 lib/sqfs/comp/compressor.c delete mode 100644 lib/sqfs/comp/gzip.c delete mode 100644 lib/sqfs/comp/internal.h delete mode 100644 lib/sqfs/comp/lz4.c delete mode 100644 lib/sqfs/comp/lzma.c delete mode 100644 lib/sqfs/comp/xz.c delete mode 100644 lib/sqfs/comp/zstd.c delete mode 100644 lib/sqfs/data_reader.c delete mode 100644 lib/sqfs/dir_reader/dir_reader.c delete mode 100644 lib/sqfs/dir_reader/get_path.c delete mode 100644 lib/sqfs/dir_reader/internal.h delete mode 100644 lib/sqfs/dir_reader/read_tree.c delete mode 100644 lib/sqfs/dir_writer.c delete mode 100644 lib/sqfs/frag_table.c delete mode 100644 lib/sqfs/id_table.c delete mode 100644 lib/sqfs/inode.c delete mode 100644 lib/sqfs/meta_reader.c delete mode 100644 lib/sqfs/meta_writer.c delete mode 100644 lib/sqfs/misc.c delete mode 100644 lib/sqfs/read_inode.c delete mode 100644 lib/sqfs/read_super.c delete mode 100644 lib/sqfs/read_table.c delete mode 100644 lib/sqfs/readdir.c create mode 100644 lib/sqfs/src/block_processor/backend.c create mode 100644 lib/sqfs/src/block_processor/block_processor.c create mode 100644 lib/sqfs/src/block_processor/frontend.c create mode 100644 lib/sqfs/src/block_processor/internal.h create mode 100644 lib/sqfs/src/block_writer.c create mode 100644 lib/sqfs/src/comp/compressor.c create mode 100644 lib/sqfs/src/comp/gzip.c create mode 100644 lib/sqfs/src/comp/internal.h create mode 100644 lib/sqfs/src/comp/lz4.c create mode 100644 lib/sqfs/src/comp/lzma.c create mode 100644 lib/sqfs/src/comp/xz.c create mode 100644 lib/sqfs/src/comp/zstd.c create mode 100644 lib/sqfs/src/data_reader.c create mode 100644 lib/sqfs/src/dir_reader/dir_reader.c create mode 100644 lib/sqfs/src/dir_reader/get_path.c create mode 100644 lib/sqfs/src/dir_reader/internal.h create mode 100644 lib/sqfs/src/dir_reader/read_tree.c create mode 100644 lib/sqfs/src/dir_writer.c create mode 100644 lib/sqfs/src/frag_table.c create mode 100644 lib/sqfs/src/id_table.c create mode 100644 lib/sqfs/src/inode.c create mode 100644 lib/sqfs/src/meta_reader.c create mode 100644 lib/sqfs/src/meta_writer.c create mode 100644 lib/sqfs/src/misc.c create mode 100644 lib/sqfs/src/read_inode.c create mode 100644 lib/sqfs/src/read_super.c create mode 100644 lib/sqfs/src/read_table.c create mode 100644 lib/sqfs/src/readdir.c create mode 100644 lib/sqfs/src/super.c create mode 100644 lib/sqfs/src/unix/io_file.c create mode 100644 lib/sqfs/src/win32/io_file.c create mode 100644 lib/sqfs/src/write_inode.c create mode 100644 lib/sqfs/src/write_super.c create mode 100644 lib/sqfs/src/write_table.c create mode 100644 lib/sqfs/src/xattr/xattr.c create mode 100644 lib/sqfs/src/xattr/xattr_reader.c create mode 100644 lib/sqfs/src/xattr/xattr_writer.c create mode 100644 lib/sqfs/src/xattr/xattr_writer.h create mode 100644 lib/sqfs/src/xattr/xattr_writer_flush.c create mode 100644 lib/sqfs/src/xattr/xattr_writer_record.c delete mode 100644 lib/sqfs/super.c delete mode 100644 lib/sqfs/unix/io_file.c delete mode 100644 lib/sqfs/win32/io_file.c delete mode 100644 lib/sqfs/write_inode.c delete mode 100644 lib/sqfs/write_super.c delete mode 100644 lib/sqfs/write_table.c delete mode 100644 lib/sqfs/xattr/xattr.c delete mode 100644 lib/sqfs/xattr/xattr_reader.c delete mode 100644 lib/sqfs/xattr/xattr_writer.c delete mode 100644 lib/sqfs/xattr/xattr_writer.h delete mode 100644 lib/sqfs/xattr/xattr_writer_flush.c delete mode 100644 lib/sqfs/xattr/xattr_writer_record.c (limited to 'lib/sqfs') diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am index 748794a..dfd88c9 100644 --- a/lib/sqfs/Makemodule.am +++ b/lib/sqfs/Makemodule.am @@ -10,30 +10,27 @@ LIBSQFS_HEARDS = include/sqfs/meta_writer.h \ include/sqfs/xattr_reader.h include/sqfs/xattr_writer.h \ include/sqfs/frag_table.h include/sqfs/block_writer.h -libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/id_table.c lib/sqfs/super.c -libsquashfs_la_SOURCES += lib/sqfs/readdir.c lib/sqfs/xattr/xattr.c -libsquashfs_la_SOURCES += lib/sqfs/write_table.c lib/sqfs/meta_writer.c -libsquashfs_la_SOURCES += lib/sqfs/read_super.c lib/sqfs/meta_reader.c -libsquashfs_la_SOURCES += lib/sqfs/read_inode.c lib/sqfs/write_inode.c -libsquashfs_la_SOURCES += lib/sqfs/dir_writer.c lib/sqfs/xattr/xattr_reader.c -libsquashfs_la_SOURCES += lib/sqfs/read_table.c lib/sqfs/comp/compressor.c -libsquashfs_la_SOURCES += lib/sqfs/comp/internal.h -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/dir_reader.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/read_tree.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/get_path.c -libsquashfs_la_SOURCES += lib/sqfs/dir_reader/internal.h -libsquashfs_la_SOURCES += lib/sqfs/inode.c lib/sqfs/xattr/xattr_writer.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_flush.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer_record.c -libsquashfs_la_SOURCES += lib/sqfs/xattr/xattr_writer.h -libsquashfs_la_SOURCES += lib/sqfs/write_super.c lib/sqfs/data_reader.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/internal.h -libsquashfs_la_SOURCES += lib/sqfs/block_processor/frontend.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/block_processor.c -libsquashfs_la_SOURCES += lib/sqfs/block_processor/backend.c -libsquashfs_la_SOURCES += lib/sqfs/frag_table.c include/sqfs/frag_table.h -libsquashfs_la_SOURCES += lib/sqfs/block_writer.c include/sqfs/block_writer.h -libsquashfs_la_SOURCES += lib/sqfs/misc.c +libsquashfs_la_SOURCES = $(LIBSQFS_HEARDS) lib/sqfs/src/id_table.c \ + lib/sqfs/src/super.c lib/sqfs/src/readdir.c lib/sqfs/src/xattr/xattr.c \ + lib/sqfs/src/write_table.c lib/sqfs/src/meta_writer.c \ + lib/sqfs/src/read_super.c lib/sqfs/src/meta_reader.c \ + lib/sqfs/src/read_inode.c lib/sqfs/src/write_inode.c \ + lib/sqfs/src/dir_writer.c lib/sqfs/src/xattr/xattr_reader.c \ + lib/sqfs/src/read_table.c lib/sqfs/src/comp/compressor.c \ + lib/sqfs/src/comp/internal.h lib/sqfs/src/dir_reader/dir_reader.c \ + lib/sqfs/src/dir_reader/read_tree.c lib/sqfs/src/dir_reader/get_path.c \ + lib/sqfs/src/dir_reader/internal.h lib/sqfs/src/inode.c \ + lib/sqfs/src/xattr/xattr_writer.c \ + lib/sqfs/src/xattr/xattr_writer_flush.c \ + lib/sqfs/src/xattr/xattr_writer_record.c \ + lib/sqfs/src/xattr/xattr_writer.h \ + lib/sqfs/src/write_super.c lib/sqfs/src/data_reader.c \ + lib/sqfs/src/block_processor/internal.h \ + lib/sqfs/src/block_processor/frontend.c \ + lib/sqfs/src/block_processor/block_processor.c \ + lib/sqfs/src/block_processor/backend.c \ + lib/sqfs/src/frag_table.c lib/sqfs/src/block_writer.c \ + lib/sqfs/src/misc.c libsquashfs_la_CPPFLAGS = $(AM_CPPFLAGS) libsquashfs_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBSQUASHFS_SO_VERSION) libsquashfs_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS) @@ -43,60 +40,60 @@ libsquashfs_la_LIBADD = $(XZ_LIBS) $(ZLIB_LIBS) $(LZ4_LIBS) libsquashfs_la_LIBADD += $(ZSTD_LIBS) $(PTHREAD_LIBS) # directly "import" stuff from libutil -libsquashfs_la_SOURCES += lib/util/str_table.c lib/util/alloc.c -libsquashfs_la_SOURCES += lib/util/xxhash.c lib/util/file_cmp.c -libsquashfs_la_SOURCES += lib/util/hash_table.c include/util/hash_table.h -libsquashfs_la_SOURCES += lib/util/rbtree.c include/util/rbtree.h -libsquashfs_la_SOURCES += lib/util/array.c include/util/array.h -libsquashfs_la_SOURCES += lib/util/is_memory_zero.c +libsquashfs_la_SOURCES += lib/util/src/str_table.c lib/util/src/alloc.c +libsquashfs_la_SOURCES += lib/util/src/xxhash.c lib/util/src/file_cmp.c +libsquashfs_la_SOURCES += lib/util/src/hash_table.c include/util/hash_table.h +libsquashfs_la_SOURCES += lib/util/src/rbtree.c include/util/rbtree.h +libsquashfs_la_SOURCES += lib/util/src/array.c include/util/array.h +libsquashfs_la_SOURCES += lib/util/src/is_memory_zero.c libsquashfs_la_SOURCES += include/util/threadpool.h # and from libcompat -libsquashfs_la_SOURCES += lib/compat/strchrnul.c lib/compat/strndup.c +libsquashfs_la_SOURCES += lib/compat/src/strchrnul.c lib/compat/src/strndup.c libsquashfs_la_SOURCES += include/compat.h if CUSTOM_ALLOC -libsquashfs_la_SOURCES += lib/util/mempool.c include/util/mempool.h +libsquashfs_la_SOURCES += lib/util/src/mempool.c include/util/mempool.h endif if WINDOWS -libsquashfs_la_SOURCES += lib/sqfs/win32/io_file.c +libsquashfs_la_SOURCES += lib/sqfs/src/win32/io_file.c libsquashfs_la_CFLAGS += -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 libsquashfs_la_CFLAGS += -Wc,-static-libgcc libsquashfs_la_LDFLAGS += -no-undefined -avoid-version else -libsquashfs_la_SOURCES += lib/sqfs/unix/io_file.c +libsquashfs_la_SOURCES += lib/sqfs/src/unix/io_file.c endif if HAVE_PTHREAD -libsquashfs_la_SOURCES += lib/util/threadpool.c +libsquashfs_la_SOURCES += lib/util/src/threadpool.c else if WINDOWS -libsquashfs_la_SOURCES += lib/util/threadpool.c +libsquashfs_la_SOURCES += lib/util/src/threadpool.c else -libsquashfs_la_SOURCES += lib/util/threadpool_serial.c +libsquashfs_la_SOURCES += lib/util/src/threadpool_serial.c libsquashfs_la_CPPFLAGS += -DNO_THREAD_IMPL endif endif if WITH_GZIP -libsquashfs_la_SOURCES += lib/sqfs/comp/gzip.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/gzip.c libsquashfs_la_CPPFLAGS += -DWITH_GZIP endif if WITH_XZ -libsquashfs_la_SOURCES += lib/sqfs/comp/xz.c -libsquashfs_la_SOURCES += lib/sqfs/comp/lzma.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/xz.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/lzma.c libsquashfs_la_CPPFLAGS += -DWITH_XZ endif if WITH_LZ4 -libsquashfs_la_SOURCES += lib/sqfs/comp/lz4.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/lz4.c libsquashfs_la_CPPFLAGS += -DWITH_LZ4 endif if WITH_ZSTD -libsquashfs_la_SOURCES += lib/sqfs/comp/zstd.c +libsquashfs_la_SOURCES += lib/sqfs/src/comp/zstd.c libsquashfs_la_CPPFLAGS += -DWITH_ZSTD endif diff --git a/lib/sqfs/block_processor/backend.c b/lib/sqfs/block_processor/backend.c deleted file mode 100644 index b443c9d..0000000 --- a/lib/sqfs/block_processor/backend.c +++ /dev/null @@ -1,335 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * backend.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int set_block_size(sqfs_inode_generic_t **inode, - sqfs_u32 index, sqfs_u32 size) -{ - size_t min_size = (index + 1) * sizeof(sqfs_u32); - size_t avail = (*inode)->payload_bytes_available; - sqfs_inode_generic_t *new; - size_t newsz; - - if (avail < min_size) { - newsz = avail ? avail : (sizeof(sqfs_u32) * 4); - while (newsz < min_size) - newsz *= 2; - - if (SZ_ADD_OV(newsz, sizeof(**inode), &newsz)) - return SQFS_ERROR_OVERFLOW; - - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if ((newsz - sizeof(**inode)) > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - new = realloc((*inode), newsz); - if (new == NULL) - return SQFS_ERROR_ALLOC; - - (*inode) = new; - (*inode)->payload_bytes_available = newsz - sizeof(**inode); - } - - (*inode)->extra[index] = size; - - if (min_size >= (*inode)->payload_bytes_used) - (*inode)->payload_bytes_used = min_size; - - return 0; -} - -static void release_old_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - blk->next = proc->free_list; - proc->free_list = blk; - - proc->backlog -= 1; -} - -static int process_completed_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - sqfs_u64 location; - sqfs_u32 size; - int err; - - if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { - sqfs_block_t *it = proc->fblk_in_flight, *prev = NULL; - - while (it != NULL && it->index != blk->index) { - prev = it; - it = it->next; - } - - if (it != NULL) { - if (prev == NULL) { - proc->fblk_in_flight = it->next; - } else { - prev->next = it->next; - } - free(it); - } - } - - err = proc->wr->write_data_block(proc->wr, blk->user, blk->size, - blk->checksum, - blk->flags & ~BLK_FLAG_INTERNAL, - blk->data, &location); - if (err) - goto out; - - proc->stats.output_bytes_generated += blk->size; - - if (blk->flags & SQFS_BLK_IS_SPARSE) { - if (blk->inode != NULL) { - sqfs_inode_make_extended(*(blk->inode)); - (*(blk->inode))->data.file_ext.sparse += blk->size; - - err = set_block_size(blk->inode, blk->index, 0); - if (err) - goto out; - } - proc->stats.sparse_block_count += 1; - } else if (blk->size != 0) { - size = blk->size; - if (!(blk->flags & SQFS_BLK_IS_COMPRESSED)) - size |= 1 << 24; - - if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { - if (proc->frag_tbl != NULL) { - err = sqfs_frag_table_set(proc->frag_tbl, - blk->index, location, - size); - if (err) - goto out; - } - proc->stats.frag_block_count += 1; - } else { - if (blk->inode != NULL) { - err = set_block_size(blk->inode, blk->index, - size); - if (err) - goto out; - } - proc->stats.data_block_count += 1; - } - } - - if (blk->flags & SQFS_BLK_LAST_BLOCK && blk->inode != NULL) - sqfs_inode_set_file_block_start(*(blk->inode), location); -out: - release_old_block(proc, blk); - return err; -} - -static int process_completed_fragment(sqfs_block_processor_t *proc, - sqfs_block_t *frag) -{ - chunk_info_t *chunk = NULL, search; - struct hash_entry *entry; - sqfs_u32 index, offset; - int err; - - if (frag->flags & SQFS_BLK_IS_SPARSE) { - if (frag->inode != NULL) { - sqfs_inode_make_extended(*(frag->inode)); - set_block_size(frag->inode, frag->index, 0); - (*(frag->inode))->data.file_ext.sparse += frag->size; - } - proc->stats.sparse_block_count += 1; - release_old_block(proc, frag); - return 0; - } - - proc->stats.total_frag_count += 1; - - if (!(frag->flags & SQFS_BLK_DONT_DEDUPLICATE)) { - search.hash = frag->checksum; - search.size = frag->size; - - proc->current_frag = frag; - proc->fblk_lookup_error = 0; - entry = hash_table_search_pre_hashed(proc->frag_ht, - search.hash, &search); - proc->current_frag = NULL; - - if (proc->fblk_lookup_error != 0) { - err = proc->fblk_lookup_error; - goto fail; - } - - if (entry != NULL) { - if (frag->inode != NULL) { - chunk = entry->data; - sqfs_inode_set_frag_location(*(frag->inode), - chunk->index, - chunk->offset); - } - release_old_block(proc, frag); - return 0; - } - } - - if (proc->frag_block != NULL) { - size_t size = proc->frag_block->size + frag->size; - - if (size > proc->max_block_size) { - proc->frag_block->io_seq_num = proc->io_seq_num++; - - err = enqueue_block(proc, proc->frag_block); - proc->frag_block = NULL; - - if (err) - goto fail; - } - } - - if (proc->frag_block == NULL) { - if (proc->frag_tbl == NULL) { - index = 0; - } else { - err = sqfs_frag_table_append(proc->frag_tbl, - 0, 0, &index); - if (err) - goto fail; - } - - offset = 0; - proc->frag_block = frag; - proc->frag_block->index = index; - proc->frag_block->flags &= - (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN); - proc->frag_block->flags |= SQFS_BLK_FRAGMENT_BLOCK; - } else { - index = proc->frag_block->index; - offset = proc->frag_block->size; - - memcpy(proc->frag_block->data + proc->frag_block->size, - frag->data, frag->size); - - proc->frag_block->size += frag->size; - proc->frag_block->flags |= - (frag->flags & - (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN)); - } - - if (proc->frag_tbl != NULL) { - err = SQFS_ERROR_ALLOC; - chunk = calloc(1, sizeof(*chunk)); - if (chunk == NULL) - goto fail; - - chunk->index = index; - chunk->offset = offset; - chunk->size = frag->size; - chunk->hash = frag->checksum; - - proc->current_frag = frag; - proc->fblk_lookup_error = 0; - entry = hash_table_insert_pre_hashed(proc->frag_ht, chunk->hash, - chunk, chunk); - proc->current_frag = NULL; - - if (proc->fblk_lookup_error != 0) { - err = proc->fblk_lookup_error; - goto fail; - } - - if (entry == NULL) - goto fail; - } - - if (frag->inode != NULL) - sqfs_inode_set_frag_location(*(frag->inode), index, offset); - - if (frag != proc->frag_block) - release_old_block(proc, frag); - - proc->stats.actual_frag_count += 1; - return 0; -fail: - free(chunk); - if (frag != proc->frag_block) - release_old_block(proc, frag); - return err; -} - -static void store_io_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - sqfs_block_t *prev = NULL, *it = proc->io_queue; - - while (it != NULL && (it->io_seq_num < blk->io_seq_num)) { - prev = it; - it = it->next; - } - - if (prev == NULL) { - proc->io_queue = blk; - } else { - prev->next = blk; - } - - blk->next = it; -} - -int dequeue_block(sqfs_block_processor_t *proc) -{ - size_t backlog_old = proc->backlog; - sqfs_block_t *blk; - int status; - - do { - while (proc->io_queue != NULL) { - if (proc->io_queue->io_seq_num != proc->io_deq_seq_num) - break; - - blk = proc->io_queue; - proc->io_queue = blk->next; - proc->io_deq_seq_num += 1; - - status = process_completed_block(proc, blk); - if (status != 0) - return status; - } - - if (proc->backlog < backlog_old) - break; - - if ((proc->backlog == 1) && - (proc->frag_block != NULL || proc->blk_current != NULL)) { - break; - } - - if ((proc->backlog == 2) && - proc->frag_block != NULL && proc->blk_current != NULL) { - break; - } - - blk = proc->pool->dequeue(proc->pool); - - if (blk == NULL) { - status = proc->pool->get_status(proc->pool); - return status ? status : SQFS_ERROR_INTERNAL; - } - - if (blk->flags & SQFS_BLK_IS_FRAGMENT) { - status = process_completed_fragment(proc, blk); - if (status != 0) - return status; - } else { - if (!(blk->flags & SQFS_BLK_FRAGMENT_BLOCK) || - (blk->flags & BLK_FLAG_MANUAL_SUBMISSION)) { - blk->io_seq_num = proc->io_seq_num++; - } - - store_io_block(proc, blk); - } - } while (proc->backlog >= backlog_old); - - return 0; -} diff --git a/lib/sqfs/block_processor/block_processor.c b/lib/sqfs/block_processor/block_processor.c deleted file mode 100644 index d607437..0000000 --- a/lib/sqfs/block_processor/block_processor.c +++ /dev/null @@ -1,358 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * block_processor.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int process_block(void *userptr, void *workitem) -{ - worker_data_t *worker = userptr; - sqfs_block_t *block = workitem; - sqfs_s32 ret; - - if (block->size == 0) - return 0; - - if (!(block->flags & SQFS_BLK_IGNORE_SPARSE) && - is_memory_zero(block->data, block->size)) { - block->flags |= SQFS_BLK_IS_SPARSE; - return 0; - } - - if (block->flags & SQFS_BLK_DONT_HASH) { - block->checksum = 0; - } else { - block->checksum = xxh32(block->data, block->size); - } - - if (block->flags & (SQFS_BLK_IS_FRAGMENT | SQFS_BLK_DONT_COMPRESS)) - return 0; - - ret = worker->cmp->do_block(worker->cmp, block->data, block->size, - worker->scratch, worker->scratch_size); - if (ret < 0) - return ret; - - if (ret > 0) { - memcpy(block->data, worker->scratch, ret); - block->size = ret; - block->flags |= SQFS_BLK_IS_COMPRESSED; - } - return 0; -} - -static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) -{ - sqfs_fragment_t info; - size_t size; - int ret; - - if (proc->cached_frag_blk == NULL) { - size = sizeof(*proc->cached_frag_blk); - - proc->cached_frag_blk = alloc_flex(size, 1, - proc->max_block_size); - - if (proc->cached_frag_blk == NULL) - return SQFS_ERROR_ALLOC; - } else { - if (proc->cached_frag_blk->index == index) - return 0; - } - - ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); - if (ret != 0) - return ret; - - size = SQFS_ON_DISK_BLOCK_SIZE(info.size); - if (size > proc->max_block_size) - return SQFS_ERROR_CORRUPTED; - - if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { - ret = proc->file->read_at(proc->file, info.start_offset, - proc->scratch, size); - if (ret != 0) - return ret; - - ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, - proc->cached_frag_blk->data, - proc->max_block_size); - if (ret <= 0) - return ret ? ret : SQFS_ERROR_OVERFLOW; - - size = ret; - } else { - ret = proc->file->read_at(proc->file, info.start_offset, - proc->cached_frag_blk->data, size); - if (ret != 0) - return ret; - } - - proc->cached_frag_blk->size = size; - proc->cached_frag_blk->index = index; - return 0; -} - -static bool chunk_info_equals(void *user, const void *k, const void *c) -{ - const chunk_info_t *key = k, *cmp = c; - sqfs_block_processor_t *proc = user; - sqfs_block_t *it; - int ret; - - if (key->size != cmp->size || key->hash != cmp->hash) - return false; - - if (proc->uncmp == NULL || proc->file == NULL) - return true; - - if (proc->current_frag == NULL || proc->frag_tbl == NULL) - return true; - - if (proc->fblk_lookup_error != 0) - return false; - - for (it = proc->fblk_in_flight; it != NULL; it = it->next) { - if (it->index == cmp->index) - break; - } - - if (it == NULL && proc->frag_block != NULL) { - if (proc->frag_block->index == cmp->index) - it = proc->frag_block; - } - - if (it == NULL) { - ret = load_frag_block(proc, cmp->index); - if (ret != 0) { - proc->fblk_lookup_error = ret; - return false; - } - - it = proc->cached_frag_blk; - } - - if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { - proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; - return false; - } - - if (cmp->size != proc->current_frag->size) { - proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; - return false; - } - - return memcmp(it->data + cmp->offset, - proc->current_frag->data, cmp->size) == 0; -} - -static void ht_delete_function(struct hash_entry *entry) -{ - free(entry->data); -} - -static void free_block_list(sqfs_block_t *list) -{ - while (list != NULL) { - sqfs_block_t *it = list; - list = it->next; - free(it); - } -} - -static void block_processor_destroy(sqfs_object_t *base) -{ - sqfs_block_processor_t *proc = (sqfs_block_processor_t *)base; - - free(proc->frag_block); - free(proc->blk_current); - free(proc->cached_frag_blk); - - free_block_list(proc->free_list); - free_block_list(proc->io_queue); - free_block_list(proc->fblk_in_flight); - - if (proc->frag_ht != NULL) - hash_table_destroy(proc->frag_ht, ht_delete_function); - - /* XXX: shut down the pool first before cleaning up the worker data */ - if (proc->pool != NULL) - proc->pool->destroy(proc->pool); - - while (proc->workers != NULL) { - worker_data_t *worker = proc->workers; - proc->workers = worker->next; - - sqfs_drop(worker->cmp); - free(worker); - } - - sqfs_drop(proc->frag_tbl); - sqfs_drop(proc->wr); - sqfs_drop(proc->file); - sqfs_drop(proc->uncmp); - free(proc); -} - -int sqfs_block_processor_sync(sqfs_block_processor_t *proc) -{ - int ret; - - for (;;) { - if (proc->backlog == 0) - break; - - if ((proc->backlog == 1) && - (proc->frag_block != NULL || proc->blk_current != NULL)) { - break; - } - - if ((proc->backlog == 2) && - proc->frag_block != NULL && proc->blk_current != NULL) { - break; - } - - ret = dequeue_block(proc); - if (ret != 0) - return ret; - } - - return 0; -} - -int sqfs_block_processor_finish(sqfs_block_processor_t *proc) -{ - sqfs_block_t *blk; - int status; - - status = sqfs_block_processor_sync(proc); - if (status != 0) - return status; - - if (proc->frag_block != NULL) { - blk = proc->frag_block; - blk->next = NULL; - proc->frag_block = NULL; - - blk->io_seq_num = proc->io_seq_num++; - - status = enqueue_block(proc, blk); - if (status != 0) - return status; - - status = sqfs_block_processor_sync(proc); - } - - return status; -} - -const sqfs_block_processor_stats_t -*sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc) -{ - return &proc->stats; -} - -int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, - sqfs_block_processor_t **out) -{ - size_t i, count, scratch_size = 0; - sqfs_block_processor_t *proc; - int ret; - - if (desc->size != sizeof(sqfs_block_processor_desc_t)) - return SQFS_ERROR_ARG_INVALID; - - if (desc->file != NULL && desc->uncmp != NULL) - scratch_size = desc->max_block_size; - - proc = alloc_flex(sizeof(*proc), 1, scratch_size); - if (proc == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(proc, block_processor_destroy, NULL); - - proc->max_backlog = desc->max_backlog; - proc->max_block_size = desc->max_block_size; - proc->frag_tbl = sqfs_grab(desc->tbl); - proc->wr = sqfs_grab(desc->wr); - proc->file = sqfs_grab(desc->file); - proc->uncmp = sqfs_grab(desc->uncmp); - proc->stats.size = sizeof(proc->stats); - - /* we need at least one current data block + one fragment block */ - if (proc->max_backlog < 3) - proc->max_backlog = 3; - - /* create the thread pool */ - proc->pool = thread_pool_create(desc->num_workers, process_block); - if (proc->pool == NULL) { - ret = SQFS_ERROR_INTERNAL; - goto fail_pool; - } - - /* create the worker compressors & scratch buffer */ - count = proc->pool->get_worker_count(proc->pool); - - for (i = 0; i < count; ++i) { - worker_data_t *worker = alloc_flex(sizeof(*worker), 1, - desc->max_block_size); - if (worker == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - worker->scratch_size = desc->max_block_size; - worker->next = proc->workers; - proc->workers = worker; - - worker->cmp = sqfs_copy(desc->cmp); - if (worker->cmp == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - proc->pool->set_worker_ptr(proc->pool, i, worker); - } - - /* create the fragment hash table */ - proc->frag_ht = hash_table_create(NULL, chunk_info_equals); - if (proc->frag_ht == NULL) { - ret = SQFS_ERROR_ALLOC; - goto fail_pool; - } - - proc->frag_ht->user = proc; - *out = proc; - return 0; -fail_pool: - block_processor_destroy((sqfs_object_t *)proc); - return ret; -} - -sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, - sqfs_compressor_t *cmp, - unsigned int num_workers, - size_t max_backlog, - sqfs_block_writer_t *wr, - sqfs_frag_table_t *tbl) -{ - sqfs_block_processor_desc_t desc; - sqfs_block_processor_t *out; - - memset(&desc, 0, sizeof(desc)); - desc.size = sizeof(desc); - desc.max_block_size = max_block_size; - desc.num_workers = num_workers; - desc.max_backlog = max_backlog; - desc.cmp = cmp; - desc.wr = wr; - desc.tbl = tbl; - - if (sqfs_block_processor_create_ex(&desc, &out) != 0) - return NULL; - - return out; -} diff --git a/lib/sqfs/block_processor/frontend.c b/lib/sqfs/block_processor/frontend.c deleted file mode 100644 index e8a4207..0000000 --- a/lib/sqfs/block_processor/frontend.c +++ /dev/null @@ -1,243 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * frontend.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int get_new_block(sqfs_block_processor_t *proc, sqfs_block_t **out) -{ - sqfs_block_t *blk; - - while (proc->backlog >= proc->max_backlog) { - int ret = dequeue_block(proc); - if (ret != 0) - return ret; - } - - if (proc->free_list != NULL) { - blk = proc->free_list; - proc->free_list = blk->next; - } else { - blk = malloc(sizeof(*blk) + proc->max_block_size); - if (blk == NULL) - return SQFS_ERROR_ALLOC; - } - - memset(blk, 0, sizeof(*blk)); - *out = blk; - - proc->backlog += 1; - return 0; -} - -static int add_sentinel_block(sqfs_block_processor_t *proc) -{ - sqfs_block_t *blk; - int ret; - - ret = get_new_block(proc, &blk); - if (ret != 0) - return ret; - - blk->inode = proc->inode; - blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; - - return enqueue_block(proc, blk); -} - -int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) -{ - int status; - - if ((blk->flags & SQFS_BLK_FRAGMENT_BLOCK) && - proc->file != NULL && proc->uncmp != NULL) { - sqfs_block_t *copy = alloc_flex(sizeof(*copy), 1, blk->size); - - if (copy == NULL) - return SQFS_ERROR_ALLOC; - - copy->size = blk->size; - copy->index = blk->index; - memcpy(copy->data, blk->data, blk->size); - - copy->next = proc->fblk_in_flight; - proc->fblk_in_flight = copy; - } - - if (proc->pool->submit(proc->pool, blk) != 0) { - status = proc->pool->get_status(proc->pool); - - if (status == 0) - status = SQFS_ERROR_ALLOC; - - blk->next = proc->free_list; - proc->free_list = blk; - return status; - } - - return 0; -} - -int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, - sqfs_inode_generic_t **inode, - void *user, sqfs_u32 flags) -{ - if (proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) - return SQFS_ERROR_UNSUPPORTED; - - if (inode != NULL) { - (*inode) = calloc(1, sizeof(sqfs_inode_generic_t)); - if ((*inode) == NULL) - return SQFS_ERROR_ALLOC; - - (*inode)->base.type = SQFS_INODE_FILE; - sqfs_inode_set_frag_location(*inode, 0xFFFFFFFF, 0xFFFFFFFF); - } - - proc->begin_called = true; - proc->inode = inode; - proc->blk_flags = flags | SQFS_BLK_FIRST_BLOCK; - proc->blk_index = 0; - proc->user = user; - return 0; -} - -int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, - size_t size) -{ - sqfs_block_t *new; - sqfs_u64 filesize; - size_t diff; - int err; - - if (!proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (proc->inode != NULL) { - sqfs_inode_get_file_size(*(proc->inode), &filesize); - sqfs_inode_set_file_size(*(proc->inode), filesize + size); - } - - while (size > 0) { - if (proc->blk_current == NULL) { - err = get_new_block(proc, &new); - if (err != 0) - return err; - - proc->blk_current = new; - proc->blk_current->flags = proc->blk_flags; - proc->blk_current->inode = proc->inode; - proc->blk_current->user = proc->user; - proc->blk_current->index = proc->blk_index++; - proc->blk_flags &= ~SQFS_BLK_FIRST_BLOCK; - } - - diff = proc->max_block_size - proc->blk_current->size; - - if (diff == 0) { - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - continue; - } - - if (diff > size) - diff = size; - - memcpy(proc->blk_current->data + proc->blk_current->size, - data, diff); - - size -= diff; - proc->blk_current->size += diff; - data = (const char *)data + diff; - - proc->stats.input_bytes_read += diff; - } - - if (proc->blk_current->size == proc->max_block_size) { - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - } - - return 0; -} - -int sqfs_block_processor_end_file(sqfs_block_processor_t *proc) -{ - int err; - - if (!proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (proc->blk_current == NULL) { - if (!(proc->blk_flags & SQFS_BLK_FIRST_BLOCK)) { - err = add_sentinel_block(proc); - if (err) - return err; - } - } else { - if (proc->blk_flags & SQFS_BLK_DONT_FRAGMENT) { - proc->blk_current->flags |= SQFS_BLK_LAST_BLOCK; - } else { - if (!(proc->blk_current->flags & - SQFS_BLK_FIRST_BLOCK)) { - err = add_sentinel_block(proc); - if (err) - return err; - } - - proc->blk_current->flags |= SQFS_BLK_IS_FRAGMENT; - } - - err = enqueue_block(proc, proc->blk_current); - proc->blk_current = NULL; - - if (err) - return err; - } - - proc->begin_called = false; - proc->inode = NULL; - proc->user = NULL; - proc->blk_flags = 0; - return 0; -} - -int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, - sqfs_u32 flags, const void *data, - size_t size) -{ - sqfs_block_t *blk; - int ret; - - if (proc->begin_called) - return SQFS_ERROR_SEQUENCE; - - if (size > proc->max_block_size) - return SQFS_ERROR_OVERFLOW; - - if (flags & ~SQFS_BLK_FLAGS_ALL) - return SQFS_ERROR_UNSUPPORTED; - - ret = get_new_block(proc, &blk); - if (ret != 0) - return ret; - - blk->flags = flags | BLK_FLAG_MANUAL_SUBMISSION; - blk->user = user; - blk->size = size; - memcpy(blk->data, data, size); - - return enqueue_block(proc, blk); -} diff --git a/lib/sqfs/block_processor/internal.h b/lib/sqfs/block_processor/internal.h deleted file mode 100644 index 0b2c88d..0000000 --- a/lib/sqfs/block_processor/internal.h +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" - -#include "sqfs/block_processor.h" -#include "sqfs/block_writer.h" -#include "sqfs/frag_table.h" -#include "sqfs/compressor.h" -#include "sqfs/inode.h" -#include "sqfs/table.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/hash_table.h" -#include "util/threadpool.h" -#include "util/util.h" - -#include -#include - -typedef struct { - sqfs_u32 index; - sqfs_u32 offset; - sqfs_u32 size; - sqfs_u32 hash; -} chunk_info_t; - -enum { - BLK_FLAG_MANUAL_SUBMISSION = 0x10000000, - BLK_FLAG_INTERNAL = 0x10000000, -}; - -typedef struct sqfs_block_t { - struct sqfs_block_t *next; - sqfs_inode_generic_t **inode; - - sqfs_u32 io_seq_num; - sqfs_u32 flags; - sqfs_u32 size; - sqfs_u32 checksum; - - /* For data blocks: index within the inode. - For fragment fragment blocks: fragment table index. */ - sqfs_u32 index; - - /* User data pointer */ - void *user; - - sqfs_u8 data[]; -} sqfs_block_t; - -typedef struct worker_data_t { - struct worker_data_t *next; - sqfs_compressor_t *cmp; - - size_t scratch_size; - sqfs_u8 scratch[]; -} worker_data_t; - -struct sqfs_block_processor_t { - sqfs_object_t obj; - - sqfs_frag_table_t *frag_tbl; - sqfs_block_t *frag_block; - sqfs_block_writer_t *wr; - - sqfs_block_processor_stats_t stats; - - sqfs_inode_generic_t **inode; - sqfs_block_t *blk_current; - sqfs_u32 blk_flags; - sqfs_u32 blk_index; - void *user; - - struct hash_table *frag_ht; - sqfs_block_t *free_list; - - size_t max_block_size; - size_t max_backlog; - size_t backlog; - - bool begin_called; - - sqfs_file_t *file; - sqfs_compressor_t *uncmp; - - thread_pool_t *pool; - worker_data_t *workers; - - sqfs_block_t *io_queue; - sqfs_u32 io_seq_num; - sqfs_u32 io_deq_seq_num; - - sqfs_block_t *current_frag; - sqfs_block_t *cached_frag_blk; - sqfs_block_t *fblk_in_flight; - int fblk_lookup_error; - - sqfs_u8 scratch[]; -}; - -SQFS_INTERNAL int enqueue_block(sqfs_block_processor_t *proc, - sqfs_block_t *blk); - -SQFS_INTERNAL int dequeue_block(sqfs_block_processor_t *proc); - -#endif /* INTERNAL_H */ diff --git a/lib/sqfs/block_writer.c b/lib/sqfs/block_writer.c deleted file mode 100644 index a5135bc..0000000 --- a/lib/sqfs/block_writer.c +++ /dev/null @@ -1,238 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * block_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/block_writer.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/array.h" -#include "util/util.h" - -#include -#include - -#define MK_BLK_HASH(chksum, size) \ - (((sqfs_u64)(size) << 32) | (sqfs_u64)(chksum)) - -#define SIZE_FROM_HASH(hash) ((hash >> 32) & ((1 << 24) - 1)) - -#define INIT_BLOCK_COUNT (128) -#define SCRATCH_SIZE (8192) - -typedef struct { - sqfs_u64 offset; - sqfs_u64 hash; -} blk_info_t; - -typedef struct { - sqfs_block_writer_t base; - sqfs_file_t *file; - - array_t blocks; - size_t devblksz; - - size_t file_start; - - sqfs_u32 flags; - - sqfs_u8 scratch[]; -} block_writer_default_t; - -static int store_block_location(block_writer_default_t *wr, sqfs_u64 offset, - sqfs_u32 size, sqfs_u32 chksum) -{ - blk_info_t info = { offset, MK_BLK_HASH(chksum, size) }; - - return array_append(&(wr->blocks), &info); -} - -static int deduplicate_blocks(block_writer_default_t *wr, sqfs_u32 flags, sqfs_u64 *out) -{ - const blk_info_t *blocks = wr->blocks.data; - sqfs_u64 loc_a, loc_b, sz; - size_t i, j, count; - int ret; - - count = wr->blocks.used - wr->file_start; - if (count == 0) { - *out = 0; - return 0; - } - - if (flags & SQFS_BLK_DONT_DEDUPLICATE) { - *out = blocks[wr->file_start].offset; - return 0; - } - - sz = 0; - loc_a = blocks[wr->file_start].offset; - - for (i = 0; i < count; ++i) - sz += SIZE_FROM_HASH(blocks[wr->file_start + i].hash); - - for (i = 0; i < wr->file_start; ++i) { - for (j = 0; j < count; ++j) { - if (blocks[i + j].hash == 0) - break; - - if (blocks[i + j].hash != - blocks[wr->file_start + j].hash) - break; - } - - if (j != count) - continue; - - if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) - break; - - loc_b = blocks[i].offset; - - ret = check_file_range_equal(wr->file, wr->scratch, - SCRATCH_SIZE, loc_a, loc_b, sz); - if (ret == 0) - break; - if (ret < 0) - return ret; - } - - *out = blocks[i].offset; - if (i >= wr->file_start) - return 0; - - if (count >= (wr->file_start - i)) { - wr->blocks.used = i + count; - } else { - wr->blocks.used = wr->file_start; - } - - sz = blocks[wr->blocks.used - 1].offset + - SIZE_FROM_HASH(blocks[wr->blocks.used - 1].hash); - - return wr->file->truncate(wr->file, sz); -} - -static int align_file(block_writer_default_t *wr, sqfs_u32 flags) -{ - void *padding; - sqfs_u64 size; - size_t diff; - int ret; - - if (!(flags & SQFS_BLK_ALIGN)) - return 0; - - size = wr->file->get_size(wr->file); - diff = size % wr->devblksz; - if (diff == 0) - return 0; - - padding = calloc(1, diff); - if (padding == 0) - return SQFS_ERROR_ALLOC; - - ret = wr->file->write_at(wr->file, size, padding, diff); - free(padding); - if (ret) - return ret; - - return store_block_location(wr, size, 0, 0); -} - -static void block_writer_destroy(sqfs_object_t *wr) -{ - sqfs_drop(((block_writer_default_t *)wr)->file); - array_cleanup(&(((block_writer_default_t *)wr)->blocks)); - free(wr); -} - -static int write_data_block(sqfs_block_writer_t *base, void *user, - sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, - const sqfs_u8 *data, sqfs_u64 *location) -{ - block_writer_default_t *wr = (block_writer_default_t *)base; - int err; - (void)user; - - if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { - err = align_file(wr, flags); - if (err) - return err; - - if (flags & SQFS_BLK_FIRST_BLOCK) - wr->file_start = wr->blocks.used; - } - - *location = wr->file->get_size(wr->file); - - if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { - sqfs_u32 out = size; - if (!(flags & SQFS_BLK_IS_COMPRESSED)) - out |= 1 << 24; - - err = store_block_location(wr, *location, out, checksum); - if (err) - return err; - - err = wr->file->write_at(wr->file, *location, data, size); - if (err) - return err; - } - - if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { - err = align_file(wr, flags); - if (err) - return err; - - if (flags & SQFS_BLK_LAST_BLOCK) - return deduplicate_blocks(wr, flags, location); - } - - return 0; -} - -static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) -{ - return ((const block_writer_default_t *)wr)->blocks.used; -} - -sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, - size_t devblksz, sqfs_u32 flags) -{ - block_writer_default_t *wr; - - if (flags & ~SQFS_BLOCK_WRITER_ALL_FLAGS) - return NULL; - - if (flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) { - wr = calloc(1, sizeof(*wr)); - } else { - wr = alloc_flex(sizeof(*wr), 1, SCRATCH_SIZE); - } - - if (wr == NULL) - return NULL; - - sqfs_object_init(wr, block_writer_destroy, NULL); - - ((sqfs_block_writer_t *)wr)->write_data_block = write_data_block; - ((sqfs_block_writer_t *)wr)->get_block_count = get_block_count; - wr->flags = flags; - wr->file = sqfs_grab(file); - wr->devblksz = devblksz; - - if (array_init(&(wr->blocks), sizeof(blk_info_t), INIT_BLOCK_COUNT)) { - sqfs_drop(wr->file); - free(wr); - return NULL; - } - - return (sqfs_block_writer_t *)wr; -} diff --git a/lib/sqfs/comp/compressor.c b/lib/sqfs/comp/compressor.c deleted file mode 100644 index 4c4d73c..0000000 --- a/lib/sqfs/comp/compressor.c +++ /dev/null @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * compressor.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include - -#include "internal.h" - -typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { -#ifdef WITH_GZIP - [SQFS_COMP_GZIP] = gzip_compressor_create, -#endif -#ifdef WITH_XZ - [SQFS_COMP_XZ] = xz_compressor_create, - [SQFS_COMP_LZMA] = lzma_compressor_create, -#endif -#ifdef WITH_LZ4 - [SQFS_COMP_LZ4] = lz4_compressor_create, -#endif -#ifdef WITH_ZSTD - [SQFS_COMP_ZSTD] = zstd_compressor_create, -#endif -}; - -static const char *names[] = { - [SQFS_COMP_GZIP] = "gzip", - [SQFS_COMP_LZMA] = "lzma", - [SQFS_COMP_LZO] = "lzo", - [SQFS_COMP_XZ] = "xz", - [SQFS_COMP_LZ4] = "lz4", - [SQFS_COMP_ZSTD] = "zstd", -}; - -int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) -{ - sqfs_u8 buffer[64]; - sqfs_u16 header; - int ret; - - /* XXX: options for all known compressors should fit into this */ - if (size >= (sizeof(buffer) - sizeof(header))) - return SQFS_ERROR_INTERNAL; - - header = htole16(0x8000 | size); - memcpy(buffer, &header, sizeof(header)); - memcpy(buffer + sizeof(header), data, size); - - ret = file->write_at(file, sizeof(sqfs_super_t), - buffer, sizeof(header) + size); - if (ret) - return ret; - - return sizeof(header) + size; -} - -int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) -{ - sqfs_u8 buffer[64]; - sqfs_u16 header; - int ret; - - /* XXX: options for all known compressors should fit into this */ - if (size >= (sizeof(buffer) - sizeof(header))) - return SQFS_ERROR_INTERNAL; - - ret = file->read_at(file, sizeof(sqfs_super_t), - buffer, sizeof(header) + size); - if (ret) - return ret; - - memcpy(&header, buffer, sizeof(header)); - - if (le16toh(header) != (0x8000 | size)) - return SQFS_ERROR_CORRUPTED; - - memcpy(data, buffer + 2, size); - return 0; -} - -int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_u8 padd0[sizeof(cfg->opt)]; - int ret; - - *out = NULL; - - /* check compressor ID */ - if (cfg == NULL) - return SQFS_ERROR_ARG_INVALID; - - if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) - return SQFS_ERROR_UNSUPPORTED; - - if (compressors[cfg->id] == NULL) - return SQFS_ERROR_UNSUPPORTED; - - /* make sure the padding bytes are cleared, so we could theoretically - turn them into option fields in the future and remain compatible */ - memset(padd0, 0, sizeof(padd0)); - - switch (cfg->id) { - case SQFS_COMP_XZ: - ret = memcmp(cfg->opt.xz.padd0, padd0, - sizeof(cfg->opt.xz.padd0)); - break; - case SQFS_COMP_LZMA: - ret = memcmp(cfg->opt.lzma.padd0, padd0, - sizeof(cfg->opt.lzma.padd0)); - break; - case SQFS_COMP_LZO: - ret = memcmp(cfg->opt.lzo.padd0, padd0, - sizeof(cfg->opt.lzo.padd0)); - break; - case SQFS_COMP_GZIP: - ret = memcmp(cfg->opt.gzip.padd0, padd0, - sizeof(cfg->opt.gzip.padd0)); - break; - default: - ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); - break; - } - - if (ret != 0) - return SQFS_ERROR_ARG_INVALID; - - return compressors[cfg->id](cfg, out); -} - -const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) -{ - if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) - return NULL; - - return names[id]; -} - -int sqfs_compressor_id_from_name(const char *name) -{ - size_t i; - - for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { - if (names[i] != NULL && strcmp(names[i], name) == 0) - return i; - } - - return SQFS_ERROR_UNSUPPORTED; -} - -int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, - SQFS_COMPRESSOR id, - size_t block_size, sqfs_u16 flags) -{ - sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; - - memset(cfg, 0, sizeof(*cfg)); - - switch (id) { - case SQFS_COMP_GZIP: - flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; - cfg->level = SQFS_GZIP_DEFAULT_LEVEL; - cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; - break; - case SQFS_COMP_LZO: - cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; - cfg->level = SQFS_LZO_DEFAULT_LEVEL; - break; - case SQFS_COMP_ZSTD: - cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; - break; - case SQFS_COMP_XZ: - flag_mask |= SQFS_COMP_FLAG_XZ_ALL; - cfg->level = SQFS_XZ_DEFAULT_LEVEL; - cfg->opt.xz.dict_size = block_size; - cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; - cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; - cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; - - if (block_size < SQFS_XZ_MIN_DICT_SIZE) - cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; - break; - case SQFS_COMP_LZMA: - flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; - cfg->level = SQFS_LZMA_DEFAULT_LEVEL; - cfg->opt.lzma.dict_size = block_size; - cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; - cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; - cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; - - if (block_size < SQFS_LZMA_MIN_DICT_SIZE) - cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; - break; - case SQFS_COMP_LZ4: - flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; - break; - default: - return SQFS_ERROR_UNSUPPORTED; - } - - if (flags & ~flag_mask) { - memset(cfg, 0, sizeof(*cfg)); - return SQFS_ERROR_UNSUPPORTED; - } - - cfg->id = id; - cfg->flags = flags; - cfg->block_size = block_size; - return 0; -} diff --git a/lib/sqfs/comp/gzip.c b/lib/sqfs/comp/gzip.c deleted file mode 100644 index beacfb8..0000000 --- a/lib/sqfs/comp/gzip.c +++ /dev/null @@ -1,307 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * gzip.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_u32 level; - sqfs_u16 window; - sqfs_u16 strategies; -} gzip_options_t; - -typedef struct { - sqfs_compressor_t base; - - z_stream strm; - bool compress; - - size_t block_size; - gzip_options_t opt; -} gzip_compressor_t; - -static void gzip_destroy(sqfs_object_t *base) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - - if (gzip->compress) { - deflateEnd(&gzip->strm); - } else { - inflateEnd(&gzip->strm); - } - - free(gzip); -} - -static void gzip_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_GZIP; - cfg->flags = gzip->opt.strategies; - cfg->block_size = gzip->block_size; - cfg->level = gzip->opt.level; - cfg->opt.gzip.window_size = gzip->opt.window; - - if (!gzip->compress) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - gzip_options_t opt; - - if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && - gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && - gzip->opt.strategies == 0) { - return 0; - } - - opt.level = htole32(gzip->opt.level); - opt.window = htole16(gzip->opt.window); - opt.strategies = htole16(gzip->opt.strategies); - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - gzip_options_t opt; - int ret; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - gzip->opt.level = le32toh(opt.level); - gzip->opt.window = le16toh(opt.window); - gzip->opt.strategies = le16toh(opt.strategies); - - if (gzip->opt.level < 1 || gzip->opt.level > 9) - return SQFS_ERROR_UNSUPPORTED; - - if (gzip->opt.window < 8 || gzip->opt.window > 15) - return SQFS_ERROR_UNSUPPORTED; - - if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) - return SQFS_ERROR_UNSUPPORTED; - - return 0; -} - -static int flag_to_zlib_strategy(int flag) -{ - switch (flag) { - case SQFS_COMP_FLAG_GZIP_DEFAULT: - return Z_DEFAULT_STRATEGY; - case SQFS_COMP_FLAG_GZIP_FILTERED: - return Z_FILTERED; - case SQFS_COMP_FLAG_GZIP_HUFFMAN: - return Z_HUFFMAN_ONLY; - case SQFS_COMP_FLAG_GZIP_RLE: - return Z_RLE; - case SQFS_COMP_FLAG_GZIP_FIXED: - return Z_FIXED; - default: - break; - } - - return 0; -} - -static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - int ret, strategy, selected = Z_DEFAULT_STRATEGY; - size_t i, length, minlength = 0; - - for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { - if ((gzip->opt.strategies & i) == 0) - continue; - - ret = deflateReset(&gzip->strm); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - strategy = flag_to_zlib_strategy(i); - - gzip->strm.next_in = (z_const Bytef *)in; - gzip->strm.avail_in = size; - gzip->strm.next_out = out; - gzip->strm.avail_out = outsize; - - ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - ret = deflate(&gzip->strm, Z_FINISH); - - if (ret == Z_STREAM_END) { - length = gzip->strm.total_out; - - if (minlength == 0 || length < minlength) { - minlength = length; - selected = strategy; - } - } else if (ret != Z_OK && ret != Z_BUF_ERROR) { - return SQFS_ERROR_COMPRESSOR; - } - } - - return selected; -} - -static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - gzip_compressor_t *gzip = (gzip_compressor_t *)base; - int ret, strategy = 0; - size_t written; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (gzip->compress && gzip->opt.strategies != 0) { - strategy = find_strategy(gzip, in, size, out, outsize); - if (strategy < 0) - return strategy; - } - - if (gzip->compress) { - ret = deflateReset(&gzip->strm); - } else { - ret = inflateReset(&gzip->strm); - } - - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - - gzip->strm.next_in = (const void *)in; - gzip->strm.avail_in = size; - gzip->strm.next_out = out; - gzip->strm.avail_out = outsize; - - if (gzip->compress && gzip->opt.strategies != 0) { - ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); - if (ret != Z_OK) - return SQFS_ERROR_COMPRESSOR; - } - - if (gzip->compress) { - ret = deflate(&gzip->strm, Z_FINISH); - } else { - ret = inflate(&gzip->strm, Z_FINISH); - } - - if (ret == Z_STREAM_END) { - written = gzip->strm.total_out; - - if (gzip->compress && written >= size) - return 0; - - return written; - } - - if (ret != Z_OK && ret != Z_BUF_ERROR) - return SQFS_ERROR_COMPRESSOR; - - return 0; -} - -static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) -{ - gzip_compressor_t *gzip = malloc(sizeof(*gzip)); - int ret; - - if (gzip == NULL) - return NULL; - - memcpy(gzip, cmp, sizeof(*gzip)); - memset(&gzip->strm, 0, sizeof(gzip->strm)); - - if (gzip->compress) { - ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, - gzip->opt.window, 8, Z_DEFAULT_STRATEGY); - } else { - ret = inflateInit(&gzip->strm); - } - - if (ret != Z_OK) { - free(gzip); - return NULL; - } - - return (sqfs_object_t *)gzip; -} - -int gzip_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - gzip_compressor_t *gzip; - sqfs_compressor_t *base; - int ret; - - if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | - SQFS_COMP_FLAG_GENERIC_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->level < SQFS_GZIP_MIN_LEVEL || - cfg->level > SQFS_GZIP_MAX_LEVEL) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || - cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { - return SQFS_ERROR_UNSUPPORTED; - } - - gzip = calloc(1, sizeof(*gzip)); - base = (sqfs_compressor_t *)gzip; - - if (gzip == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(gzip, gzip_destroy, gzip_create_copy); - - gzip->opt.level = cfg->level; - gzip->opt.window = cfg->opt.gzip.window_size; - gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; - gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; - gzip->block_size = cfg->block_size; - base->get_configuration = gzip_get_configuration; - base->do_block = gzip_do_block; - base->write_options = gzip_write_options; - base->read_options = gzip_read_options; - - if (gzip->compress) { - ret = deflateInit2(&gzip->strm, cfg->level, - Z_DEFLATED, cfg->opt.gzip.window_size, 8, - Z_DEFAULT_STRATEGY); - } else { - ret = inflateInit(&gzip->strm); - } - - if (ret != Z_OK) { - free(gzip); - return SQFS_ERROR_COMPRESSOR; - } - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/internal.h b/lib/sqfs/comp/internal.h deleted file mode 100644 index e4c3dd8..0000000 --- a/lib/sqfs/comp/internal.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef INTERNAL_H -#define INTERNAL_H - -#include "config.h" - -#include "sqfs/predef.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -SQFS_INTERNAL -int sqfs_generic_write_options(sqfs_file_t *file, const void *data, - size_t size); - -SQFS_INTERNAL -int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); - -SQFS_INTERNAL -int xz_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int gzip_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int lz4_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int zstd_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -SQFS_INTERNAL -int lzma_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out); - -#endif /* INTERNAL_H */ diff --git a/lib/sqfs/comp/lz4.c b/lib/sqfs/comp/lz4.c deleted file mode 100644 index 77f4a6e..0000000 --- a/lib/sqfs/comp/lz4.c +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * lz4.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include - -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - bool high_compression; -} lz4_compressor_t; - -typedef struct { - sqfs_u32 version; - sqfs_u32 flags; -} lz4_options; - -#define LZ4LEGACY 1 - -/* old verions of liblz4 don't have this */ -#ifndef LZ4HC_CLEVEL_MAX -#define LZ4HC_CLEVEL_MAX 12 -#endif - -static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lz4_compressor_t *lz4 = (lz4_compressor_t *)base; - lz4_options opt = { - .version = htole32(LZ4LEGACY), - .flags = htole32(lz4->high_compression ? - SQFS_COMP_FLAG_LZ4_HC : 0), - }; - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - lz4_options opt; - int ret; - (void)base; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.version = le32toh(opt.version); - opt.flags = le32toh(opt.flags); - - if (opt.version != LZ4LEGACY) - return SQFS_ERROR_UNSUPPORTED; - - return 0; -} - -static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lz4_compressor_t *lz4 = (lz4_compressor_t *)base; - int ret; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (lz4->high_compression) { - ret = LZ4_compress_HC((const void *)in, (void *)out, - size, outsize, LZ4HC_CLEVEL_MAX); - } else { - ret = LZ4_compress_default((const void *)in, (void *)out, - size, outsize); - } - - if (ret < 0) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - int ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); - - if (ret < 0) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static void lz4_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_LZ4; - cfg->block_size = lz4->block_size; - - if (lz4->high_compression) - cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; - - if (base->do_block == lz4_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) -{ - lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); - - if (lz4 == NULL) - return NULL; - - memcpy(lz4, cmp, sizeof(*lz4)); - return (sqfs_object_t *)lz4; -} - -static void lz4_destroy(sqfs_object_t *base) -{ - free(base); -} - -int lz4_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - lz4_compressor_t *lz4; - - if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | - SQFS_COMP_FLAG_GENERIC_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (cfg->level != 0) - return SQFS_ERROR_UNSUPPORTED; - - lz4 = calloc(1, sizeof(*lz4)); - base = (sqfs_compressor_t *)lz4; - if (lz4 == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(lz4, lz4_destroy, lz4_create_copy); - - lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; - lz4->block_size = cfg->block_size; - - base->get_configuration = lz4_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - lz4_uncomp_block : lz4_comp_block; - base->write_options = lz4_write_options; - base->read_options = lz4_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/lzma.c b/lib/sqfs/comp/lzma.c deleted file mode 100644 index 5456603..0000000 --- a/lib/sqfs/comp/lzma.c +++ /dev/null @@ -1,281 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * lzma.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include - -#include "internal.h" - -#define LZMA_SIZE_OFFSET (5) -#define LZMA_SIZE_BYTES (8) -#define LZMA_HEADER_SIZE (13) - -#define MEMLIMIT (64 * 1024 * 1024) - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - size_t dict_size; - - sqfs_u32 flags; - sqfs_u8 level; - sqfs_u8 lc; - sqfs_u8 lp; - sqfs_u8 pb; -} lzma_compressor_t; - -static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - (void)base; (void)file; - return 0; -} - -static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - (void)base; - (void)file; - return SQFS_ERROR_UNSUPPORTED; -} - -static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, - const sqfs_u8 *in, size_t size, - sqfs_u8 *out, size_t outsize) -{ - lzma_stream strm = LZMA_STREAM_INIT; - lzma_options_lzma opt; - int ret; - - lzma_lzma_preset(&opt, preset); - opt.dict_size = lzma->block_size; - opt.lc = lzma->lc; - opt.lp = lzma->lp; - opt.pb = lzma->pb; - - if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - strm.next_out = out; - strm.avail_out = outsize; - strm.next_in = in; - strm.avail_in = size; - - ret = lzma_code(&strm, LZMA_FINISH); - lzma_end(&strm); - - if (ret != LZMA_STREAM_END) - return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; - - if (strm.total_out > size) - return 0; - - out[LZMA_SIZE_OFFSET ] = size & 0xFF; - out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; - out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; - out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; - out[LZMA_SIZE_OFFSET + 4] = 0; - out[LZMA_SIZE_OFFSET + 5] = 0; - out[LZMA_SIZE_OFFSET + 6] = 0; - out[LZMA_SIZE_OFFSET + 7] = 0; - return strm.total_out; -} - -static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - lzma_compressor_t *lzma = (lzma_compressor_t *)base; - sqfs_s32 ret, smallest; - sqfs_u32 preset; - - if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - preset = lzma->level; - ret = try_compress(lzma, preset, in, size, out, outsize); - if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) - return ret; - - preset |= LZMA_PRESET_EXTREME; - smallest = ret; - - ret = try_compress(lzma, preset, in, size, out, outsize); - if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) - return ret; - - preset &= ~LZMA_PRESET_EXTREME; - return smallest == 0 ? 0 : - try_compress(lzma, preset, in, size, out, outsize); -} - -static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; - lzma_stream strm = LZMA_STREAM_INIT; - size_t hdrsize; - int ret; - (void)base; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - if (size < sizeof(lzma_header)) - return SQFS_ERROR_CORRUPTED; - - hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | - ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | - ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | - ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); - - if (hdrsize > outsize) - return 0; - - if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - memcpy(lzma_header, in, sizeof(lzma_header)); - memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); - - strm.next_out = out; - strm.avail_out = outsize; - strm.next_in = lzma_header; - strm.avail_in = sizeof(lzma_header); - - ret = lzma_code(&strm, LZMA_RUN); - - if (ret != LZMA_OK || strm.avail_in != 0) { - lzma_end(&strm); - return SQFS_ERROR_COMPRESSOR; - } - - strm.next_in = in + sizeof(lzma_header); - strm.avail_in = size - sizeof(lzma_header); - - ret = lzma_code(&strm, LZMA_FINISH); - lzma_end(&strm); - - if (ret != LZMA_STREAM_END && ret != LZMA_OK) - return SQFS_ERROR_COMPRESSOR; - - if (ret == LZMA_OK) { - if (strm.total_out < hdrsize || strm.avail_in != 0) - return 0; - } - - return hdrsize; -} - -static void lzma_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_LZMA; - cfg->block_size = lzma->block_size; - cfg->flags = lzma->flags; - cfg->level = lzma->level; - cfg->opt.lzma.dict_size = lzma->dict_size; - cfg->opt.lzma.lc = lzma->lc; - cfg->opt.lzma.lp = lzma->lp; - cfg->opt.lzma.pb = lzma->pb; -} - -static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) -{ - lzma_compressor_t *copy = malloc(sizeof(*copy)); - - if (copy != NULL) - memcpy(copy, cmp, sizeof(*copy)); - - return (sqfs_object_t *)copy; -} - -static void lzma_destroy(sqfs_object_t *base) -{ - free(base); -} - -int lzma_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - lzma_compressor_t *lzma; - sqfs_u32 mask; - - mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; - - if (cfg->flags & ~mask) - return SQFS_ERROR_UNSUPPORTED; - - /* XXX: values are unsigned and minimum is 0 */ - if (cfg->level > SQFS_LZMA_MAX_LEVEL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size == 0) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - mask = cfg->opt.lzma.dict_size; - mask &= mask - 1; - - if (mask != 0) { - if ((mask & (mask - 1)) != 0) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) - return SQFS_ERROR_UNSUPPORTED; - } - - lzma = calloc(1, sizeof(*lzma)); - base = (sqfs_compressor_t *)lzma; - if (lzma == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(lzma, lzma_destroy, lzma_create_copy); - - lzma->block_size = cfg->block_size; - lzma->flags = cfg->flags; - lzma->level = cfg->level; - lzma->dict_size = cfg->opt.lzma.dict_size; - lzma->lc = cfg->opt.lzma.lc; - lzma->lp = cfg->opt.lzma.lp; - lzma->pb = cfg->opt.lzma.pb; - - base->get_configuration = lzma_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - lzma_uncomp_block : lzma_comp_block; - base->write_options = lzma_write_options; - base->read_options = lzma_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/xz.c b/lib/sqfs/comp/xz.c deleted file mode 100644 index 13545ed..0000000 --- a/lib/sqfs/comp/xz.c +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xz.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - size_t dict_size; - - sqfs_u8 level; - sqfs_u8 lc; - sqfs_u8 lp; - sqfs_u8 pb; - - int flags; -} xz_compressor_t; - -typedef struct { - sqfs_u32 dict_size; - sqfs_u32 flags; -} xz_options_t; - -static bool is_dict_size_valid(size_t size) -{ - size_t x = size & (size - 1); - - if (x == 0) - return true; - - return size == (x | (x >> 1)); -} - -static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - xz_options_t opt; - sqfs_u32 flags; - - if (xz->flags == 0 && xz->dict_size == xz->block_size) - return 0; - - flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; - flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; - - opt.dict_size = htole32(xz->dict_size); - opt.flags = htole32(flags); - - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - xz_options_t opt; - int ret; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.dict_size = le32toh(opt.dict_size); - opt.flags = le32toh(opt.flags); - - if (!is_dict_size_valid(opt.dict_size)) - return SQFS_ERROR_CORRUPTED; - - if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) - return SQFS_ERROR_UNSUPPORTED; - - xz->flags = opt.flags; - xz->dict_size = opt.dict_size; - return 0; -} - -static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, - const sqfs_u8 *in, sqfs_u32 size, - sqfs_u8 *out, sqfs_u32 outsize, - sqfs_u32 presets) -{ - lzma_filter filters[5]; - lzma_options_lzma opt; - size_t written = 0; - lzma_ret ret; - int i = 0; - - if (lzma_lzma_preset(&opt, presets)) - return SQFS_ERROR_COMPRESSOR; - - opt.lc = xz->lc; - opt.lp = xz->lp; - opt.pb = xz->pb; - opt.dict_size = xz->dict_size; - - if (filter != LZMA_VLI_UNKNOWN) { - filters[i].id = filter; - filters[i].options = NULL; - ++i; - } - - filters[i].id = LZMA_FILTER_LZMA2; - filters[i].options = &opt; - ++i; - - filters[i].id = LZMA_VLI_UNKNOWN; - filters[i].options = NULL; - ++i; - - ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, - in, size, out, &written, outsize); - - if (ret == LZMA_OK) - return (written >= size) ? 0 : written; - - if (ret != LZMA_BUF_ERROR) - return SQFS_ERROR_COMPRESSOR; - - return 0; -} - -static lzma_vli flag_to_vli(int flag) -{ - switch (flag) { - case SQFS_COMP_FLAG_XZ_X86: - return LZMA_FILTER_X86; - case SQFS_COMP_FLAG_XZ_POWERPC: - return LZMA_FILTER_POWERPC; - case SQFS_COMP_FLAG_XZ_IA64: - return LZMA_FILTER_IA64; - case SQFS_COMP_FLAG_XZ_ARM: - return LZMA_FILTER_ARM; - case SQFS_COMP_FLAG_XZ_ARMTHUMB: - return LZMA_FILTER_ARMTHUMB; - case SQFS_COMP_FLAG_XZ_SPARC: - return LZMA_FILTER_SPARC; - default: - break; - } - - return LZMA_VLI_UNKNOWN; -} - -static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - xz_compressor_t *xz = (xz_compressor_t *)base; - lzma_vli filter, selected = LZMA_VLI_UNKNOWN; - sqfs_s32 ret, smallest; - bool extreme; - size_t i; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, - outsize, xz->level); - if (ret < 0 || xz->flags == 0) - return ret; - - smallest = ret; - extreme = false; - - if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { - ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, - xz->level | LZMA_PRESET_EXTREME); - - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - extreme = true; - } - } - - for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { - if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) - continue; - - filter = flag_to_vli(i); - - ret = compress(xz, filter, in, size, out, outsize, xz->level); - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - selected = filter; - extreme = false; - } - - if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { - ret = compress(xz, filter, in, size, out, outsize, - xz->level | LZMA_PRESET_EXTREME); - - if (ret > 0 && (smallest == 0 || ret < smallest)) { - smallest = ret; - selected = filter; - extreme = true; - } - } - } - - if (smallest == 0) - return 0; - - return compress(xz, selected, in, size, out, outsize, - xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); -} - -static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - sqfs_u64 memlimit = 65 * 1024 * 1024; - size_t dest_pos = 0; - size_t src_pos = 0; - lzma_ret ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, - in, &src_pos, size, - out, &dest_pos, outsize); - - if (ret == LZMA_OK && size == src_pos) - return dest_pos; - - return SQFS_ERROR_COMPRESSOR; -} - -static void xz_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const xz_compressor_t *xz = (const xz_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_XZ; - cfg->flags = xz->flags; - cfg->block_size = xz->block_size; - cfg->level = xz->level; - cfg->opt.xz.dict_size = xz->dict_size; - cfg->opt.xz.lc = xz->lc; - cfg->opt.xz.lp = xz->lp; - cfg->opt.xz.pb = xz->pb; - - if (base->do_block == xz_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) -{ - xz_compressor_t *xz = malloc(sizeof(*xz)); - - if (xz == NULL) - return NULL; - - memcpy(xz, cmp, sizeof(*xz)); - return (sqfs_object_t *)xz; -} - -static void xz_destroy(sqfs_object_t *base) -{ - free(base); -} - -int xz_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - sqfs_compressor_t *base; - xz_compressor_t *xz; - - if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | - SQFS_COMP_FLAG_XZ_ALL)) { - return SQFS_ERROR_UNSUPPORTED; - } - - if (!is_dict_size_valid(cfg->opt.xz.dict_size)) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->level > SQFS_XZ_MAX_LEVEL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) - return SQFS_ERROR_UNSUPPORTED; - - xz = calloc(1, sizeof(*xz)); - base = (sqfs_compressor_t *)xz; - if (xz == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(xz, xz_destroy, xz_create_copy); - - xz->flags = cfg->flags; - xz->dict_size = cfg->opt.xz.dict_size; - xz->block_size = cfg->block_size; - xz->lc = cfg->opt.xz.lc; - xz->lp = cfg->opt.xz.lp; - xz->pb = cfg->opt.xz.pb; - xz->level = cfg->level; - base->get_configuration = xz_get_configuration; - base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? - xz_uncomp_block : xz_comp_block; - base->write_options = xz_write_options; - base->read_options = xz_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/comp/zstd.c b/lib/sqfs/comp/zstd.c deleted file mode 100644 index a6d7975..0000000 --- a/lib/sqfs/comp/zstd.c +++ /dev/null @@ -1,172 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * zstd.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include -#include -#include - -#include -#include - -#include "internal.h" - -typedef struct { - sqfs_compressor_t base; - size_t block_size; - ZSTD_CCtx *zctx; - int level; -} zstd_compressor_t; - -typedef struct { - sqfs_u32 level; -} zstd_options_t; - -static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - zstd_options_t opt; - - if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) - return 0; - - opt.level = htole32(zstd->level); - return sqfs_generic_write_options(file, &opt, sizeof(opt)); -} - -static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) -{ - zstd_options_t opt; - int ret; - (void)base; - - ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); - if (ret) - return ret; - - opt.level = le32toh(opt.level); - return 0; -} - -static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - size_t ret; - - if (size >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, - zstd->level); - - if (ZSTD_isError(ret)) { - if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) - return 0; - - return SQFS_ERROR_COMPRESSOR; - } - - return ret < size ? ret : 0; -} - -static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, - sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) -{ - size_t ret; - (void)base; - - if (outsize >= 0x7FFFFFFF) - return SQFS_ERROR_ARG_INVALID; - - ret = ZSTD_decompress(out, outsize, in, size); - - if (ZSTD_isError(ret)) - return SQFS_ERROR_COMPRESSOR; - - return ret; -} - -static void zstd_get_configuration(const sqfs_compressor_t *base, - sqfs_compressor_config_t *cfg) -{ - const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; - - memset(cfg, 0, sizeof(*cfg)); - cfg->id = SQFS_COMP_ZSTD; - - cfg->block_size = zstd->block_size; - cfg->level = zstd->level; - - if (base->do_block == zstd_uncomp_block) - cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; -} - -static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) -{ - zstd_compressor_t *zstd = malloc(sizeof(*zstd)); - - if (zstd == NULL) - return NULL; - - memcpy(zstd, cmp, sizeof(*zstd)); - - zstd->zctx = ZSTD_createCCtx(); - - if (zstd->zctx == NULL) { - free(zstd); - return NULL; - } - - return (sqfs_object_t *)zstd; -} - -static void zstd_destroy(sqfs_object_t *base) -{ - zstd_compressor_t *zstd = (zstd_compressor_t *)base; - - ZSTD_freeCCtx(zstd->zctx); - free(zstd); -} - -int zstd_compressor_create(const sqfs_compressor_config_t *cfg, - sqfs_compressor_t **out) -{ - zstd_compressor_t *zstd; - sqfs_compressor_t *base; - - if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) - return SQFS_ERROR_UNSUPPORTED; - - if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) - return SQFS_ERROR_UNSUPPORTED; - - zstd = calloc(1, sizeof(*zstd)); - base = (sqfs_compressor_t *)zstd; - if (zstd == NULL) - return SQFS_ERROR_ALLOC; - - sqfs_object_init(zstd, zstd_destroy, zstd_create_copy); - - zstd->block_size = cfg->block_size; - zstd->level = cfg->level; - zstd->zctx = ZSTD_createCCtx(); - if (zstd->zctx == NULL) { - free(zstd); - return SQFS_ERROR_COMPRESSOR; - } - - base->get_configuration = zstd_get_configuration; - base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? - zstd_uncomp_block : zstd_comp_block; - base->write_options = zstd_write_options; - base->read_options = zstd_read_options; - - *out = base; - return 0; -} diff --git a/lib/sqfs/data_reader.c b/lib/sqfs/data_reader.c deleted file mode 100644 index 3f0cd74..0000000 --- a/lib/sqfs/data_reader.c +++ /dev/null @@ -1,374 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * data_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/data_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/frag_table.h" -#include "sqfs/block.h" -#include "sqfs/error.h" -#include "sqfs/table.h" -#include "sqfs/inode.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -struct sqfs_data_reader_t { - sqfs_object_t obj; - - sqfs_frag_table_t *frag_tbl; - sqfs_compressor_t *cmp; - sqfs_file_t *file; - - sqfs_u8 *data_block; - size_t data_blk_size; - sqfs_u64 current_block; - - sqfs_u8 *frag_block; - size_t frag_blk_size; - sqfs_u32 current_frag_index; - sqfs_u32 block_size; - - sqfs_u8 scratch[]; -}; - -static int get_block(sqfs_data_reader_t *data, sqfs_u64 off, sqfs_u32 size, - sqfs_u32 max_size, size_t *out_sz, sqfs_u8 **out) -{ - sqfs_u32 on_disk_size; - sqfs_s32 ret; - int err; - - *out = alloc_array(1, max_size); - *out_sz = max_size; - - if (*out == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail; - } - - if (SQFS_IS_SPARSE_BLOCK(size)) - return 0; - - on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); - - if (on_disk_size > max_size) { - err = SQFS_ERROR_OVERFLOW; - goto fail; - } - - if (SQFS_IS_BLOCK_COMPRESSED(size)) { - err = data->file->read_at(data->file, off, - data->scratch, on_disk_size); - if (err) - goto fail; - - ret = data->cmp->do_block(data->cmp, data->scratch, - on_disk_size, *out, max_size); - if (ret <= 0) { - err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; - goto fail; - } - - *out_sz = ret; - } else { - err = data->file->read_at(data->file, off, - *out, on_disk_size); - if (err) - goto fail; - - *out_sz = on_disk_size; - } - - return 0; -fail: - free(*out); - *out = NULL; - *out_sz = 0; - return err; -} - -static int precache_data_block(sqfs_data_reader_t *data, sqfs_u64 location, - sqfs_u32 size) -{ - if (data->data_block != NULL && data->current_block == location) - return 0; - - free(data->data_block); - data->current_block = location; - - return get_block(data, location, size, data->block_size, - &data->data_blk_size, &data->data_block); -} - -static int precache_fragment_block(sqfs_data_reader_t *data, size_t idx) -{ - sqfs_fragment_t ent; - int ret; - - if (data->frag_block != NULL && idx == data->current_frag_index) - return 0; - - ret = sqfs_frag_table_lookup(data->frag_tbl, idx, &ent); - if (ret != 0) - return ret; - - free(data->frag_block); - data->current_frag_index = idx; - - return get_block(data, ent.start_offset, ent.size, data->block_size, - &data->frag_blk_size, &data->frag_block); -} - -static void data_reader_destroy(sqfs_object_t *obj) -{ - sqfs_data_reader_t *data = (sqfs_data_reader_t *)obj; - - sqfs_drop(data->cmp); - sqfs_drop(data->file); - sqfs_drop(data->frag_tbl); - free(data->data_block); - free(data->frag_block); - free(data); -} - -static sqfs_object_t *data_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_data_reader_t *data = (const sqfs_data_reader_t *)obj; - sqfs_data_reader_t *copy; - - copy = alloc_flex(sizeof(*data), 1, data->block_size); - if (copy == NULL) - return NULL; - - memcpy(copy, data, sizeof(*data) + data->block_size); - - copy->frag_tbl = sqfs_copy(data->frag_tbl); - if (copy->frag_tbl == NULL) - goto fail_ftbl; - - if (data->data_block != NULL) { - copy->data_block = malloc(data->data_blk_size); - if (copy->data_block == NULL) - goto fail_dblk; - - memcpy(copy->data_block, data->data_block, - data->data_blk_size); - } - - if (copy->frag_block != NULL) { - copy->frag_block = malloc(copy->frag_blk_size); - if (copy->frag_block == NULL) - goto fail_fblk; - - memcpy(copy->frag_block, data->frag_block, - data->frag_blk_size); - } - - /* duplicate references */ - copy->file = sqfs_grab(copy->file); - copy->cmp = sqfs_grab(copy->cmp); - return (sqfs_object_t *)copy; -fail_fblk: - free(copy->data_block); -fail_dblk: - sqfs_drop(copy->frag_tbl); -fail_ftbl: - free(copy); - return NULL; -} - -sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, - size_t block_size, - sqfs_compressor_t *cmp, - sqfs_u32 flags) -{ - sqfs_data_reader_t *data; - - if (flags != 0) - return NULL; - - data = alloc_flex(sizeof(*data), 1, block_size); - if (data == NULL) - return NULL; - - sqfs_object_init(data, data_reader_destroy, data_reader_copy); - - data->frag_tbl = sqfs_frag_table_create(0); - if (data->frag_tbl == NULL) { - free(data); - return NULL; - } - - data->file = sqfs_grab(file); - data->block_size = block_size; - data->cmp = sqfs_grab(cmp); - return data; -} - -int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, - const sqfs_super_t *super) -{ - int ret; - - free(data->frag_block); - data->frag_block = NULL; - data->current_frag_index = 0; - - ret = sqfs_frag_table_read(data->frag_tbl, data->file, - super, data->cmp); - if (ret != 0) - return ret; - - data->current_frag_index = sqfs_frag_table_get_size(data->frag_tbl); - return 0; -} - -int sqfs_data_reader_get_block(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - size_t index, size_t *size, sqfs_u8 **out) -{ - size_t i, unpacked_size; - sqfs_u64 off, filesz; - - sqfs_inode_get_file_block_start(inode, &off); - sqfs_inode_get_file_size(inode, &filesz); - - if (index >= sqfs_inode_get_file_block_count(inode)) - return SQFS_ERROR_OUT_OF_BOUNDS; - - for (i = 0; i < index; ++i) { - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - filesz -= data->block_size; - } - - unpacked_size = filesz < data->block_size ? filesz : data->block_size; - - return get_block(data, off, inode->extra[index], - unpacked_size, size, out); -} - -int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - size_t *size, sqfs_u8 **out) -{ - sqfs_u32 frag_idx, frag_off, frag_sz; - size_t block_count; - sqfs_u64 filesz; - int err; - - sqfs_inode_get_file_size(inode, &filesz); - sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); - *size = 0; - *out = NULL; - - block_count = sqfs_inode_get_file_block_count(inode); - - if (block_count > (UINT64_MAX / data->block_size)) - return SQFS_ERROR_OVERFLOW; - - if ((sqfs_u64)block_count * data->block_size >= filesz) - return 0; - - frag_sz = filesz % data->block_size; - - err = precache_fragment_block(data, frag_idx); - if (err) - return err; - - if (frag_off + frag_sz > data->block_size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - *out = alloc_array(1, frag_sz); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - *size = frag_sz; - memcpy(*out, (char *)data->frag_block + frag_off, frag_sz); - return 0; -} - -sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, - const sqfs_inode_generic_t *inode, - sqfs_u64 offset, void *buffer, sqfs_u32 size) -{ - sqfs_u32 frag_idx, frag_off, diff, total = 0; - size_t i, block_count; - sqfs_u64 off, filesz; - char *ptr; - int err; - - if (size >= 0x7FFFFFFF) - size = 0x7FFFFFFE; - - /* work out file location and size */ - sqfs_inode_get_file_size(inode, &filesz); - sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); - sqfs_inode_get_file_block_start(inode, &off); - block_count = sqfs_inode_get_file_block_count(inode); - - if (offset >= filesz) - return 0; - - if ((filesz - offset) < (sqfs_u64)size) - size = filesz - offset; - - if (size == 0) - return 0; - - /* find location of the first block */ - for (i = 0; offset > data->block_size && i < block_count; ++i) { - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - offset -= data->block_size; - } - - /* copy data from blocks */ - while (i < block_count && size > 0) { - diff = data->block_size - offset; - if (size < diff) - diff = size; - - if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { - memset(buffer, 0, diff); - } else { - err = precache_data_block(data, off, inode->extra[i]); - if (err) - return err; - - memcpy(buffer, (char *)data->data_block + offset, diff); - off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); - } - - ++i; - offset = 0; - size -= diff; - total += diff; - buffer = (char *)buffer + diff; - } - - /* copy from fragment */ - if (size > 0) { - err = precache_fragment_block(data, frag_idx); - if (err) - return err; - - if ((frag_off + offset) >= data->frag_blk_size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if ((data->frag_blk_size - (frag_off + offset)) < size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - ptr = (char *)data->frag_block + frag_off + offset; - memcpy(buffer, ptr, size); - total += size; - } - - return total; -} diff --git a/lib/sqfs/dir_reader/dir_reader.c b/lib/sqfs/dir_reader/dir_reader.c deleted file mode 100644 index d70f729..0000000 --- a/lib/sqfs/dir_reader/dir_reader.c +++ /dev/null @@ -1,366 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * fs_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int inode_copy(const sqfs_inode_generic_t *inode, - sqfs_inode_generic_t **out) -{ - *out = alloc_flex(sizeof(*inode), 1, inode->payload_bytes_used); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - memcpy(*out, inode, sizeof(*inode) + inode->payload_bytes_used); - return 0; -} - -static int dcache_key_compare(const void *ctx, const void *l, const void *r) -{ - sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); - (void)ctx; - - return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); -} - -static int dcache_add(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *inode, sqfs_u64 ref) -{ - sqfs_u32 inum = inode->base.inode_number; - - if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) - return 0; - - if (inode->base.type != SQFS_INODE_DIR && - inode->base.type != SQFS_INODE_EXT_DIR) { - return 0; - } - - if (rbtree_lookup(&rd->dcache, &inum) != NULL) - return 0; - - return rbtree_insert(&rd->dcache, &inum, &ref); -} - -static int dcache_find(sqfs_dir_reader_t *rd, sqfs_u32 inode, sqfs_u64 *ref) -{ - rbtree_node_t *node; - - if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) - return SQFS_ERROR_NO_ENTRY; - - node = rbtree_lookup(&rd->dcache, &inode); - if (node == NULL) - return SQFS_ERROR_NO_ENTRY; - - *ref = *((sqfs_u64 *)rbtree_node_value(node)); - return 0; -} - -static void dir_reader_destroy(sqfs_object_t *obj) -{ - sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; - - if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(&rd->dcache); - - sqfs_drop(rd->meta_inode); - sqfs_drop(rd->meta_dir); - free(rd); -} - -static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_dir_reader_t *rd = (const sqfs_dir_reader_t *)obj; - sqfs_dir_reader_t *copy = malloc(sizeof(*copy)); - - if (copy == NULL) - return NULL; - - memcpy(copy, rd, sizeof(*copy)); - - if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) { - if (rbtree_copy(&rd->dcache, ©->dcache)) - goto fail_cache; - } - - copy->meta_inode = sqfs_copy(rd->meta_inode); - if (copy->meta_inode == NULL) - goto fail_mino; - - copy->meta_dir = sqfs_copy(rd->meta_dir); - if (copy->meta_dir == NULL) - goto fail_mdir; - - return (sqfs_object_t *)copy; -fail_mdir: - sqfs_drop(copy->meta_inode); -fail_mino: - if (copy->flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(©->dcache); -fail_cache: - free(copy); - return NULL; -} - -sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, - sqfs_compressor_t *cmp, - sqfs_file_t *file, - sqfs_u32 flags) -{ - sqfs_dir_reader_t *rd; - sqfs_u64 start, limit; - int ret; - - if (flags & ~SQFS_DIR_READER_ALL_FLAGS) - return NULL; - - rd = calloc(1, sizeof(*rd)); - if (rd == NULL) - return NULL; - - sqfs_object_init(rd, dir_reader_destroy, dir_reader_copy); - - if (flags & SQFS_DIR_READER_DOT_ENTRIES) { - ret = rbtree_init(&rd->dcache, sizeof(sqfs_u32), - sizeof(sqfs_u64), dcache_key_compare); - - if (ret != 0) - goto fail_dcache; - } - - start = super->inode_table_start; - limit = super->directory_table_start; - - rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit); - if (rd->meta_inode == NULL) - goto fail_mino; - - start = super->directory_table_start; - limit = super->id_table_start; - - if (super->fragment_table_start < limit) - limit = super->fragment_table_start; - - if (super->export_table_start < limit) - limit = super->export_table_start; - - rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); - if (rd->meta_dir == NULL) - goto fail_mdir; - - rd->super = *super; - rd->flags = flags; - rd->state = DIR_STATE_NONE; - return rd; -fail_mdir: - sqfs_drop(rd->meta_inode); -fail_mino: - if (flags & SQFS_DIR_READER_DOT_ENTRIES) - rbtree_cleanup(&rd->dcache); -fail_dcache: - free(rd); - return NULL; -} - -int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *inode, - sqfs_u32 flags) -{ - sqfs_u32 parent; - int ret; - - if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) - return SQFS_ERROR_UNSUPPORTED; - - ret = sqfs_readdir_state_init(&rd->it, &rd->super, inode); - if (ret) - return ret; - - if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && - !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { - if (inode->base.type == SQFS_INODE_EXT_DIR) { - parent = inode->data.dir_ext.parent_inode; - } else { - parent = inode->data.dir.parent_inode; - } - - if (dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) - return SQFS_ERROR_NO_ENTRY; - - if (rd->cur_ref == rd->super.root_inode_ref) { - rd->parent_ref = rd->cur_ref; - } else if (dcache_find(rd, parent, &rd->parent_ref)) { - return SQFS_ERROR_NO_ENTRY; - } - - rd->state = DIR_STATE_OPENED; - } else { - rd->state = DIR_STATE_ENTRIES; - } - - rd->start_state = rd->state; - return 0; -} - -static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) -{ - size_t len = strlen(str); - sqfs_dir_entry_t *ent; - - ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); - if (ent == NULL) - return SQFS_ERROR_ALLOC; - - ent->type = SQFS_INODE_DIR; - ent->size = len - 1; - - strcpy((char *)ent->name, str); - - *out = ent; - return 0; -} - -int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) -{ - int err; - - switch (rd->state) { - case DIR_STATE_OPENED: - err = mk_dummy_entry(".", out); - if (err == 0) { - rd->state = DIR_STATE_DOT; - rd->ent_ref = rd->cur_ref; - } - return err; - case DIR_STATE_DOT: - err = mk_dummy_entry("..", out); - if (err == 0) { - rd->state = DIR_STATE_ENTRIES; - rd->ent_ref = rd->parent_ref; - } - return err; - case DIR_STATE_ENTRIES: - break; - default: - return SQFS_ERROR_SEQUENCE; - } - - return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, - out, NULL, &rd->ent_ref); -} - -int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) -{ - if (rd->state == DIR_STATE_NONE) - return SQFS_ERROR_SEQUENCE; - - sqfs_readdir_state_reset(&rd->it); - rd->state = rd->start_state; - return 0; -} - -int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) -{ - sqfs_dir_entry_t *ent; - int ret; - - ret = sqfs_dir_reader_rewind(rd); - if (ret != 0) - return ret; - - do { - ret = sqfs_dir_reader_read(rd, &ent); - if (ret < 0) - return ret; - if (ret > 0) - return SQFS_ERROR_NO_ENTRY; - - ret = strcmp((const char *)ent->name, name); - free(ent); - } while (ret < 0); - - return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY; -} - -int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, - sqfs_inode_generic_t **inode) -{ - int ret; - - ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, - rd->ent_ref >> 16, - rd->ent_ref & 0x0FFFF, inode); - if (ret != 0) - return ret; - - return dcache_add(rd, *inode, rd->ent_ref); -} - -int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, - sqfs_inode_generic_t **inode) -{ - sqfs_u64 block_start = rd->super.root_inode_ref >> 16; - sqfs_u16 offset = rd->super.root_inode_ref & 0xFFFF; - int ret; - - ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, - block_start, offset, inode); - if (ret != 0) - return ret; - - return dcache_add(rd, *inode, rd->super.root_inode_ref); -} - -int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, - const sqfs_inode_generic_t *start, - const char *path, sqfs_inode_generic_t **out) -{ - sqfs_inode_generic_t *inode; - const char *ptr; - int ret = 0; - char *name; - - if (start == NULL) { - ret = sqfs_dir_reader_get_root_inode(rd, &inode); - } else { - ret = inode_copy(start, &inode); - } - - if (ret) - return ret; - - for (; *path != '\0'; path = ptr) { - if (*path == '/') { - for (ptr = path; *ptr == '/'; ++ptr) - ; - continue; - } - - ret = sqfs_dir_reader_open_dir(rd, inode, 0); - free(inode); - if (ret) - return ret; - - ptr = strchrnul(path, '/'); - - name = strndup(path, ptr - path); - if (name == NULL) - return SQFS_ERROR_ALLOC; - - ret = sqfs_dir_reader_find(rd, name); - free(name); - if (ret) - return ret; - - ret = sqfs_dir_reader_get_inode(rd, &inode); - if (ret) - return ret; - } - - *out = inode; - return 0; -} diff --git a/lib/sqfs/dir_reader/get_path.c b/lib/sqfs/dir_reader/get_path.c deleted file mode 100644 index 847bfd3..0000000 --- a/lib/sqfs/dir_reader/get_path.c +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * get_path.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -#include -#include - -int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out) -{ - const sqfs_tree_node_t *it; - size_t clen, len = 0; - char *str, *ptr; - - *out = NULL; - - if (node == NULL) - return SQFS_ERROR_ARG_INVALID; - - for (it = node; it->parent != NULL; it = it->parent) { - if (it->parent == node) - return SQFS_ERROR_LINK_LOOP; - - /* non-root nodes must have a valid name */ - clen = strlen((const char *)it->name); - - if (clen == 0) - return SQFS_ERROR_CORRUPTED; - - if (strchr((const char *)it->name, '/') != NULL) - return SQFS_ERROR_CORRUPTED; - - if (it->name[0] == '.') { - if (clen == 1 || (clen == 2 && it->name[1] == '.')) - return SQFS_ERROR_CORRUPTED; - } - - /* compute total path length */ - if (SZ_ADD_OV(clen, 1, &clen)) - return SQFS_ERROR_OVERFLOW; - - if (SZ_ADD_OV(len, clen, &len)) - return SQFS_ERROR_OVERFLOW; - } - - /* root node must not have a name */ - if (it->name[0] != '\0') - return SQFS_ERROR_ARG_INVALID; - - /* generate the path */ - if (node->parent == NULL) { - str = strdup("/"); - if (str == NULL) - return SQFS_ERROR_ALLOC; - } else { - if (SZ_ADD_OV(len, 1, &len)) - return SQFS_ERROR_OVERFLOW; - - str = malloc(len); - if (str == NULL) - return SQFS_ERROR_ALLOC; - - ptr = str + len - 1; - *ptr = '\0'; - - for (it = node; it->parent != NULL; it = it->parent) { - len = strlen((const char *)it->name); - ptr -= len; - - memcpy(ptr, (const char *)it->name, len); - *(--ptr) = '/'; - } - } - - *out = str; - return 0; -} diff --git a/lib/sqfs/dir_reader/internal.h b/lib/sqfs/dir_reader/internal.h deleted file mode 100644 index 471d197..0000000 --- a/lib/sqfs/dir_reader/internal.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * internal.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef DIR_READER_INTERNAL_H -#define DIR_READER_INTERNAL_H - -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/dir_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/id_table.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/error.h" -#include "sqfs/dir.h" -#include "util/rbtree.h" -#include "util/util.h" - -#include -#include - -enum { - DIR_STATE_NONE = 0, - DIR_STATE_OPENED = 1, - DIR_STATE_DOT = 2, - DIR_STATE_ENTRIES = 3, -}; - -struct sqfs_dir_reader_t { - sqfs_object_t base; - - sqfs_meta_reader_t *meta_dir; - sqfs_meta_reader_t *meta_inode; - sqfs_super_t super; - - sqfs_readdir_state_t it; - - sqfs_u32 flags; - - int start_state; - int state; - sqfs_u64 parent_ref; - sqfs_u64 cur_ref; - sqfs_u64 ent_ref; - rbtree_t dcache; -}; - -#endif /* DIR_READER_INTERNAL_H */ diff --git a/lib/sqfs/dir_reader/read_tree.c b/lib/sqfs/dir_reader/read_tree.c deleted file mode 100644 index 91cc2c0..0000000 --- a/lib/sqfs/dir_reader/read_tree.c +++ /dev/null @@ -1,288 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_tree.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "internal.h" - -static int should_skip(int type, unsigned int flags) -{ - switch (type) { - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_BDEV: - return (flags & SQFS_TREE_NO_DEVICES); - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - return (flags & SQFS_TREE_NO_SLINKS); - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - return(flags & SQFS_TREE_NO_SOCKETS); - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - return (flags & SQFS_TREE_NO_FIFO); - default: - break; - } - - return 0; -} - -static bool would_be_own_parent(sqfs_tree_node_t *parent, sqfs_tree_node_t *n) -{ - sqfs_u32 inum = n->inode->base.inode_number; - - while (parent != NULL) { - if (parent->inode->base.inode_number == inum) - return true; - - parent = parent->parent; - } - - return false; -} - -static sqfs_tree_node_t *create_node(sqfs_inode_generic_t *inode, - const char *name) -{ - sqfs_tree_node_t *n; - - n = alloc_flex(sizeof(*n), 1, strlen(name) + 1); - if (n == NULL) - return NULL; - - n->inode = inode; - strcpy((char *)n->name, name); - return n; -} - -static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root, - unsigned int flags) -{ - sqfs_tree_node_t *n, *prev, **tail; - sqfs_inode_generic_t *inode; - sqfs_dir_entry_t *ent; - int err; - - tail = &root->children; - - for (;;) { - err = sqfs_dir_reader_read(dr, &ent); - if (err > 0) - break; - if (err < 0) - return err; - - if (should_skip(ent->type, flags)) { - free(ent); - continue; - } - - err = sqfs_dir_reader_get_inode(dr, &inode); - if (err) { - free(ent); - return err; - } - - n = create_node(inode, (const char *)ent->name); - free(ent); - - if (n == NULL) { - free(inode); - return SQFS_ERROR_ALLOC; - } - - if (would_be_own_parent(root, n)) { - free(n); - free(inode); - return SQFS_ERROR_LINK_LOOP; - } - - *tail = n; - tail = &n->next; - n->parent = root; - } - - n = root->children; - prev = NULL; - - while (n != NULL) { - if (n->inode->base.type == SQFS_INODE_DIR || - n->inode->base.type == SQFS_INODE_EXT_DIR) { - if (!(flags & SQFS_TREE_NO_RECURSE)) { - err = sqfs_dir_reader_open_dir(dr, n->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (err) - return err; - - err = fill_dir(dr, n, flags); - if (err) - return err; - } - - if (n->children == NULL && - (flags & SQFS_TREE_NO_EMPTY)) { - free(n->inode); - if (prev == NULL) { - root->children = root->children->next; - free(n); - n = root->children; - } else { - prev->next = n->next; - free(n); - n = prev->next; - } - continue; - } - } - - prev = n; - n = n->next; - } - - return 0; -} - -static int resolve_ids(sqfs_tree_node_t *root, const sqfs_id_table_t *idtbl) -{ - sqfs_tree_node_t *it; - int err; - - for (it = root->children; it != NULL; it = it->next) - resolve_ids(it, idtbl); - - err = sqfs_id_table_index_to_id(idtbl, root->inode->base.uid_idx, - &root->uid); - if (err) - return err; - - return sqfs_id_table_index_to_id(idtbl, root->inode->base.gid_idx, - &root->gid); -} - -void sqfs_dir_tree_destroy(sqfs_tree_node_t *root) -{ - sqfs_tree_node_t *it; - - if (!root) - return; - - while (root->children != NULL) { - it = root->children; - root->children = it->next; - - sqfs_dir_tree_destroy(it); - } - - free(root->inode); - free(root); -} - -int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, - const sqfs_id_table_t *idtbl, - const char *path, unsigned int flags, - sqfs_tree_node_t **out) -{ - sqfs_tree_node_t *root, *tail, *new; - sqfs_inode_generic_t *inode; - sqfs_dir_entry_t *ent; - const char *ptr; - int ret; - - if (flags & ~SQFS_TREE_ALL_FLAGS) - return SQFS_ERROR_UNSUPPORTED; - - ret = sqfs_dir_reader_get_root_inode(rd, &inode); - if (ret) - return ret; - - root = tail = create_node(inode, ""); - if (root == NULL) { - free(inode); - return SQFS_ERROR_ALLOC; - } - inode = NULL; - - while (path != NULL && *path != '\0') { - if (*path == '/') { - while (*path == '/') - ++path; - continue; - } - - ret = sqfs_dir_reader_open_dir(rd, tail->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (ret) - goto fail; - - ptr = strchrnul(path, '/'); - - for (;;) { - ret = sqfs_dir_reader_read(rd, &ent); - if (ret < 0) - goto fail; - if (ret > 0) { - ret = SQFS_ERROR_NO_ENTRY; - goto fail; - } - - ret = strncmp((const char *)ent->name, - path, ptr - path); - if (ret == 0 && ent->name[ptr - path] == '\0') - break; - free(ent); - } - - ret = sqfs_dir_reader_get_inode(rd, &inode); - if (ret) { - free(ent); - goto fail; - } - - new = create_node(inode, (const char *)ent->name); - free(ent); - - if (new == NULL) { - free(inode); - ret = SQFS_ERROR_ALLOC; - goto fail; - } - - inode = NULL; - path = ptr; - - if (flags & SQFS_TREE_STORE_PARENTS) { - tail->children = new; - new->parent = tail; - tail = new; - } else { - sqfs_dir_tree_destroy(root); - root = tail = new; - } - } - - if (tail->inode->base.type == SQFS_INODE_DIR || - tail->inode->base.type == SQFS_INODE_EXT_DIR) { - ret = sqfs_dir_reader_open_dir(rd, tail->inode, - SQFS_DIR_OPEN_NO_DOT_ENTRIES); - if (ret) - goto fail; - - ret = fill_dir(rd, tail, flags); - if (ret) - goto fail; - } - - ret = resolve_ids(root, idtbl); - if (ret) - goto fail; - - *out = root; - return 0; -fail: - sqfs_dir_tree_destroy(root); - return ret; -} diff --git a/lib/sqfs/dir_writer.c b/lib/sqfs/dir_writer.c deleted file mode 100644 index d2b72df..0000000 --- a/lib/sqfs/dir_writer.c +++ /dev/null @@ -1,460 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * dir_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#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" -#include "sqfs/dir.h" -#include "util/array.h" -#include "util/util.h" - -#include -#include - -#define DIR_INDEX_THRESHOLD (256) - -typedef struct dir_entry_t { - struct dir_entry_t *next; - sqfs_u64 inode_ref; - sqfs_u32 inode_num; - sqfs_u16 type; - size_t name_len; - char name[]; -} dir_entry_t; - -typedef struct index_ent_t { - struct index_ent_t *next; - dir_entry_t *ent; - sqfs_u64 block; - sqfs_u32 index; -} index_ent_t; - -struct sqfs_dir_writer_t { - sqfs_object_t base; - - dir_entry_t *list; - dir_entry_t *list_end; - - index_ent_t *idx; - index_ent_t *idx_end; - - sqfs_u64 dir_ref; - size_t dir_size; - size_t ent_count; - sqfs_meta_writer_t *dm; - - array_t export_tbl; -}; - -static int get_type(sqfs_u16 mode) -{ - switch (mode & S_IFMT) { - case S_IFSOCK: return SQFS_INODE_SOCKET; - case S_IFIFO: return SQFS_INODE_FIFO; - case S_IFLNK: return SQFS_INODE_SLINK; - case S_IFBLK: return SQFS_INODE_BDEV; - case S_IFCHR: return SQFS_INODE_CDEV; - case S_IFDIR: return SQFS_INODE_DIR; - case S_IFREG: return SQFS_INODE_FILE; - default: - break; - } - - return SQFS_ERROR_UNSUPPORTED; -} - -static void writer_reset(sqfs_dir_writer_t *writer) -{ - dir_entry_t *ent; - index_ent_t *idx; - - while (writer->idx != NULL) { - idx = writer->idx; - writer->idx = idx->next; - free(idx); - } - - while (writer->list != NULL) { - ent = writer->list; - writer->list = ent->next; - free(ent); - } - - writer->list_end = NULL; - writer->idx_end = NULL; - writer->dir_ref = 0; - writer->dir_size = 0; - writer->ent_count = 0; -} - -static int add_export_table_entry(sqfs_dir_writer_t *writer, - sqfs_u32 inum, sqfs_u64 iref) -{ - sqfs_u64 *ptr; - int ret; - - if (writer->export_tbl.data == NULL) - return 0; - - if (inum < 1) - return SQFS_ERROR_ARG_INVALID; - - ret = array_set_capacity(&writer->export_tbl, inum); - if (ret != 0) - return ret; - - ptr = (sqfs_u64 *)writer->export_tbl.data; - - if ((inum - 1) >= writer->export_tbl.used) { - memset(ptr + writer->export_tbl.used, 0xFF, - (inum - writer->export_tbl.used) * sizeof(*ptr)); - - writer->export_tbl.used = inum; - } - - ptr[inum - 1] = iref; - return 0; -} - -static void dir_writer_destroy(sqfs_object_t *obj) -{ - sqfs_dir_writer_t *writer = (sqfs_dir_writer_t *)obj; - - sqfs_drop(writer->dm); - writer_reset(writer); - array_cleanup(&writer->export_tbl); - free(writer); -} - -sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, - sqfs_u32 flags) -{ - 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; - - sqfs_object_init(writer, dir_writer_destroy, NULL); - - if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { - if (array_init(&writer->export_tbl, sizeof(sqfs_u64), 512)) { - free(writer); - return NULL; - } - - memset(writer->export_tbl.data, 0xFF, - writer->export_tbl.size * writer->export_tbl.count); - } - - writer->dm = sqfs_grab(dm); - return writer; -} - -int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags) -{ - sqfs_u32 offset; - sqfs_u64 block; - - if (flags != 0) - return SQFS_ERROR_UNSUPPORTED; - - writer_reset(writer); - - sqfs_meta_writer_get_position(writer->dm, &block, &offset); - writer->dir_ref = (block << 16) | offset; - return 0; -} - -int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, - sqfs_u32 inode_num, sqfs_u64 inode_ref, - sqfs_u16 mode) -{ - dir_entry_t *ent; - int type, err; - - type = get_type(mode); - if (type < 0) - return type; - - 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; - - ent->inode_ref = inode_ref; - ent->inode_num = inode_num; - ent->type = type; - ent->name_len = strlen(name); - memcpy(ent->name, name, ent->name_len); - - if (writer->list_end == NULL) { - writer->list = writer->list_end = ent; - } else { - writer->list_end->next = ent; - writer->list_end = ent; - } - - writer->ent_count += 1; - return 0; -} - -static size_t get_conseq_entry_count(sqfs_u32 offset, dir_entry_t *head) -{ - size_t size, count = 0; - dir_entry_t *it; - sqfs_s32 diff; - - size = (offset + sizeof(sqfs_dir_header_t)) % SQFS_META_BLOCK_SIZE; - - for (it = head; it != NULL; it = it->next) { - if ((it->inode_ref >> 16) != (head->inode_ref >> 16)) - break; - - diff = it->inode_num - head->inode_num; - - if (diff > 32767 || diff < -32767) - break; - - size += sizeof(sqfs_dir_entry_t) + it->name_len; - - if (count > 0 && size > SQFS_META_BLOCK_SIZE) - break; - - count += 1; - - if (count == SQFS_MAX_DIR_ENT) - break; - } - - return count; -} - -static int add_header(sqfs_dir_writer_t *writer, size_t count, - dir_entry_t *ref, sqfs_u64 block) -{ - sqfs_dir_header_t hdr; - index_ent_t *idx; - int err; - - hdr.count = htole32(count - 1); - hdr.start_block = htole32(ref->inode_ref >> 16); - hdr.inode_number = htole32(ref->inode_num); - - err = sqfs_meta_writer_append(writer->dm, &hdr, sizeof(hdr)); - if (err) - return err; - - idx = calloc(1, sizeof(*idx)); - if (idx == NULL) - return SQFS_ERROR_ALLOC; - - idx->ent = ref; - idx->block = block; - idx->index = writer->dir_size; - - if (writer->idx_end == NULL) { - writer->idx = writer->idx_end = idx; - } else { - writer->idx_end->next = idx; - writer->idx_end = idx; - } - - writer->dir_size += sizeof(hdr); - return 0; -} - -int sqfs_dir_writer_end(sqfs_dir_writer_t *writer) -{ - dir_entry_t *it, *first; - sqfs_dir_entry_t ent; - sqfs_u16 *diff_u16; - size_t i, count; - sqfs_u32 offset; - sqfs_u64 block; - int err; - - for (it = writer->list; it != NULL; ) { - sqfs_meta_writer_get_position(writer->dm, &block, &offset); - count = get_conseq_entry_count(offset, it); - - err = add_header(writer, count, it, block); - if (err) - return err; - - first = it; - - for (i = 0; i < count; ++i) { - ent.offset = htole16(it->inode_ref & 0x0000FFFF); - ent.inode_diff = it->inode_num - first->inode_num; - ent.type = htole16(it->type); - ent.size = htole16(it->name_len - 1); - - diff_u16 = (sqfs_u16 *)&ent.inode_diff; - *diff_u16 = htole16(*diff_u16); - - err = sqfs_meta_writer_append(writer->dm, &ent, - sizeof(ent)); - if (err) - return err; - - err = sqfs_meta_writer_append(writer->dm, it->name, - it->name_len); - if (err) - return err; - - writer->dir_size += sizeof(ent) + it->name_len; - it = it->next; - } - } - - return 0; -} - -size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer) -{ - return writer->dir_size; -} - -sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer) -{ - return writer->dir_ref; -} - -size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer) -{ - size_t index_size = 0; - index_ent_t *idx; - - for (idx = writer->idx; idx != NULL; idx = idx->next) - index_size += sizeof(sqfs_dir_index_t) + idx->ent->name_len; - - return index_size; -} - -size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer) -{ - return writer->ent_count; -} - -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) -{ - sqfs_inode_generic_t *inode; - sqfs_dir_index_t ent; - sqfs_u64 start_block; - sqfs_u16 block_offset; - size_t index_size; - index_ent_t *idx; - sqfs_u8 *ptr; - - index_size = 0; - - for (idx = writer->idx; idx != NULL; idx = idx->next) - index_size += sizeof(ent) + idx->ent->name_len; - - inode = alloc_flex(sizeof(*inode), 1, index_size); - if (inode == NULL) - return NULL; - - inode->payload_bytes_available = index_size; - start_block = writer->dir_ref >> 16; - block_offset = writer->dir_ref & 0xFFFF; - - if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || - writer->dir_size > (0xFFFF - 3)) { - inode->base.type = SQFS_INODE_EXT_DIR; - } else { - inode->base.type = SQFS_INODE_DIR; - } - - if (writer->ent_count >= DIR_INDEX_THRESHOLD) - inode->base.type = SQFS_INODE_EXT_DIR; - - if (inode->base.type == SQFS_INODE_DIR) { - inode->data.dir.start_block = start_block; - inode->data.dir.nlink = writer->ent_count + hlinks + 2; - inode->data.dir.size = writer->dir_size + 3; - inode->data.dir.offset = block_offset; - inode->data.dir.parent_inode = parent_ino; - } else { - inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; - inode->data.dir_ext.size = writer->dir_size + 3; - inode->data.dir_ext.start_block = start_block; - inode->data.dir_ext.parent_inode = parent_ino; - inode->data.dir_ext.offset = block_offset; - inode->data.dir_ext.xattr_idx = xattr; - inode->data.dir_ext.inodex_count = 0; - inode->payload_bytes_used = 0; - - for (idx = writer->idx; idx != NULL; idx = idx->next) { - memset(&ent, 0, sizeof(ent)); - ent.start_block = idx->block; - ent.index = idx->index; - ent.size = idx->ent->name_len - 1; - - ptr = (sqfs_u8 *)inode->extra + - inode->payload_bytes_used; - memcpy(ptr, &ent, sizeof(ent)); - memcpy(ptr + sizeof(ent), idx->ent->name, - idx->ent->name_len); - - inode->data.dir_ext.inodex_count += 1; - inode->payload_bytes_used += sizeof(ent); - inode->payload_bytes_used += idx->ent->name_len; - } - } - - 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.data == NULL) - return 0; - - size = writer->export_tbl.size * writer->export_tbl.used; - - ret = sqfs_write_table(file, cmp, writer->export_tbl.data, - size, &start); - if (ret) - return ret; - - super->export_table_start = start; - super->flags |= SQFS_FLAG_EXPORTABLE; - return 0; -} diff --git a/lib/sqfs/frag_table.c b/lib/sqfs/frag_table.c deleted file mode 100644 index 151df28..0000000 --- a/lib/sqfs/frag_table.c +++ /dev/null @@ -1,204 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * frag_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#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 "util/array.h" - -#include -#include - -struct sqfs_frag_table_t { - sqfs_object_t base; - - array_t table; -}; - -static void frag_table_destroy(sqfs_object_t *obj) -{ - sqfs_frag_table_t *tbl = (sqfs_frag_table_t *)obj; - - array_cleanup(&tbl->table); - free(tbl); -} - -static sqfs_object_t *frag_table_copy(const sqfs_object_t *obj) -{ - const sqfs_frag_table_t *tbl = (const sqfs_frag_table_t *)obj; - sqfs_frag_table_t *copy = calloc(1, sizeof(*copy)); - - if (copy == NULL) - return NULL; - - if (array_init_copy(©->table, &tbl->table)) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -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; - - sqfs_object_init(tbl, frag_table_destroy, frag_table_copy); - - array_init(&tbl->table, sizeof(sqfs_fragment_t), 0); - return 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; - - array_cleanup(&tbl->table); - tbl->table.size = sizeof(sqfs_fragment_t); - - 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.data = raw; - tbl->table.count = super->fragment_entry_count; - tbl->table.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->table.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.data, - tbl->table.size * tbl->table.used, - &super->fragment_table_start); - if (err) - return err; - - super->fragment_entry_count = tbl->table.used; - super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; - super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; - super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; - - for (i = 0; i < tbl->table.used; ++i) { - sqfs_u32 sz = ((sqfs_fragment_t *)tbl->table.data)[i].size; - - if (SQFS_IS_BLOCK_COMPRESSED(le32toh(sz))) { - 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) -{ - sqfs_fragment_t *frag = array_get(&tbl->table, index); - - if (frag == NULL) - return SQFS_ERROR_OUT_OF_BOUNDS; - - out->start_offset = le64toh(frag->start_offset); - out->size = le32toh(frag->size); - out->pad0 = le32toh(frag->pad0); - return 0; -} - -int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, - sqfs_u32 size, sqfs_u32 *index) -{ - sqfs_fragment_t frag; - - if (index != NULL) - *index = tbl->table.used; - - memset(&frag, 0, sizeof(frag)); - frag.start_offset = htole64(location); - frag.size = htole32(size); - - return array_append(&tbl->table, &frag); -} - -int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, - sqfs_u64 location, sqfs_u32 size) -{ - sqfs_fragment_t frag; - - memset(&frag, 0, sizeof(frag)); - frag.start_offset = htole64(location); - frag.size = htole32(size); - - return array_set(&tbl->table, index, &frag); -} - -size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) -{ - return tbl->table.used; -} diff --git a/lib/sqfs/id_table.c b/lib/sqfs/id_table.c deleted file mode 100644 index ec3fdfe..0000000 --- a/lib/sqfs/id_table.c +++ /dev/null @@ -1,162 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * id_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/id_table.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/error.h" -#include "compat.h" -#include "util/array.h" - -#include -#include - -struct sqfs_id_table_t { - sqfs_object_t base; - - array_t ids; -}; - -static void id_table_destroy(sqfs_object_t *obj) -{ - sqfs_id_table_t *tbl = (sqfs_id_table_t *)obj; - - array_cleanup(&tbl->ids); - free(tbl); -} - -static sqfs_object_t *id_table_copy(const sqfs_object_t *obj) -{ - const sqfs_id_table_t *tbl = (const sqfs_id_table_t *)obj; - sqfs_id_table_t *copy = calloc(1, sizeof(*copy)); - - if (copy == NULL) - return NULL; - - if (array_init_copy(©->ids, &tbl->ids) != 0) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags) -{ - sqfs_id_table_t *tbl; - - if (flags != 0) - return NULL; - - tbl = calloc(1, sizeof(sqfs_id_table_t)); - - if (tbl != NULL) { - array_init(&tbl->ids, sizeof(sqfs_u32), 0); - sqfs_object_init(tbl, id_table_destroy, id_table_copy); - } - - return tbl; -} - -int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out) -{ - size_t i; - - for (i = 0; i < tbl->ids.used; ++i) { - if (((sqfs_u32 *)tbl->ids.data)[i] == id) { - *out = i; - return 0; - } - } - - if (tbl->ids.used == 0x10000) - return SQFS_ERROR_OVERFLOW; - - *out = tbl->ids.used; - return array_append(&tbl->ids, &id); -} - -int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, - sqfs_u32 *out) -{ - if (index >= tbl->ids.used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - *out = ((sqfs_u32 *)tbl->ids.data)[index]; - return 0; -} - -int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, - const sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 upper_limit, lower_limit; - void *raw_ids; - size_t i; - int ret; - - if (!super->id_count || super->id_table_start >= super->bytes_used) - return SQFS_ERROR_CORRUPTED; - - upper_limit = super->id_table_start; - lower_limit = super->directory_table_start; - - if (super->fragment_table_start > lower_limit && - super->fragment_table_start < upper_limit) { - lower_limit = super->fragment_table_start; - } - - if (super->export_table_start > lower_limit && - super->export_table_start < upper_limit) { - lower_limit = super->export_table_start; - } - - array_cleanup(&tbl->ids); - tbl->ids.size = sizeof(sqfs_u32); - - ret = sqfs_read_table(file, cmp, super->id_count * sizeof(sqfs_u32), - super->id_table_start, lower_limit, - upper_limit, &raw_ids); - if (ret) - return ret; - - for (i = 0; i < super->id_count; ++i) - ((sqfs_u32 *)raw_ids)[i] = le32toh(((sqfs_u32 *)raw_ids)[i]); - - tbl->ids.data = raw_ids; - tbl->ids.used = super->id_count; - tbl->ids.count = super->id_count; - return 0; -} - -int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 start; - size_t i; - int ret; - - for (i = 0; i < tbl->ids.used; ++i) { - ((sqfs_u32 *)tbl->ids.data)[i] = - htole32(((sqfs_u32 *)tbl->ids.data)[i]); - } - - super->id_count = tbl->ids.used; - - ret = sqfs_write_table(file, cmp, tbl->ids.data, - sizeof(sqfs_u32) * tbl->ids.used, &start); - - super->id_table_start = start; - - for (i = 0; i < tbl->ids.used; ++i) { - ((sqfs_u32 *)tbl->ids.data)[i] = - le32toh(((sqfs_u32 *)tbl->ids.data)[i]); - } - - return ret; -} diff --git a/lib/sqfs/inode.c b/lib/sqfs/inode.c deleted file mode 100644 index ce51cf5..0000000 --- a/lib/sqfs/inode.c +++ /dev/null @@ -1,380 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/inode.h" -#include "sqfs/error.h" -#include "sqfs/dir.h" - -#include -#include - -#include "util/util.h" - -static int inverse_type[] = { - [SQFS_INODE_DIR] = SQFS_INODE_EXT_DIR, - [SQFS_INODE_FILE] = SQFS_INODE_EXT_FILE, - [SQFS_INODE_SLINK] = SQFS_INODE_EXT_SLINK, - [SQFS_INODE_BDEV] = SQFS_INODE_EXT_BDEV, - [SQFS_INODE_CDEV] = SQFS_INODE_EXT_CDEV, - [SQFS_INODE_FIFO] = SQFS_INODE_EXT_FIFO, - [SQFS_INODE_SOCKET] = SQFS_INODE_EXT_SOCKET, - [SQFS_INODE_EXT_DIR] = SQFS_INODE_DIR, - [SQFS_INODE_EXT_FILE] = SQFS_INODE_FILE, - [SQFS_INODE_EXT_SLINK] = SQFS_INODE_SLINK, - [SQFS_INODE_EXT_BDEV] = SQFS_INODE_BDEV, - [SQFS_INODE_EXT_CDEV] = SQFS_INODE_CDEV, - [SQFS_INODE_EXT_FIFO] = SQFS_INODE_FIFO, - [SQFS_INODE_EXT_SOCKET] = SQFS_INODE_SOCKET, -}; - -int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, - sqfs_u32 *out) -{ - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - *out = 0xFFFFFFFF; - break; - case SQFS_INODE_EXT_DIR: - *out = inode->data.dir_ext.xattr_idx; - break; - case SQFS_INODE_EXT_FILE: - *out = inode->data.file_ext.xattr_idx; - break; - case SQFS_INODE_EXT_SLINK: - *out = inode->data.slink_ext.xattr_idx; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - *out = inode->data.dev_ext.xattr_idx; - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - *out = inode->data.ipc_ext.xattr_idx; - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - return 0; -} - -int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index) -{ - int err; - - if (index != 0xFFFFFFFF) { - err = sqfs_inode_make_extended(inode); - if (err) - return err; - } - - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - break; - case SQFS_INODE_EXT_DIR: - inode->data.dir_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_FILE: - inode->data.file_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_SLINK: - inode->data.slink_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - inode->data.dev_ext.xattr_idx = index; - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - inode->data.ipc_ext.xattr_idx = index; - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - return 0; -} - -int sqfs_inode_make_extended(sqfs_inode_generic_t *inode) -{ - switch (inode->base.type) { - case SQFS_INODE_DIR: { - sqfs_inode_dir_ext_t temp = { - .nlink = inode->data.dir.nlink, - .size = inode->data.dir.size, - .start_block = inode->data.dir.start_block, - .parent_inode = inode->data.dir.parent_inode, - .inodex_count = 0, - .offset = inode->data.dir.offset, - .xattr_idx = 0xFFFFFFFF, - }; - inode->data.dir_ext = temp; - break; - } - case SQFS_INODE_FILE: { - sqfs_inode_file_ext_t temp = { - .blocks_start = inode->data.file.blocks_start, - .file_size = inode->data.file.file_size, - .sparse = 0, - .nlink = 1, - .fragment_idx = inode->data.file.fragment_index, - .fragment_offset = inode->data.file.fragment_offset, - .xattr_idx = 0xFFFFFFFF, - }; - inode->data.file_ext = temp; - break; - } - case SQFS_INODE_SLINK: - inode->data.slink_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; - break; - case SQFS_INODE_EXT_DIR: - case SQFS_INODE_EXT_FILE: - case SQFS_INODE_EXT_SLINK: - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - return 0; - default: - return SQFS_ERROR_CORRUPTED; - } - - inode->base.type = inverse_type[inode->base.type]; - return 0; -} - -int sqfs_inode_make_basic(sqfs_inode_generic_t *inode) -{ - sqfs_u32 xattr; - int err; - - err = sqfs_inode_get_xattr_index(inode, &xattr); - if (err != 0 || xattr != 0xFFFFFFFF) - return err; - - switch (inode->base.type) { - case SQFS_INODE_DIR: - case SQFS_INODE_FILE: - case SQFS_INODE_SLINK: - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - return 0; - case SQFS_INODE_EXT_DIR: { - sqfs_inode_dir_t temp = { - .start_block = inode->data.dir_ext.start_block, - .nlink = inode->data.dir_ext.nlink, - .size = inode->data.dir_ext.size, - .offset = inode->data.dir_ext.offset, - .parent_inode = inode->data.dir_ext.parent_inode, - }; - - if (inode->data.dir_ext.size > 0x0FFFF) - return 0; - - inode->data.dir = temp; - break; - } - case SQFS_INODE_EXT_FILE: { - sqfs_inode_file_t temp = { - .blocks_start = inode->data.file_ext.blocks_start, - .fragment_index = inode->data.file_ext.fragment_idx, - .fragment_offset = inode->data.file_ext.fragment_offset, - .file_size = inode->data.file_ext.file_size, - }; - - if (inode->data.file_ext.blocks_start > 0x0FFFFFFFFUL) - return 0; - if (inode->data.file_ext.file_size > 0x0FFFFFFFFUL) - return 0; - if (inode->data.file_ext.sparse > 0) - return 0; - if (inode->data.file_ext.nlink > 1) - return 0; - - inode->data.file = temp; - break; - } - case SQFS_INODE_EXT_SLINK: - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - break; - default: - return SQFS_ERROR_CORRUPTED; - } - - inode->base.type = inverse_type[inode->base.type]; - return 0; -} - -int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.file_size = size; - - if (size < 0x0FFFFFFFFUL) - sqfs_inode_make_basic(inode); - } else if (inode->base.type == SQFS_INODE_FILE) { - if (size > 0x0FFFFFFFFUL) { - sqfs_inode_make_extended(inode); - inode->data.file_ext.file_size = size; - } else { - inode->data.file.file_size = size; - } - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, - sqfs_u32 index, sqfs_u32 offset) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.fragment_idx = index; - inode->data.file_ext.fragment_offset = offset; - } else if (inode->base.type == SQFS_INODE_FILE) { - inode->data.file.fragment_index = index; - inode->data.file.fragment_offset = offset; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, - sqfs_u64 location) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - inode->data.file_ext.blocks_start = location; - - if (location < 0x0FFFFFFFFUL) - sqfs_inode_make_basic(inode); - } else if (inode->base.type == SQFS_INODE_FILE) { - if (location > 0x0FFFFFFFFUL) { - sqfs_inode_make_extended(inode); - inode->data.file_ext.blocks_start = location; - } else { - inode->data.file.blocks_start = location; - } - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *size = inode->data.file_ext.file_size; - } else if (inode->base.type == SQFS_INODE_FILE) { - *size = inode->data.file.file_size; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, - sqfs_u32 *index, sqfs_u32 *offset) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *index = inode->data.file_ext.fragment_idx; - *offset = inode->data.file_ext.fragment_offset; - } else if (inode->base.type == SQFS_INODE_FILE) { - *index = inode->data.file.fragment_index; - *offset = inode->data.file.fragment_offset; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, - sqfs_u64 *location) -{ - if (inode->base.type == SQFS_INODE_EXT_FILE) { - *location = inode->data.file_ext.blocks_start; - } else if (inode->base.type == SQFS_INODE_FILE) { - *location = inode->data.file.blocks_start; - } else { - return SQFS_ERROR_NOT_FILE; - } - - return 0; -} - -int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, - sqfs_dir_index_t **out, - size_t index) -{ - sqfs_dir_index_t ent; - const char *ptr; - size_t offset; - - if (inode->base.type != SQFS_INODE_EXT_DIR) { - if (inode->base.type == SQFS_INODE_DIR) - return SQFS_ERROR_OUT_OF_BOUNDS; - - return SQFS_ERROR_NOT_DIR; - } - - offset = 0; - ptr = (const char *)inode->extra; - - for (;;) { - if (offset >= inode->payload_bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (index == 0) - break; - - memcpy(&ent, ptr + offset, sizeof(ent)); - offset += sizeof(ent) + ent.size + 1; - index -= 1; - } - - memcpy(&ent, ptr + offset, sizeof(ent)); - - *out = alloc_flex(sizeof(ent), 1, ent.size + 2); - if (*out == NULL) - return SQFS_ERROR_ALLOC; - - memcpy(*out, &ent, sizeof(ent)); - memcpy((*out)->name, ptr + offset + sizeof(ent), ent.size + 1); - return 0; -} diff --git a/lib/sqfs/meta_reader.c b/lib/sqfs/meta_reader.c deleted file mode 100644 index e431d40..0000000 --- a/lib/sqfs/meta_reader.c +++ /dev/null @@ -1,191 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * meta_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -struct sqfs_meta_reader_t { - sqfs_object_t base; - - sqfs_u64 start; - sqfs_u64 limit; - size_t data_used; - - /* The location of the current block in the image */ - sqfs_u64 block_offset; - - /* The location of the next block after the current one */ - sqfs_u64 next_block; - - /* A byte offset into the uncompressed data of the current block */ - size_t offset; - - /* The underlying file descriptor to read from */ - sqfs_file_t *file; - - /* A pointer to the compressor to use for extracting data */ - sqfs_compressor_t *cmp; - - /* The raw data read from the input file */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE]; - - /* The uncompressed data read from the input file */ - sqfs_u8 scratch[SQFS_META_BLOCK_SIZE]; -}; - -static void meta_reader_destroy(sqfs_object_t *m) -{ - sqfs_meta_reader_t *mr = (sqfs_meta_reader_t *)m; - - sqfs_drop(mr->file); - sqfs_drop(mr->cmp); - free(m); -} - -static sqfs_object_t *meta_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_meta_reader_t *m = (const sqfs_meta_reader_t *)obj; - sqfs_meta_reader_t *copy = malloc(sizeof(*copy)); - - if (copy != NULL) { - memcpy(copy, m, sizeof(*m)); - - /* duplicate references */ - copy->cmp = sqfs_grab(copy->cmp); - copy->file = sqfs_grab(copy->file); - } - - return (sqfs_object_t *)copy; -} - -sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, - sqfs_compressor_t *cmp, - sqfs_u64 start, sqfs_u64 limit) -{ - sqfs_meta_reader_t *m = calloc(1, sizeof(*m)); - - if (m == NULL) - return NULL; - - sqfs_object_init(m, meta_reader_destroy, meta_reader_copy); - - m->block_offset = 0xFFFFFFFFFFFFFFFFUL; - m->start = start; - m->limit = limit; - m->file = sqfs_grab(file); - m->cmp = sqfs_grab(cmp); - return m; -} - -int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, - size_t offset) -{ - bool compressed; - sqfs_u16 header; - sqfs_u32 size; - sqfs_s32 ret; - int err; - - if (block_start < m->start || block_start >= m->limit) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (block_start == m->block_offset) { - if (offset >= m->data_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - m->offset = offset; - return 0; - } - - err = m->file->read_at(m->file, block_start, &header, 2); - if (err) - return err; - - header = le16toh(header); - compressed = (header & 0x8000) == 0; - size = header & 0x7FFF; - - if (size > sizeof(m->data)) - return SQFS_ERROR_CORRUPTED; - - if ((block_start + 2 + size) > m->limit) - return SQFS_ERROR_OUT_OF_BOUNDS; - - err = m->file->read_at(m->file, block_start + 2, m->data, size); - if (err) - return err; - - if (compressed) { - ret = m->cmp->do_block(m->cmp, m->data, size, - m->scratch, sizeof(m->scratch)); - - if (ret < 0) - return ret; - - memcpy(m->data, m->scratch, ret); - m->data_used = ret; - } else { - m->data_used = size; - } - - if (offset >= m->data_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - m->block_offset = block_start; - m->next_block = block_start + size + 2; - m->offset = offset; - return 0; -} - -void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, - sqfs_u64 *block_start, size_t *offset) -{ - if (m->offset == m->data_used) { - *block_start = m->next_block; - *offset = 0; - } else { - *block_start = m->block_offset; - *offset = m->offset; - } -} - -int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size) -{ - size_t diff; - int ret; - - while (size != 0) { - diff = m->data_used - m->offset; - - if (diff == 0) { - ret = sqfs_meta_reader_seek(m, m->next_block, 0); - if (ret) - return ret; - diff = m->data_used; - } - - if (diff > size) - diff = size; - - memcpy(data, m->data + m->offset, diff); - - m->offset += diff; - data = (char *)data + diff; - size -= diff; - } - - return 0; -} diff --git a/lib/sqfs/meta_writer.c b/lib/sqfs/meta_writer.c deleted file mode 100644 index bf3f426..0000000 --- a/lib/sqfs/meta_writer.c +++ /dev/null @@ -1,215 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * meta_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/compressor.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include - -typedef struct meta_block_t { - struct meta_block_t *next; - - /* possibly compressed data with 2 byte header */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE + 2]; -} meta_block_t; - -struct sqfs_meta_writer_t { - sqfs_object_t base; - - /* A byte offset into the uncompressed data of the current block */ - size_t offset; - - /* The location of the current block in the file */ - size_t block_offset; - - /* The underlying file descriptor to write to */ - sqfs_file_t *file; - - /* A pointer to the compressor to use for compressing the data */ - sqfs_compressor_t *cmp; - - /* The raw data chunk that data is appended to */ - sqfs_u8 data[SQFS_META_BLOCK_SIZE]; - - sqfs_u32 flags; - meta_block_t *list; - meta_block_t *list_end; -}; - -static int write_block(sqfs_file_t *file, meta_block_t *outblk) -{ - sqfs_u16 header; - size_t count; - sqfs_u64 off; - - memcpy(&header, outblk->data, sizeof(header)); - count = le16toh(header) & 0x7FFF; - off = file->get_size(file); - - return file->write_at(file, off, outblk->data, count + 2); -} - -static void meta_writer_destroy(sqfs_object_t *obj) -{ - sqfs_meta_writer_t *m = (sqfs_meta_writer_t *)obj; - meta_block_t *blk; - - while (m->list != NULL) { - blk = m->list; - m->list = blk->next; - free(blk); - } - - sqfs_drop(m->file); - sqfs_drop(m->cmp); - free(m); -} - -sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, - sqfs_compressor_t *cmp, - sqfs_u32 flags) -{ - sqfs_meta_writer_t *m; - - if (flags & ~SQFS_META_WRITER_ALL_FLAGS) - return NULL; - - m = calloc(1, sizeof(*m)); - if (m == NULL) - return NULL; - - sqfs_object_init(m, meta_writer_destroy, NULL); - - m->cmp = sqfs_grab(cmp); - m->file = sqfs_grab(file); - m->flags = flags; - return m; -} - -int sqfs_meta_writer_flush(sqfs_meta_writer_t *m) -{ - meta_block_t *outblk; - sqfs_u16 header; - sqfs_u32 count; - sqfs_s32 ret; - - if (m->offset == 0) - return 0; - - outblk = calloc(1, sizeof(*outblk)); - if (outblk == NULL) - return SQFS_ERROR_ALLOC; - - ret = m->cmp->do_block(m->cmp, m->data, m->offset, - outblk->data + 2, sizeof(outblk->data) - 2); - if (ret < 0) { - free(outblk); - return ret; - } - - if (ret > 0) { - header = htole16(ret); - count = ret + 2; - } else { - header = htole16(m->offset | 0x8000); - memcpy(outblk->data + 2, m->data, m->offset); - count = m->offset + 2; - } - - memcpy(outblk->data, &header, sizeof(header)); - - ret = 0; - - if (m->flags & SQFS_META_WRITER_KEEP_IN_MEMORY) { - if (m->list == NULL) { - m->list = outblk; - } else { - m->list_end->next = outblk; - } - m->list_end = outblk; - } else { - ret = write_block(m->file, outblk); - free(outblk); - } - - memset(m->data, 0, sizeof(m->data)); - m->offset = 0; - m->block_offset += count; - return ret; -} - -int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, - size_t size) -{ - size_t diff; - int ret; - - while (size != 0) { - diff = sizeof(m->data) - m->offset; - - if (diff == 0) { - ret = sqfs_meta_writer_flush(m); - if (ret) - return ret; - diff = sizeof(m->data); - } - - if (diff > size) - diff = size; - - memcpy(m->data + m->offset, data, diff); - m->offset += diff; - size -= diff; - data = (const char *)data + diff; - } - - if (m->offset == sizeof(m->data)) - return sqfs_meta_writer_flush(m); - - return 0; -} - -void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, - sqfs_u64 *block_start, - sqfs_u32 *offset) -{ - *block_start = m->block_offset; - *offset = m->offset; -} - -void sqfs_meta_writer_reset(sqfs_meta_writer_t *m) -{ - m->block_offset = 0; - m->offset = 0; -} - -int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m) -{ - meta_block_t *blk; - int ret; - - while (m->list != NULL) { - blk = m->list; - - ret = write_block(m->file, blk); - if (ret) - return ret; - - m->list = blk->next; - free(blk); - } - - m->list_end = NULL; - return 0; -} diff --git a/lib/sqfs/misc.c b/lib/sqfs/misc.c deleted file mode 100644 index 74a4203..0000000 --- a/lib/sqfs/misc.c +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * misc.c - * - * Copyright (C) 2021 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/predef.h" - -#include - -void sqfs_free(void *ptr) -{ - free(ptr); -} diff --git a/lib/sqfs/read_inode.c b/lib/sqfs/read_inode.c deleted file mode 100644 index 12bef48..0000000 --- a/lib/sqfs/read_inode.c +++ /dev/null @@ -1,424 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "util/util.h" - -#include -#include -#include - -#define SWAB16(x) x = le16toh(x) -#define SWAB32(x) x = le32toh(x) -#define SWAB64(x) x = le64toh(x) - -static int set_mode(sqfs_inode_t *inode) -{ - inode->mode &= ~S_IFMT; - - switch (inode->type) { - case SQFS_INODE_SOCKET: - case SQFS_INODE_EXT_SOCKET: - inode->mode |= S_IFSOCK; - break; - case SQFS_INODE_SLINK: - case SQFS_INODE_EXT_SLINK: - inode->mode |= S_IFLNK; - break; - case SQFS_INODE_FILE: - case SQFS_INODE_EXT_FILE: - inode->mode |= S_IFREG; - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_EXT_BDEV: - inode->mode |= S_IFBLK; - break; - case SQFS_INODE_DIR: - case SQFS_INODE_EXT_DIR: - inode->mode |= S_IFDIR; - break; - case SQFS_INODE_CDEV: - case SQFS_INODE_EXT_CDEV: - inode->mode |= S_IFCHR; - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_EXT_FIFO: - inode->mode |= S_IFIFO; - break; - default: - return SQFS_ERROR_UNSUPPORTED; - } - - return 0; -} - -static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size, - sqfs_u32 frag_index, sqfs_u32 frag_offset) -{ - sqfs_u64 count = size / block_size; - - if ((size % block_size) != 0 && - (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { - ++count; - } - - return count; -} - -static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - size_t block_size, sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_file_t file; - sqfs_u64 i, count; - int err; - - err = sqfs_meta_reader_read(ir, &file, sizeof(file)); - if (err) - return err; - - SWAB32(file.blocks_start); - SWAB32(file.fragment_index); - SWAB32(file.fragment_offset); - SWAB32(file.file_size); - - count = get_block_count(file.file_size, block_size, - file.fragment_index, file.fragment_offset); - - out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = *base; - out->data.file = file; - out->payload_bytes_available = count * sizeof(sqfs_u32); - out->payload_bytes_used = count * sizeof(sqfs_u32); - - err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); - if (err) { - free(out); - return err; - } - - for (i = 0; i < count; ++i) - SWAB32(out->extra[i]); - - *result = out; - return 0; -} - -static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - size_t block_size, sqfs_inode_generic_t **result) -{ - sqfs_inode_file_ext_t file; - sqfs_inode_generic_t *out; - sqfs_u64 i, count; - int err; - - err = sqfs_meta_reader_read(ir, &file, sizeof(file)); - if (err) - return err; - - SWAB64(file.blocks_start); - SWAB64(file.file_size); - SWAB64(file.sparse); - SWAB32(file.nlink); - SWAB32(file.fragment_idx); - SWAB32(file.fragment_offset); - SWAB32(file.xattr_idx); - - count = get_block_count(file.file_size, block_size, - file.fragment_idx, file.fragment_offset); - - out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); - if (out == NULL) { - return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW : - SQFS_ERROR_ALLOC; - } - - out->base = *base; - out->data.file_ext = file; - out->payload_bytes_available = count * sizeof(sqfs_u32); - out->payload_bytes_used = count * sizeof(sqfs_u32); - - err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); - if (err) { - free(out); - return err; - } - - for (i = 0; i < count; ++i) - SWAB32(out->extra[i]); - - *result = out; - return 0; -} - -static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_slink_t slink; - size_t size; - int err; - - err = sqfs_meta_reader_read(ir, &slink, sizeof(slink)); - if (err) - return err; - - SWAB32(slink.nlink); - SWAB32(slink.target_size); - - if (SZ_ADD_OV(slink.target_size, 1, &size) || - SZ_ADD_OV(sizeof(*out), size, &size)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, size); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->payload_bytes_available = size - sizeof(*out); - out->payload_bytes_used = size - sizeof(*out) - 1; - out->base = *base; - out->data.slink = slink; - - err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size); - if (err) { - free(out); - return err; - } - - *result = out; - return 0; -} - -static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - sqfs_u32 xattr; - int err; - - err = read_inode_slink(ir, base, result); - if (err) - return err; - - err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr)); - if (err) { - free(*result); - return err; - } - - (*result)->data.slink_ext.xattr_idx = le32toh(xattr); - return 0; -} - -static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, - sqfs_inode_generic_t **result) -{ - size_t i, new_sz, index_max, index_used; - sqfs_inode_generic_t *out, *new; - sqfs_inode_dir_ext_t dir; - sqfs_dir_index_t ent; - int err; - - err = sqfs_meta_reader_read(ir, &dir, sizeof(dir)); - if (err) - return err; - - SWAB32(dir.nlink); - SWAB32(dir.size); - SWAB32(dir.start_block); - SWAB32(dir.parent_inode); - SWAB16(dir.inodex_count); - SWAB16(dir.offset); - SWAB32(dir.xattr_idx); - - index_max = dir.size ? 128 : 0; - index_used = 0; - - out = alloc_flex(sizeof(*out), 1, index_max); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = *base; - out->data.dir_ext = dir; - - if (dir.size == 0) { - *result = out; - return 0; - } - - for (i = 0; i < dir.inodex_count; ++i) { - err = sqfs_meta_reader_read(ir, &ent, sizeof(ent)); - if (err) { - free(out); - return err; - } - - SWAB32(ent.start_block); - SWAB32(ent.index); - SWAB32(ent.size); - - new_sz = index_max; - while (sizeof(ent) + ent.size + 1 > new_sz - index_used) { - if (SZ_MUL_OV(new_sz, 2, &new_sz)) { - free(out); - return SQFS_ERROR_OVERFLOW; - } - } - - if (new_sz > index_max) { - new = realloc(out, sizeof(*out) + new_sz); - if (new == NULL) { - free(out); - return SQFS_ERROR_ALLOC; - } - out = new; - index_max = new_sz; - } - - memcpy((char *)out->extra + index_used, &ent, sizeof(ent)); - index_used += sizeof(ent); - - err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used, - ent.size + 1); - if (err) { - free(out); - return err; - } - - index_used += ent.size + 1; - } - - out->payload_bytes_used = index_used; - out->payload_bytes_available = index_used; - *result = out; - return 0; -} - -int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, - const sqfs_super_t *super, - sqfs_u64 block_start, size_t offset, - sqfs_inode_generic_t **result) -{ - sqfs_inode_generic_t *out; - sqfs_inode_t inode; - int err; - - /* read base inode */ - block_start += super->inode_table_start; - - err = sqfs_meta_reader_seek(ir, block_start, offset); - if (err) - return err; - - err = sqfs_meta_reader_read(ir, &inode, sizeof(inode)); - if (err) - return err; - - SWAB16(inode.type); - SWAB16(inode.mode); - SWAB16(inode.uid_idx); - SWAB16(inode.gid_idx); - SWAB32(inode.mod_time); - SWAB32(inode.inode_number); - - err = set_mode(&inode); - if (err) - return err; - - /* inode types where the size is variable */ - switch (inode.type) { - case SQFS_INODE_FILE: - return read_inode_file(ir, &inode, super->block_size, result); - case SQFS_INODE_SLINK: - return read_inode_slink(ir, &inode, result); - case SQFS_INODE_EXT_FILE: - return read_inode_file_ext(ir, &inode, super->block_size, - result); - case SQFS_INODE_EXT_SLINK: - return read_inode_slink_ext(ir, &inode, result); - case SQFS_INODE_EXT_DIR: - return read_inode_dir_ext(ir, &inode, result); - default: - break; - } - - /* everything else */ - out = calloc(1, sizeof(*out)); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - out->base = inode; - - switch (inode.type) { - case SQFS_INODE_DIR: - err = sqfs_meta_reader_read(ir, &out->data.dir, - sizeof(out->data.dir)); - if (err) - goto fail_free; - - SWAB32(out->data.dir.start_block); - SWAB32(out->data.dir.nlink); - SWAB16(out->data.dir.size); - SWAB16(out->data.dir.offset); - SWAB32(out->data.dir.parent_inode); - break; - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: - err = sqfs_meta_reader_read(ir, &out->data.dev, - sizeof(out->data.dev)); - if (err) - goto fail_free; - SWAB32(out->data.dev.nlink); - SWAB32(out->data.dev.devno); - break; - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: - err = sqfs_meta_reader_read(ir, &out->data.ipc, - sizeof(out->data.ipc)); - if (err) - goto fail_free; - SWAB32(out->data.ipc.nlink); - break; - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: - err = sqfs_meta_reader_read(ir, &out->data.dev_ext, - sizeof(out->data.dev_ext)); - if (err) - goto fail_free; - SWAB32(out->data.dev_ext.nlink); - SWAB32(out->data.dev_ext.devno); - SWAB32(out->data.dev_ext.xattr_idx); - break; - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: - err = sqfs_meta_reader_read(ir, &out->data.ipc_ext, - sizeof(out->data.ipc_ext)); - if (err) - goto fail_free; - SWAB32(out->data.ipc_ext.nlink); - SWAB32(out->data.ipc_ext.xattr_idx); - break; - default: - err = SQFS_ERROR_UNSUPPORTED; - goto fail_free; - } - - *result = out; - return 0; -fail_free: - free(out); - return err; -} diff --git a/lib/sqfs/read_super.c b/lib/sqfs/read_super.c deleted file mode 100644 index 11bc314..0000000 --- a/lib/sqfs/read_super.c +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/error.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file) -{ - size_t block_size = 0; - sqfs_super_t temp; - int i, ret; - - ret = file->read_at(file, 0, &temp, sizeof(temp)); - if (ret) - return ret; - - temp.magic = le32toh(temp.magic); - temp.inode_count = le32toh(temp.inode_count); - temp.modification_time = le32toh(temp.modification_time); - temp.block_size = le32toh(temp.block_size); - temp.fragment_entry_count = le32toh(temp.fragment_entry_count); - temp.compression_id = le16toh(temp.compression_id); - temp.block_log = le16toh(temp.block_log); - temp.flags = le16toh(temp.flags); - temp.id_count = le16toh(temp.id_count); - temp.version_major = le16toh(temp.version_major); - temp.version_minor = le16toh(temp.version_minor); - temp.root_inode_ref = le64toh(temp.root_inode_ref); - temp.bytes_used = le64toh(temp.bytes_used); - temp.id_table_start = le64toh(temp.id_table_start); - temp.xattr_id_table_start = le64toh(temp.xattr_id_table_start); - temp.inode_table_start = le64toh(temp.inode_table_start); - temp.directory_table_start = le64toh(temp.directory_table_start); - temp.fragment_table_start = le64toh(temp.fragment_table_start); - temp.export_table_start = le64toh(temp.export_table_start); - - if (temp.magic != SQFS_MAGIC) - return SFQS_ERROR_SUPER_MAGIC; - - if ((temp.version_major != SQFS_VERSION_MAJOR) || - (temp.version_minor != SQFS_VERSION_MINOR)) - return SFQS_ERROR_SUPER_VERSION; - - if ((temp.block_size - 1) & temp.block_size) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_size < SQFS_MIN_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_size > SQFS_MAX_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (temp.block_log < 12 || temp.block_log > 20) - return SQFS_ERROR_CORRUPTED; - - block_size = 1; - - for (i = 0; i < temp.block_log; ++i) - block_size <<= 1; - - if (temp.block_size != block_size) - return SQFS_ERROR_CORRUPTED; - - if (temp.compression_id < SQFS_COMP_MIN || - temp.compression_id > SQFS_COMP_MAX) - return SQFS_ERROR_UNSUPPORTED; - - if (temp.id_count == 0) - return SQFS_ERROR_CORRUPTED; - - memcpy(super, &temp, sizeof(temp)); - return 0; -} diff --git a/lib/sqfs/read_table.c b/lib/sqfs/read_table.c deleted file mode 100644 index c6a9bbe..0000000 --- a/lib/sqfs/read_table.c +++ /dev/null @@ -1,91 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * read_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/table.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, - size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, - sqfs_u64 upper_limit, void **out) -{ - size_t diff, block_count, blk_idx = 0; - sqfs_u64 start, *locations; - sqfs_meta_reader_t *m; - void *data, *ptr; - int err; - - data = malloc(table_size); - if (data == NULL) - return SQFS_ERROR_ALLOC; - - /* restore list from image */ - block_count = table_size / SQFS_META_BLOCK_SIZE; - - if ((table_size % SQFS_META_BLOCK_SIZE) != 0) - ++block_count; - - locations = alloc_array(sizeof(sqfs_u64), block_count); - - if (locations == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail_data; - } - - err = file->read_at(file, location, locations, - sizeof(sqfs_u64) * block_count); - if (err) - goto fail_idx; - - /* Read the actual data */ - m = sqfs_meta_reader_create(file, cmp, lower_limit, upper_limit); - if (m == NULL) { - err = SQFS_ERROR_ALLOC; - goto fail_idx; - } - - ptr = data; - - while (table_size > 0) { - start = le64toh(locations[blk_idx++]); - - err = sqfs_meta_reader_seek(m, start, 0); - if (err) - goto fail; - - diff = SQFS_META_BLOCK_SIZE; - if (diff > table_size) - diff = table_size; - - err = sqfs_meta_reader_read(m, ptr, diff); - if (err) - goto fail; - - ptr = (char *)ptr + diff; - table_size -= diff; - } - - sqfs_drop(m); - free(locations); - *out = data; - return 0; -fail: - sqfs_drop(m); -fail_idx: - free(locations); -fail_data: - free(data); - *out = NULL; - return err; -} diff --git a/lib/sqfs/readdir.c b/lib/sqfs/readdir.c deleted file mode 100644 index e2dbcd4..0000000 --- a/lib/sqfs/readdir.c +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * readdir.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_reader.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "compat.h" - -#include -#include - -int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, - sqfs_dir_header_t *hdr) -{ - int err = sqfs_meta_reader_read(m, hdr, sizeof(*hdr)); - if (err) - return err; - - hdr->count = le32toh(hdr->count); - hdr->start_block = le32toh(hdr->start_block); - hdr->inode_number = le32toh(hdr->inode_number); - - if (hdr->count > (SQFS_MAX_DIR_ENT - 1)) - return SQFS_ERROR_CORRUPTED; - - return 0; -} - -int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, - sqfs_dir_entry_t **result) -{ - sqfs_dir_entry_t ent, *out; - sqfs_u16 *diff_u16; - int err; - - err = sqfs_meta_reader_read(m, &ent, sizeof(ent)); - if (err) - return err; - - diff_u16 = (sqfs_u16 *)&ent.inode_diff; - *diff_u16 = le16toh(*diff_u16); - - ent.offset = le16toh(ent.offset); - ent.type = le16toh(ent.type); - ent.size = le16toh(ent.size); - - out = calloc(1, sizeof(*out) + ent.size + 2); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = ent; - err = sqfs_meta_reader_read(m, out->name, ent.size + 1); - if (err) { - free(out); - return err; - } - - *result = out; - return 0; -} - -int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, - const sqfs_inode_generic_t *inode) -{ - memset(s, 0, sizeof(*s)); - - if (inode->base.type == SQFS_INODE_DIR) { - s->init.block = inode->data.dir.start_block; - s->init.offset = inode->data.dir.offset; - s->init.size = inode->data.dir.size; - } else if (inode->base.type == SQFS_INODE_EXT_DIR) { - s->init.block = inode->data.dir_ext.start_block; - s->init.offset = inode->data.dir_ext.offset; - s->init.size = inode->data.dir_ext.size; - } else { - return SQFS_ERROR_NOT_DIR; - } - - s->init.block += super->directory_table_start; - s->current = s->init; - return 0; -} - -int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, - sqfs_dir_entry_t **ent, - sqfs_u32 *inum, sqfs_u64 *iref) -{ - size_t count; - int ret; - - if (it->entries == 0) { - sqfs_dir_header_t hdr; - - if (it->current.size <= sizeof(hdr)) - goto out_eof; - - ret = sqfs_meta_reader_seek(m, it->current.block, - it->current.offset); - if (ret != 0) - return ret; - - ret = sqfs_meta_reader_read_dir_header(m, &hdr); - if (ret != 0) - return ret; - - sqfs_meta_reader_get_position(m, &it->current.block, - &it->current.offset); - - it->current.size -= sizeof(hdr); - it->entries = hdr.count + 1; - it->inum_base = hdr.inode_number; - it->inode_block = hdr.start_block; - } - - if (it->current.size <= sizeof(**ent)) - goto out_eof; - - ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); - if (ret != 0) - return ret; - - ret = sqfs_meta_reader_read_dir_ent(m, ent); - if (ret) - return ret; - - sqfs_meta_reader_get_position(m, &it->current.block, - &it->current.offset); - - it->current.size -= sizeof(**ent); - it->entries -= 1; - - count = (*ent)->size + 1; - - if (count >= it->current.size) { - it->current.size = 0; - } else { - it->current.size -= count; - } - - if (inum != NULL) - *inum = it->inum_base + (*ent)->inode_diff; - - if (iref != NULL) { - *iref = (sqfs_u64)it->inode_block << 16UL; - *iref |= (*ent)->offset; - } - - return 0; -out_eof: - it->current.size = 0; - it->entries = 0; - return 1; -} diff --git a/lib/sqfs/src/block_processor/backend.c b/lib/sqfs/src/block_processor/backend.c new file mode 100644 index 0000000..b443c9d --- /dev/null +++ b/lib/sqfs/src/block_processor/backend.c @@ -0,0 +1,335 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * backend.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int set_block_size(sqfs_inode_generic_t **inode, + sqfs_u32 index, sqfs_u32 size) +{ + size_t min_size = (index + 1) * sizeof(sqfs_u32); + size_t avail = (*inode)->payload_bytes_available; + sqfs_inode_generic_t *new; + size_t newsz; + + if (avail < min_size) { + newsz = avail ? avail : (sizeof(sqfs_u32) * 4); + while (newsz < min_size) + newsz *= 2; + + if (SZ_ADD_OV(newsz, sizeof(**inode), &newsz)) + return SQFS_ERROR_OVERFLOW; + + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if ((newsz - sizeof(**inode)) > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + new = realloc((*inode), newsz); + if (new == NULL) + return SQFS_ERROR_ALLOC; + + (*inode) = new; + (*inode)->payload_bytes_available = newsz - sizeof(**inode); + } + + (*inode)->extra[index] = size; + + if (min_size >= (*inode)->payload_bytes_used) + (*inode)->payload_bytes_used = min_size; + + return 0; +} + +static void release_old_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + blk->next = proc->free_list; + proc->free_list = blk; + + proc->backlog -= 1; +} + +static int process_completed_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + sqfs_u64 location; + sqfs_u32 size; + int err; + + if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { + sqfs_block_t *it = proc->fblk_in_flight, *prev = NULL; + + while (it != NULL && it->index != blk->index) { + prev = it; + it = it->next; + } + + if (it != NULL) { + if (prev == NULL) { + proc->fblk_in_flight = it->next; + } else { + prev->next = it->next; + } + free(it); + } + } + + err = proc->wr->write_data_block(proc->wr, blk->user, blk->size, + blk->checksum, + blk->flags & ~BLK_FLAG_INTERNAL, + blk->data, &location); + if (err) + goto out; + + proc->stats.output_bytes_generated += blk->size; + + if (blk->flags & SQFS_BLK_IS_SPARSE) { + if (blk->inode != NULL) { + sqfs_inode_make_extended(*(blk->inode)); + (*(blk->inode))->data.file_ext.sparse += blk->size; + + err = set_block_size(blk->inode, blk->index, 0); + if (err) + goto out; + } + proc->stats.sparse_block_count += 1; + } else if (blk->size != 0) { + size = blk->size; + if (!(blk->flags & SQFS_BLK_IS_COMPRESSED)) + size |= 1 << 24; + + if (blk->flags & SQFS_BLK_FRAGMENT_BLOCK) { + if (proc->frag_tbl != NULL) { + err = sqfs_frag_table_set(proc->frag_tbl, + blk->index, location, + size); + if (err) + goto out; + } + proc->stats.frag_block_count += 1; + } else { + if (blk->inode != NULL) { + err = set_block_size(blk->inode, blk->index, + size); + if (err) + goto out; + } + proc->stats.data_block_count += 1; + } + } + + if (blk->flags & SQFS_BLK_LAST_BLOCK && blk->inode != NULL) + sqfs_inode_set_file_block_start(*(blk->inode), location); +out: + release_old_block(proc, blk); + return err; +} + +static int process_completed_fragment(sqfs_block_processor_t *proc, + sqfs_block_t *frag) +{ + chunk_info_t *chunk = NULL, search; + struct hash_entry *entry; + sqfs_u32 index, offset; + int err; + + if (frag->flags & SQFS_BLK_IS_SPARSE) { + if (frag->inode != NULL) { + sqfs_inode_make_extended(*(frag->inode)); + set_block_size(frag->inode, frag->index, 0); + (*(frag->inode))->data.file_ext.sparse += frag->size; + } + proc->stats.sparse_block_count += 1; + release_old_block(proc, frag); + return 0; + } + + proc->stats.total_frag_count += 1; + + if (!(frag->flags & SQFS_BLK_DONT_DEDUPLICATE)) { + search.hash = frag->checksum; + search.size = frag->size; + + proc->current_frag = frag; + proc->fblk_lookup_error = 0; + entry = hash_table_search_pre_hashed(proc->frag_ht, + search.hash, &search); + proc->current_frag = NULL; + + if (proc->fblk_lookup_error != 0) { + err = proc->fblk_lookup_error; + goto fail; + } + + if (entry != NULL) { + if (frag->inode != NULL) { + chunk = entry->data; + sqfs_inode_set_frag_location(*(frag->inode), + chunk->index, + chunk->offset); + } + release_old_block(proc, frag); + return 0; + } + } + + if (proc->frag_block != NULL) { + size_t size = proc->frag_block->size + frag->size; + + if (size > proc->max_block_size) { + proc->frag_block->io_seq_num = proc->io_seq_num++; + + err = enqueue_block(proc, proc->frag_block); + proc->frag_block = NULL; + + if (err) + goto fail; + } + } + + if (proc->frag_block == NULL) { + if (proc->frag_tbl == NULL) { + index = 0; + } else { + err = sqfs_frag_table_append(proc->frag_tbl, + 0, 0, &index); + if (err) + goto fail; + } + + offset = 0; + proc->frag_block = frag; + proc->frag_block->index = index; + proc->frag_block->flags &= + (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN); + proc->frag_block->flags |= SQFS_BLK_FRAGMENT_BLOCK; + } else { + index = proc->frag_block->index; + offset = proc->frag_block->size; + + memcpy(proc->frag_block->data + proc->frag_block->size, + frag->data, frag->size); + + proc->frag_block->size += frag->size; + proc->frag_block->flags |= + (frag->flags & + (SQFS_BLK_DONT_COMPRESS | SQFS_BLK_ALIGN)); + } + + if (proc->frag_tbl != NULL) { + err = SQFS_ERROR_ALLOC; + chunk = calloc(1, sizeof(*chunk)); + if (chunk == NULL) + goto fail; + + chunk->index = index; + chunk->offset = offset; + chunk->size = frag->size; + chunk->hash = frag->checksum; + + proc->current_frag = frag; + proc->fblk_lookup_error = 0; + entry = hash_table_insert_pre_hashed(proc->frag_ht, chunk->hash, + chunk, chunk); + proc->current_frag = NULL; + + if (proc->fblk_lookup_error != 0) { + err = proc->fblk_lookup_error; + goto fail; + } + + if (entry == NULL) + goto fail; + } + + if (frag->inode != NULL) + sqfs_inode_set_frag_location(*(frag->inode), index, offset); + + if (frag != proc->frag_block) + release_old_block(proc, frag); + + proc->stats.actual_frag_count += 1; + return 0; +fail: + free(chunk); + if (frag != proc->frag_block) + release_old_block(proc, frag); + return err; +} + +static void store_io_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + sqfs_block_t *prev = NULL, *it = proc->io_queue; + + while (it != NULL && (it->io_seq_num < blk->io_seq_num)) { + prev = it; + it = it->next; + } + + if (prev == NULL) { + proc->io_queue = blk; + } else { + prev->next = blk; + } + + blk->next = it; +} + +int dequeue_block(sqfs_block_processor_t *proc) +{ + size_t backlog_old = proc->backlog; + sqfs_block_t *blk; + int status; + + do { + while (proc->io_queue != NULL) { + if (proc->io_queue->io_seq_num != proc->io_deq_seq_num) + break; + + blk = proc->io_queue; + proc->io_queue = blk->next; + proc->io_deq_seq_num += 1; + + status = process_completed_block(proc, blk); + if (status != 0) + return status; + } + + if (proc->backlog < backlog_old) + break; + + if ((proc->backlog == 1) && + (proc->frag_block != NULL || proc->blk_current != NULL)) { + break; + } + + if ((proc->backlog == 2) && + proc->frag_block != NULL && proc->blk_current != NULL) { + break; + } + + blk = proc->pool->dequeue(proc->pool); + + if (blk == NULL) { + status = proc->pool->get_status(proc->pool); + return status ? status : SQFS_ERROR_INTERNAL; + } + + if (blk->flags & SQFS_BLK_IS_FRAGMENT) { + status = process_completed_fragment(proc, blk); + if (status != 0) + return status; + } else { + if (!(blk->flags & SQFS_BLK_FRAGMENT_BLOCK) || + (blk->flags & BLK_FLAG_MANUAL_SUBMISSION)) { + blk->io_seq_num = proc->io_seq_num++; + } + + store_io_block(proc, blk); + } + } while (proc->backlog >= backlog_old); + + return 0; +} diff --git a/lib/sqfs/src/block_processor/block_processor.c b/lib/sqfs/src/block_processor/block_processor.c new file mode 100644 index 0000000..d607437 --- /dev/null +++ b/lib/sqfs/src/block_processor/block_processor.c @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * block_processor.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int process_block(void *userptr, void *workitem) +{ + worker_data_t *worker = userptr; + sqfs_block_t *block = workitem; + sqfs_s32 ret; + + if (block->size == 0) + return 0; + + if (!(block->flags & SQFS_BLK_IGNORE_SPARSE) && + is_memory_zero(block->data, block->size)) { + block->flags |= SQFS_BLK_IS_SPARSE; + return 0; + } + + if (block->flags & SQFS_BLK_DONT_HASH) { + block->checksum = 0; + } else { + block->checksum = xxh32(block->data, block->size); + } + + if (block->flags & (SQFS_BLK_IS_FRAGMENT | SQFS_BLK_DONT_COMPRESS)) + return 0; + + ret = worker->cmp->do_block(worker->cmp, block->data, block->size, + worker->scratch, worker->scratch_size); + if (ret < 0) + return ret; + + if (ret > 0) { + memcpy(block->data, worker->scratch, ret); + block->size = ret; + block->flags |= SQFS_BLK_IS_COMPRESSED; + } + return 0; +} + +static int load_frag_block(sqfs_block_processor_t *proc, sqfs_u32 index) +{ + sqfs_fragment_t info; + size_t size; + int ret; + + if (proc->cached_frag_blk == NULL) { + size = sizeof(*proc->cached_frag_blk); + + proc->cached_frag_blk = alloc_flex(size, 1, + proc->max_block_size); + + if (proc->cached_frag_blk == NULL) + return SQFS_ERROR_ALLOC; + } else { + if (proc->cached_frag_blk->index == index) + return 0; + } + + ret = sqfs_frag_table_lookup(proc->frag_tbl, index, &info); + if (ret != 0) + return ret; + + size = SQFS_ON_DISK_BLOCK_SIZE(info.size); + if (size > proc->max_block_size) + return SQFS_ERROR_CORRUPTED; + + if (SQFS_IS_BLOCK_COMPRESSED(info.size)) { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->scratch, size); + if (ret != 0) + return ret; + + ret = proc->uncmp->do_block(proc->uncmp, proc->scratch, size, + proc->cached_frag_blk->data, + proc->max_block_size); + if (ret <= 0) + return ret ? ret : SQFS_ERROR_OVERFLOW; + + size = ret; + } else { + ret = proc->file->read_at(proc->file, info.start_offset, + proc->cached_frag_blk->data, size); + if (ret != 0) + return ret; + } + + proc->cached_frag_blk->size = size; + proc->cached_frag_blk->index = index; + return 0; +} + +static bool chunk_info_equals(void *user, const void *k, const void *c) +{ + const chunk_info_t *key = k, *cmp = c; + sqfs_block_processor_t *proc = user; + sqfs_block_t *it; + int ret; + + if (key->size != cmp->size || key->hash != cmp->hash) + return false; + + if (proc->uncmp == NULL || proc->file == NULL) + return true; + + if (proc->current_frag == NULL || proc->frag_tbl == NULL) + return true; + + if (proc->fblk_lookup_error != 0) + return false; + + for (it = proc->fblk_in_flight; it != NULL; it = it->next) { + if (it->index == cmp->index) + break; + } + + if (it == NULL && proc->frag_block != NULL) { + if (proc->frag_block->index == cmp->index) + it = proc->frag_block; + } + + if (it == NULL) { + ret = load_frag_block(proc, cmp->index); + if (ret != 0) { + proc->fblk_lookup_error = ret; + return false; + } + + it = proc->cached_frag_blk; + } + + if (cmp->offset >= it->size || (it->size - cmp->offset) < cmp->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + if (cmp->size != proc->current_frag->size) { + proc->fblk_lookup_error = SQFS_ERROR_CORRUPTED; + return false; + } + + return memcmp(it->data + cmp->offset, + proc->current_frag->data, cmp->size) == 0; +} + +static void ht_delete_function(struct hash_entry *entry) +{ + free(entry->data); +} + +static void free_block_list(sqfs_block_t *list) +{ + while (list != NULL) { + sqfs_block_t *it = list; + list = it->next; + free(it); + } +} + +static void block_processor_destroy(sqfs_object_t *base) +{ + sqfs_block_processor_t *proc = (sqfs_block_processor_t *)base; + + free(proc->frag_block); + free(proc->blk_current); + free(proc->cached_frag_blk); + + free_block_list(proc->free_list); + free_block_list(proc->io_queue); + free_block_list(proc->fblk_in_flight); + + if (proc->frag_ht != NULL) + hash_table_destroy(proc->frag_ht, ht_delete_function); + + /* XXX: shut down the pool first before cleaning up the worker data */ + if (proc->pool != NULL) + proc->pool->destroy(proc->pool); + + while (proc->workers != NULL) { + worker_data_t *worker = proc->workers; + proc->workers = worker->next; + + sqfs_drop(worker->cmp); + free(worker); + } + + sqfs_drop(proc->frag_tbl); + sqfs_drop(proc->wr); + sqfs_drop(proc->file); + sqfs_drop(proc->uncmp); + free(proc); +} + +int sqfs_block_processor_sync(sqfs_block_processor_t *proc) +{ + int ret; + + for (;;) { + if (proc->backlog == 0) + break; + + if ((proc->backlog == 1) && + (proc->frag_block != NULL || proc->blk_current != NULL)) { + break; + } + + if ((proc->backlog == 2) && + proc->frag_block != NULL && proc->blk_current != NULL) { + break; + } + + ret = dequeue_block(proc); + if (ret != 0) + return ret; + } + + return 0; +} + +int sqfs_block_processor_finish(sqfs_block_processor_t *proc) +{ + sqfs_block_t *blk; + int status; + + status = sqfs_block_processor_sync(proc); + if (status != 0) + return status; + + if (proc->frag_block != NULL) { + blk = proc->frag_block; + blk->next = NULL; + proc->frag_block = NULL; + + blk->io_seq_num = proc->io_seq_num++; + + status = enqueue_block(proc, blk); + if (status != 0) + return status; + + status = sqfs_block_processor_sync(proc); + } + + return status; +} + +const sqfs_block_processor_stats_t +*sqfs_block_processor_get_stats(const sqfs_block_processor_t *proc) +{ + return &proc->stats; +} + +int sqfs_block_processor_create_ex(const sqfs_block_processor_desc_t *desc, + sqfs_block_processor_t **out) +{ + size_t i, count, scratch_size = 0; + sqfs_block_processor_t *proc; + int ret; + + if (desc->size != sizeof(sqfs_block_processor_desc_t)) + return SQFS_ERROR_ARG_INVALID; + + if (desc->file != NULL && desc->uncmp != NULL) + scratch_size = desc->max_block_size; + + proc = alloc_flex(sizeof(*proc), 1, scratch_size); + if (proc == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(proc, block_processor_destroy, NULL); + + proc->max_backlog = desc->max_backlog; + proc->max_block_size = desc->max_block_size; + proc->frag_tbl = sqfs_grab(desc->tbl); + proc->wr = sqfs_grab(desc->wr); + proc->file = sqfs_grab(desc->file); + proc->uncmp = sqfs_grab(desc->uncmp); + proc->stats.size = sizeof(proc->stats); + + /* we need at least one current data block + one fragment block */ + if (proc->max_backlog < 3) + proc->max_backlog = 3; + + /* create the thread pool */ + proc->pool = thread_pool_create(desc->num_workers, process_block); + if (proc->pool == NULL) { + ret = SQFS_ERROR_INTERNAL; + goto fail_pool; + } + + /* create the worker compressors & scratch buffer */ + count = proc->pool->get_worker_count(proc->pool); + + for (i = 0; i < count; ++i) { + worker_data_t *worker = alloc_flex(sizeof(*worker), 1, + desc->max_block_size); + if (worker == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + worker->scratch_size = desc->max_block_size; + worker->next = proc->workers; + proc->workers = worker; + + worker->cmp = sqfs_copy(desc->cmp); + if (worker->cmp == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + proc->pool->set_worker_ptr(proc->pool, i, worker); + } + + /* create the fragment hash table */ + proc->frag_ht = hash_table_create(NULL, chunk_info_equals); + if (proc->frag_ht == NULL) { + ret = SQFS_ERROR_ALLOC; + goto fail_pool; + } + + proc->frag_ht->user = proc; + *out = proc; + return 0; +fail_pool: + block_processor_destroy((sqfs_object_t *)proc); + return ret; +} + +sqfs_block_processor_t *sqfs_block_processor_create(size_t max_block_size, + sqfs_compressor_t *cmp, + unsigned int num_workers, + size_t max_backlog, + sqfs_block_writer_t *wr, + sqfs_frag_table_t *tbl) +{ + sqfs_block_processor_desc_t desc; + sqfs_block_processor_t *out; + + memset(&desc, 0, sizeof(desc)); + desc.size = sizeof(desc); + desc.max_block_size = max_block_size; + desc.num_workers = num_workers; + desc.max_backlog = max_backlog; + desc.cmp = cmp; + desc.wr = wr; + desc.tbl = tbl; + + if (sqfs_block_processor_create_ex(&desc, &out) != 0) + return NULL; + + return out; +} diff --git a/lib/sqfs/src/block_processor/frontend.c b/lib/sqfs/src/block_processor/frontend.c new file mode 100644 index 0000000..e8a4207 --- /dev/null +++ b/lib/sqfs/src/block_processor/frontend.c @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frontend.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int get_new_block(sqfs_block_processor_t *proc, sqfs_block_t **out) +{ + sqfs_block_t *blk; + + while (proc->backlog >= proc->max_backlog) { + int ret = dequeue_block(proc); + if (ret != 0) + return ret; + } + + if (proc->free_list != NULL) { + blk = proc->free_list; + proc->free_list = blk->next; + } else { + blk = malloc(sizeof(*blk) + proc->max_block_size); + if (blk == NULL) + return SQFS_ERROR_ALLOC; + } + + memset(blk, 0, sizeof(*blk)); + *out = blk; + + proc->backlog += 1; + return 0; +} + +static int add_sentinel_block(sqfs_block_processor_t *proc) +{ + sqfs_block_t *blk; + int ret; + + ret = get_new_block(proc, &blk); + if (ret != 0) + return ret; + + blk->inode = proc->inode; + blk->flags = proc->blk_flags | SQFS_BLK_LAST_BLOCK; + + return enqueue_block(proc, blk); +} + +int enqueue_block(sqfs_block_processor_t *proc, sqfs_block_t *blk) +{ + int status; + + if ((blk->flags & SQFS_BLK_FRAGMENT_BLOCK) && + proc->file != NULL && proc->uncmp != NULL) { + sqfs_block_t *copy = alloc_flex(sizeof(*copy), 1, blk->size); + + if (copy == NULL) + return SQFS_ERROR_ALLOC; + + copy->size = blk->size; + copy->index = blk->index; + memcpy(copy->data, blk->data, blk->size); + + copy->next = proc->fblk_in_flight; + proc->fblk_in_flight = copy; + } + + if (proc->pool->submit(proc->pool, blk) != 0) { + status = proc->pool->get_status(proc->pool); + + if (status == 0) + status = SQFS_ERROR_ALLOC; + + blk->next = proc->free_list; + proc->free_list = blk; + return status; + } + + return 0; +} + +int sqfs_block_processor_begin_file(sqfs_block_processor_t *proc, + sqfs_inode_generic_t **inode, + void *user, sqfs_u32 flags) +{ + if (proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (flags & ~SQFS_BLK_USER_SETTABLE_FLAGS) + return SQFS_ERROR_UNSUPPORTED; + + if (inode != NULL) { + (*inode) = calloc(1, sizeof(sqfs_inode_generic_t)); + if ((*inode) == NULL) + return SQFS_ERROR_ALLOC; + + (*inode)->base.type = SQFS_INODE_FILE; + sqfs_inode_set_frag_location(*inode, 0xFFFFFFFF, 0xFFFFFFFF); + } + + proc->begin_called = true; + proc->inode = inode; + proc->blk_flags = flags | SQFS_BLK_FIRST_BLOCK; + proc->blk_index = 0; + proc->user = user; + return 0; +} + +int sqfs_block_processor_append(sqfs_block_processor_t *proc, const void *data, + size_t size) +{ + sqfs_block_t *new; + sqfs_u64 filesize; + size_t diff; + int err; + + if (!proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (proc->inode != NULL) { + sqfs_inode_get_file_size(*(proc->inode), &filesize); + sqfs_inode_set_file_size(*(proc->inode), filesize + size); + } + + while (size > 0) { + if (proc->blk_current == NULL) { + err = get_new_block(proc, &new); + if (err != 0) + return err; + + proc->blk_current = new; + proc->blk_current->flags = proc->blk_flags; + proc->blk_current->inode = proc->inode; + proc->blk_current->user = proc->user; + proc->blk_current->index = proc->blk_index++; + proc->blk_flags &= ~SQFS_BLK_FIRST_BLOCK; + } + + diff = proc->max_block_size - proc->blk_current->size; + + if (diff == 0) { + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + continue; + } + + if (diff > size) + diff = size; + + memcpy(proc->blk_current->data + proc->blk_current->size, + data, diff); + + size -= diff; + proc->blk_current->size += diff; + data = (const char *)data + diff; + + proc->stats.input_bytes_read += diff; + } + + if (proc->blk_current->size == proc->max_block_size) { + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + } + + return 0; +} + +int sqfs_block_processor_end_file(sqfs_block_processor_t *proc) +{ + int err; + + if (!proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (proc->blk_current == NULL) { + if (!(proc->blk_flags & SQFS_BLK_FIRST_BLOCK)) { + err = add_sentinel_block(proc); + if (err) + return err; + } + } else { + if (proc->blk_flags & SQFS_BLK_DONT_FRAGMENT) { + proc->blk_current->flags |= SQFS_BLK_LAST_BLOCK; + } else { + if (!(proc->blk_current->flags & + SQFS_BLK_FIRST_BLOCK)) { + err = add_sentinel_block(proc); + if (err) + return err; + } + + proc->blk_current->flags |= SQFS_BLK_IS_FRAGMENT; + } + + err = enqueue_block(proc, proc->blk_current); + proc->blk_current = NULL; + + if (err) + return err; + } + + proc->begin_called = false; + proc->inode = NULL; + proc->user = NULL; + proc->blk_flags = 0; + return 0; +} + +int sqfs_block_processor_submit_block(sqfs_block_processor_t *proc, void *user, + sqfs_u32 flags, const void *data, + size_t size) +{ + sqfs_block_t *blk; + int ret; + + if (proc->begin_called) + return SQFS_ERROR_SEQUENCE; + + if (size > proc->max_block_size) + return SQFS_ERROR_OVERFLOW; + + if (flags & ~SQFS_BLK_FLAGS_ALL) + return SQFS_ERROR_UNSUPPORTED; + + ret = get_new_block(proc, &blk); + if (ret != 0) + return ret; + + blk->flags = flags | BLK_FLAG_MANUAL_SUBMISSION; + blk->user = user; + blk->size = size; + memcpy(blk->data, data, size); + + return enqueue_block(proc, blk); +} diff --git a/lib/sqfs/src/block_processor/internal.h b/lib/sqfs/src/block_processor/internal.h new file mode 100644 index 0000000..0b2c88d --- /dev/null +++ b/lib/sqfs/src/block_processor/internal.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "sqfs/block_processor.h" +#include "sqfs/block_writer.h" +#include "sqfs/frag_table.h" +#include "sqfs/compressor.h" +#include "sqfs/inode.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/hash_table.h" +#include "util/threadpool.h" +#include "util/util.h" + +#include +#include + +typedef struct { + sqfs_u32 index; + sqfs_u32 offset; + sqfs_u32 size; + sqfs_u32 hash; +} chunk_info_t; + +enum { + BLK_FLAG_MANUAL_SUBMISSION = 0x10000000, + BLK_FLAG_INTERNAL = 0x10000000, +}; + +typedef struct sqfs_block_t { + struct sqfs_block_t *next; + sqfs_inode_generic_t **inode; + + sqfs_u32 io_seq_num; + sqfs_u32 flags; + sqfs_u32 size; + sqfs_u32 checksum; + + /* For data blocks: index within the inode. + For fragment fragment blocks: fragment table index. */ + sqfs_u32 index; + + /* User data pointer */ + void *user; + + sqfs_u8 data[]; +} sqfs_block_t; + +typedef struct worker_data_t { + struct worker_data_t *next; + sqfs_compressor_t *cmp; + + size_t scratch_size; + sqfs_u8 scratch[]; +} worker_data_t; + +struct sqfs_block_processor_t { + sqfs_object_t obj; + + sqfs_frag_table_t *frag_tbl; + sqfs_block_t *frag_block; + sqfs_block_writer_t *wr; + + sqfs_block_processor_stats_t stats; + + sqfs_inode_generic_t **inode; + sqfs_block_t *blk_current; + sqfs_u32 blk_flags; + sqfs_u32 blk_index; + void *user; + + struct hash_table *frag_ht; + sqfs_block_t *free_list; + + size_t max_block_size; + size_t max_backlog; + size_t backlog; + + bool begin_called; + + sqfs_file_t *file; + sqfs_compressor_t *uncmp; + + thread_pool_t *pool; + worker_data_t *workers; + + sqfs_block_t *io_queue; + sqfs_u32 io_seq_num; + sqfs_u32 io_deq_seq_num; + + sqfs_block_t *current_frag; + sqfs_block_t *cached_frag_blk; + sqfs_block_t *fblk_in_flight; + int fblk_lookup_error; + + sqfs_u8 scratch[]; +}; + +SQFS_INTERNAL int enqueue_block(sqfs_block_processor_t *proc, + sqfs_block_t *blk); + +SQFS_INTERNAL int dequeue_block(sqfs_block_processor_t *proc); + +#endif /* INTERNAL_H */ diff --git a/lib/sqfs/src/block_writer.c b/lib/sqfs/src/block_writer.c new file mode 100644 index 0000000..a5135bc --- /dev/null +++ b/lib/sqfs/src/block_writer.c @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * block_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/block_writer.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/array.h" +#include "util/util.h" + +#include +#include + +#define MK_BLK_HASH(chksum, size) \ + (((sqfs_u64)(size) << 32) | (sqfs_u64)(chksum)) + +#define SIZE_FROM_HASH(hash) ((hash >> 32) & ((1 << 24) - 1)) + +#define INIT_BLOCK_COUNT (128) +#define SCRATCH_SIZE (8192) + +typedef struct { + sqfs_u64 offset; + sqfs_u64 hash; +} blk_info_t; + +typedef struct { + sqfs_block_writer_t base; + sqfs_file_t *file; + + array_t blocks; + size_t devblksz; + + size_t file_start; + + sqfs_u32 flags; + + sqfs_u8 scratch[]; +} block_writer_default_t; + +static int store_block_location(block_writer_default_t *wr, sqfs_u64 offset, + sqfs_u32 size, sqfs_u32 chksum) +{ + blk_info_t info = { offset, MK_BLK_HASH(chksum, size) }; + + return array_append(&(wr->blocks), &info); +} + +static int deduplicate_blocks(block_writer_default_t *wr, sqfs_u32 flags, sqfs_u64 *out) +{ + const blk_info_t *blocks = wr->blocks.data; + sqfs_u64 loc_a, loc_b, sz; + size_t i, j, count; + int ret; + + count = wr->blocks.used - wr->file_start; + if (count == 0) { + *out = 0; + return 0; + } + + if (flags & SQFS_BLK_DONT_DEDUPLICATE) { + *out = blocks[wr->file_start].offset; + return 0; + } + + sz = 0; + loc_a = blocks[wr->file_start].offset; + + for (i = 0; i < count; ++i) + sz += SIZE_FROM_HASH(blocks[wr->file_start + i].hash); + + for (i = 0; i < wr->file_start; ++i) { + for (j = 0; j < count; ++j) { + if (blocks[i + j].hash == 0) + break; + + if (blocks[i + j].hash != + blocks[wr->file_start + j].hash) + break; + } + + if (j != count) + continue; + + if (wr->flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) + break; + + loc_b = blocks[i].offset; + + ret = check_file_range_equal(wr->file, wr->scratch, + SCRATCH_SIZE, loc_a, loc_b, sz); + if (ret == 0) + break; + if (ret < 0) + return ret; + } + + *out = blocks[i].offset; + if (i >= wr->file_start) + return 0; + + if (count >= (wr->file_start - i)) { + wr->blocks.used = i + count; + } else { + wr->blocks.used = wr->file_start; + } + + sz = blocks[wr->blocks.used - 1].offset + + SIZE_FROM_HASH(blocks[wr->blocks.used - 1].hash); + + return wr->file->truncate(wr->file, sz); +} + +static int align_file(block_writer_default_t *wr, sqfs_u32 flags) +{ + void *padding; + sqfs_u64 size; + size_t diff; + int ret; + + if (!(flags & SQFS_BLK_ALIGN)) + return 0; + + size = wr->file->get_size(wr->file); + diff = size % wr->devblksz; + if (diff == 0) + return 0; + + padding = calloc(1, diff); + if (padding == 0) + return SQFS_ERROR_ALLOC; + + ret = wr->file->write_at(wr->file, size, padding, diff); + free(padding); + if (ret) + return ret; + + return store_block_location(wr, size, 0, 0); +} + +static void block_writer_destroy(sqfs_object_t *wr) +{ + sqfs_drop(((block_writer_default_t *)wr)->file); + array_cleanup(&(((block_writer_default_t *)wr)->blocks)); + free(wr); +} + +static int write_data_block(sqfs_block_writer_t *base, void *user, + sqfs_u32 size, sqfs_u32 checksum, sqfs_u32 flags, + const sqfs_u8 *data, sqfs_u64 *location) +{ + block_writer_default_t *wr = (block_writer_default_t *)base; + int err; + (void)user; + + if (flags & (SQFS_BLK_FIRST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { + err = align_file(wr, flags); + if (err) + return err; + + if (flags & SQFS_BLK_FIRST_BLOCK) + wr->file_start = wr->blocks.used; + } + + *location = wr->file->get_size(wr->file); + + if (size != 0 && !(flags & SQFS_BLK_IS_SPARSE)) { + sqfs_u32 out = size; + if (!(flags & SQFS_BLK_IS_COMPRESSED)) + out |= 1 << 24; + + err = store_block_location(wr, *location, out, checksum); + if (err) + return err; + + err = wr->file->write_at(wr->file, *location, data, size); + if (err) + return err; + } + + if (flags & (SQFS_BLK_LAST_BLOCK | SQFS_BLK_FRAGMENT_BLOCK)) { + err = align_file(wr, flags); + if (err) + return err; + + if (flags & SQFS_BLK_LAST_BLOCK) + return deduplicate_blocks(wr, flags, location); + } + + return 0; +} + +static sqfs_u64 get_block_count(const sqfs_block_writer_t *wr) +{ + return ((const block_writer_default_t *)wr)->blocks.used; +} + +sqfs_block_writer_t *sqfs_block_writer_create(sqfs_file_t *file, + size_t devblksz, sqfs_u32 flags) +{ + block_writer_default_t *wr; + + if (flags & ~SQFS_BLOCK_WRITER_ALL_FLAGS) + return NULL; + + if (flags & SQFS_BLOCK_WRITER_HASH_COMPARE_ONLY) { + wr = calloc(1, sizeof(*wr)); + } else { + wr = alloc_flex(sizeof(*wr), 1, SCRATCH_SIZE); + } + + if (wr == NULL) + return NULL; + + sqfs_object_init(wr, block_writer_destroy, NULL); + + ((sqfs_block_writer_t *)wr)->write_data_block = write_data_block; + ((sqfs_block_writer_t *)wr)->get_block_count = get_block_count; + wr->flags = flags; + wr->file = sqfs_grab(file); + wr->devblksz = devblksz; + + if (array_init(&(wr->blocks), sizeof(blk_info_t), INIT_BLOCK_COUNT)) { + sqfs_drop(wr->file); + free(wr); + return NULL; + } + + return (sqfs_block_writer_t *)wr; +} diff --git a/lib/sqfs/src/comp/compressor.c b/lib/sqfs/src/comp/compressor.c new file mode 100644 index 0000000..4c4d73c --- /dev/null +++ b/lib/sqfs/src/comp/compressor.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * compressor.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include + +#include "internal.h" + +typedef int (*compressor_fun_t)(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { +#ifdef WITH_GZIP + [SQFS_COMP_GZIP] = gzip_compressor_create, +#endif +#ifdef WITH_XZ + [SQFS_COMP_XZ] = xz_compressor_create, + [SQFS_COMP_LZMA] = lzma_compressor_create, +#endif +#ifdef WITH_LZ4 + [SQFS_COMP_LZ4] = lz4_compressor_create, +#endif +#ifdef WITH_ZSTD + [SQFS_COMP_ZSTD] = zstd_compressor_create, +#endif +}; + +static const char *names[] = { + [SQFS_COMP_GZIP] = "gzip", + [SQFS_COMP_LZMA] = "lzma", + [SQFS_COMP_LZO] = "lzo", + [SQFS_COMP_XZ] = "xz", + [SQFS_COMP_LZ4] = "lz4", + [SQFS_COMP_ZSTD] = "zstd", +}; + +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + header = htole16(0x8000 | size); + memcpy(buffer, &header, sizeof(header)); + memcpy(buffer + sizeof(header), data, size); + + ret = file->write_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + return sizeof(header) + size; +} + +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size) +{ + sqfs_u8 buffer[64]; + sqfs_u16 header; + int ret; + + /* XXX: options for all known compressors should fit into this */ + if (size >= (sizeof(buffer) - sizeof(header))) + return SQFS_ERROR_INTERNAL; + + ret = file->read_at(file, sizeof(sqfs_super_t), + buffer, sizeof(header) + size); + if (ret) + return ret; + + memcpy(&header, buffer, sizeof(header)); + + if (le16toh(header) != (0x8000 | size)) + return SQFS_ERROR_CORRUPTED; + + memcpy(data, buffer + 2, size); + return 0; +} + +int sqfs_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_u8 padd0[sizeof(cfg->opt)]; + int ret; + + *out = NULL; + + /* check compressor ID */ + if (cfg == NULL) + return SQFS_ERROR_ARG_INVALID; + + if (cfg->id < SQFS_COMP_MIN || cfg->id > SQFS_COMP_MAX) + return SQFS_ERROR_UNSUPPORTED; + + if (compressors[cfg->id] == NULL) + return SQFS_ERROR_UNSUPPORTED; + + /* make sure the padding bytes are cleared, so we could theoretically + turn them into option fields in the future and remain compatible */ + memset(padd0, 0, sizeof(padd0)); + + switch (cfg->id) { + case SQFS_COMP_XZ: + ret = memcmp(cfg->opt.xz.padd0, padd0, + sizeof(cfg->opt.xz.padd0)); + break; + case SQFS_COMP_LZMA: + ret = memcmp(cfg->opt.lzma.padd0, padd0, + sizeof(cfg->opt.lzma.padd0)); + break; + case SQFS_COMP_LZO: + ret = memcmp(cfg->opt.lzo.padd0, padd0, + sizeof(cfg->opt.lzo.padd0)); + break; + case SQFS_COMP_GZIP: + ret = memcmp(cfg->opt.gzip.padd0, padd0, + sizeof(cfg->opt.gzip.padd0)); + break; + default: + ret = memcmp(cfg->opt.padd0, padd0, sizeof(cfg->opt.padd0)); + break; + } + + if (ret != 0) + return SQFS_ERROR_ARG_INVALID; + + return compressors[cfg->id](cfg, out); +} + +const char *sqfs_compressor_name_from_id(SQFS_COMPRESSOR id) +{ + if (id < 0 || (size_t)id >= sizeof(names) / sizeof(names[0])) + return NULL; + + return names[id]; +} + +int sqfs_compressor_id_from_name(const char *name) +{ + size_t i; + + for (i = 0; i < sizeof(names) / sizeof(names[0]); ++i) { + if (names[i] != NULL && strcmp(names[i], name) == 0) + return i; + } + + return SQFS_ERROR_UNSUPPORTED; +} + +int sqfs_compressor_config_init(sqfs_compressor_config_t *cfg, + SQFS_COMPRESSOR id, + size_t block_size, sqfs_u16 flags) +{ + sqfs_u32 flag_mask = SQFS_COMP_FLAG_GENERIC_ALL; + + memset(cfg, 0, sizeof(*cfg)); + + switch (id) { + case SQFS_COMP_GZIP: + flag_mask |= SQFS_COMP_FLAG_GZIP_ALL; + cfg->level = SQFS_GZIP_DEFAULT_LEVEL; + cfg->opt.gzip.window_size = SQFS_GZIP_DEFAULT_WINDOW; + break; + case SQFS_COMP_LZO: + cfg->opt.lzo.algorithm = SQFS_LZO_DEFAULT_ALG; + cfg->level = SQFS_LZO_DEFAULT_LEVEL; + break; + case SQFS_COMP_ZSTD: + cfg->level = SQFS_ZSTD_DEFAULT_LEVEL; + break; + case SQFS_COMP_XZ: + flag_mask |= SQFS_COMP_FLAG_XZ_ALL; + cfg->level = SQFS_XZ_DEFAULT_LEVEL; + cfg->opt.xz.dict_size = block_size; + cfg->opt.xz.lc = SQFS_XZ_DEFAULT_LC; + cfg->opt.xz.lp = SQFS_XZ_DEFAULT_LP; + cfg->opt.xz.pb = SQFS_XZ_DEFAULT_PB; + + if (block_size < SQFS_XZ_MIN_DICT_SIZE) + cfg->opt.xz.dict_size = SQFS_XZ_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZMA: + flag_mask |= SQFS_COMP_FLAG_LZMA_ALL; + cfg->level = SQFS_LZMA_DEFAULT_LEVEL; + cfg->opt.lzma.dict_size = block_size; + cfg->opt.lzma.lc = SQFS_LZMA_DEFAULT_LC; + cfg->opt.lzma.lp = SQFS_LZMA_DEFAULT_LP; + cfg->opt.lzma.pb = SQFS_LZMA_DEFAULT_PB; + + if (block_size < SQFS_LZMA_MIN_DICT_SIZE) + cfg->opt.lzma.dict_size = SQFS_LZMA_MIN_DICT_SIZE; + break; + case SQFS_COMP_LZ4: + flag_mask |= SQFS_COMP_FLAG_LZ4_ALL; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + if (flags & ~flag_mask) { + memset(cfg, 0, sizeof(*cfg)); + return SQFS_ERROR_UNSUPPORTED; + } + + cfg->id = id; + cfg->flags = flags; + cfg->block_size = block_size; + return 0; +} diff --git a/lib/sqfs/src/comp/gzip.c b/lib/sqfs/src/comp/gzip.c new file mode 100644 index 0000000..beacfb8 --- /dev/null +++ b/lib/sqfs/src/comp/gzip.c @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * gzip.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_u32 level; + sqfs_u16 window; + sqfs_u16 strategies; +} gzip_options_t; + +typedef struct { + sqfs_compressor_t base; + + z_stream strm; + bool compress; + + size_t block_size; + gzip_options_t opt; +} gzip_compressor_t; + +static void gzip_destroy(sqfs_object_t *base) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + + if (gzip->compress) { + deflateEnd(&gzip->strm); + } else { + inflateEnd(&gzip->strm); + } + + free(gzip); +} + +static void gzip_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const gzip_compressor_t *gzip = (const gzip_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_GZIP; + cfg->flags = gzip->opt.strategies; + cfg->block_size = gzip->block_size; + cfg->level = gzip->opt.level; + cfg->opt.gzip.window_size = gzip->opt.window; + + if (!gzip->compress) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static int gzip_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + + if (gzip->opt.level == SQFS_GZIP_DEFAULT_LEVEL && + gzip->opt.window == SQFS_GZIP_DEFAULT_WINDOW && + gzip->opt.strategies == 0) { + return 0; + } + + opt.level = htole32(gzip->opt.level); + opt.window = htole16(gzip->opt.window); + opt.strategies = htole16(gzip->opt.strategies); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int gzip_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + gzip_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + gzip->opt.level = le32toh(opt.level); + gzip->opt.window = le16toh(opt.window); + gzip->opt.strategies = le16toh(opt.strategies); + + if (gzip->opt.level < 1 || gzip->opt.level > 9) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.window < 8 || gzip->opt.window > 15) + return SQFS_ERROR_UNSUPPORTED; + + if (gzip->opt.strategies & ~SQFS_COMP_FLAG_GZIP_ALL) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static int flag_to_zlib_strategy(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_GZIP_DEFAULT: + return Z_DEFAULT_STRATEGY; + case SQFS_COMP_FLAG_GZIP_FILTERED: + return Z_FILTERED; + case SQFS_COMP_FLAG_GZIP_HUFFMAN: + return Z_HUFFMAN_ONLY; + case SQFS_COMP_FLAG_GZIP_RLE: + return Z_RLE; + case SQFS_COMP_FLAG_GZIP_FIXED: + return Z_FIXED; + default: + break; + } + + return 0; +} + +static int find_strategy(gzip_compressor_t *gzip, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret, strategy, selected = Z_DEFAULT_STRATEGY; + size_t i, length, minlength = 0; + + for (i = 0x01; i & SQFS_COMP_FLAG_GZIP_ALL; i <<= 1) { + if ((gzip->opt.strategies & i) == 0) + continue; + + ret = deflateReset(&gzip->strm); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + strategy = flag_to_zlib_strategy(i); + + gzip->strm.next_in = (z_const Bytef *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + ret = deflate(&gzip->strm, Z_FINISH); + + if (ret == Z_STREAM_END) { + length = gzip->strm.total_out; + + if (minlength == 0 || length < minlength) { + minlength = length; + selected = strategy; + } + } else if (ret != Z_OK && ret != Z_BUF_ERROR) { + return SQFS_ERROR_COMPRESSOR; + } + } + + return selected; +} + +static sqfs_s32 gzip_do_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + gzip_compressor_t *gzip = (gzip_compressor_t *)base; + int ret, strategy = 0; + size_t written; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (gzip->compress && gzip->opt.strategies != 0) { + strategy = find_strategy(gzip, in, size, out, outsize); + if (strategy < 0) + return strategy; + } + + if (gzip->compress) { + ret = deflateReset(&gzip->strm); + } else { + ret = inflateReset(&gzip->strm); + } + + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + + gzip->strm.next_in = (const void *)in; + gzip->strm.avail_in = size; + gzip->strm.next_out = out; + gzip->strm.avail_out = outsize; + + if (gzip->compress && gzip->opt.strategies != 0) { + ret = deflateParams(&gzip->strm, gzip->opt.level, strategy); + if (ret != Z_OK) + return SQFS_ERROR_COMPRESSOR; + } + + if (gzip->compress) { + ret = deflate(&gzip->strm, Z_FINISH); + } else { + ret = inflate(&gzip->strm, Z_FINISH); + } + + if (ret == Z_STREAM_END) { + written = gzip->strm.total_out; + + if (gzip->compress && written >= size) + return 0; + + return written; + } + + if (ret != Z_OK && ret != Z_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static sqfs_object_t *gzip_create_copy(const sqfs_object_t *cmp) +{ + gzip_compressor_t *gzip = malloc(sizeof(*gzip)); + int ret; + + if (gzip == NULL) + return NULL; + + memcpy(gzip, cmp, sizeof(*gzip)); + memset(&gzip->strm, 0, sizeof(gzip->strm)); + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, gzip->opt.level, Z_DEFLATED, + gzip->opt.window, 8, Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return NULL; + } + + return (sqfs_object_t *)gzip; +} + +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + gzip_compressor_t *gzip; + sqfs_compressor_t *base; + int ret; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GZIP_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level < SQFS_GZIP_MIN_LEVEL || + cfg->level > SQFS_GZIP_MAX_LEVEL) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->opt.gzip.window_size < SQFS_GZIP_MIN_WINDOW || + cfg->opt.gzip.window_size > SQFS_GZIP_MAX_WINDOW) { + return SQFS_ERROR_UNSUPPORTED; + } + + gzip = calloc(1, sizeof(*gzip)); + base = (sqfs_compressor_t *)gzip; + + if (gzip == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(gzip, gzip_destroy, gzip_create_copy); + + gzip->opt.level = cfg->level; + gzip->opt.window = cfg->opt.gzip.window_size; + gzip->opt.strategies = cfg->flags & SQFS_COMP_FLAG_GZIP_ALL; + gzip->compress = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) == 0; + gzip->block_size = cfg->block_size; + base->get_configuration = gzip_get_configuration; + base->do_block = gzip_do_block; + base->write_options = gzip_write_options; + base->read_options = gzip_read_options; + + if (gzip->compress) { + ret = deflateInit2(&gzip->strm, cfg->level, + Z_DEFLATED, cfg->opt.gzip.window_size, 8, + Z_DEFAULT_STRATEGY); + } else { + ret = inflateInit(&gzip->strm); + } + + if (ret != Z_OK) { + free(gzip); + return SQFS_ERROR_COMPRESSOR; + } + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/internal.h b/lib/sqfs/src/comp/internal.h new file mode 100644 index 0000000..e4c3dd8 --- /dev/null +++ b/lib/sqfs/src/comp/internal.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef INTERNAL_H +#define INTERNAL_H + +#include "config.h" + +#include "sqfs/predef.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +SQFS_INTERNAL +int sqfs_generic_write_options(sqfs_file_t *file, const void *data, + size_t size); + +SQFS_INTERNAL +int sqfs_generic_read_options(sqfs_file_t *file, void *data, size_t size); + +SQFS_INTERNAL +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int gzip_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +SQFS_INTERNAL +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out); + +#endif /* INTERNAL_H */ diff --git a/lib/sqfs/src/comp/lz4.c b/lib/sqfs/src/comp/lz4.c new file mode 100644 index 0000000..77f4a6e --- /dev/null +++ b/lib/sqfs/src/comp/lz4.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lz4.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + bool high_compression; +} lz4_compressor_t; + +typedef struct { + sqfs_u32 version; + sqfs_u32 flags; +} lz4_options; + +#define LZ4LEGACY 1 + +/* old verions of liblz4 don't have this */ +#ifndef LZ4HC_CLEVEL_MAX +#define LZ4HC_CLEVEL_MAX 12 +#endif + +static int lz4_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + lz4_options opt = { + .version = htole32(LZ4LEGACY), + .flags = htole32(lz4->high_compression ? + SQFS_COMP_FLAG_LZ4_HC : 0), + }; + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int lz4_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + lz4_options opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.version = le32toh(opt.version); + opt.flags = le32toh(opt.flags); + + if (opt.version != LZ4LEGACY) + return SQFS_ERROR_UNSUPPORTED; + + return 0; +} + +static sqfs_s32 lz4_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lz4_compressor_t *lz4 = (lz4_compressor_t *)base; + int ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (lz4->high_compression) { + ret = LZ4_compress_HC((const void *)in, (void *)out, + size, outsize, LZ4HC_CLEVEL_MAX); + } else { + ret = LZ4_compress_default((const void *)in, (void *)out, + size, outsize); + } + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static sqfs_s32 lz4_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + int ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = LZ4_decompress_safe((const void *)in, (void *)out, size, outsize); + + if (ret < 0) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void lz4_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lz4_compressor_t *lz4 = (const lz4_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZ4; + cfg->block_size = lz4->block_size; + + if (lz4->high_compression) + cfg->flags |= SQFS_COMP_FLAG_LZ4_HC; + + if (base->do_block == lz4_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *lz4_create_copy(const sqfs_object_t *cmp) +{ + lz4_compressor_t *lz4 = malloc(sizeof(*lz4)); + + if (lz4 == NULL) + return NULL; + + memcpy(lz4, cmp, sizeof(*lz4)); + return (sqfs_object_t *)lz4; +} + +static void lz4_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lz4_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lz4_compressor_t *lz4; + + if (cfg->flags & ~(SQFS_COMP_FLAG_LZ4_ALL | + SQFS_COMP_FLAG_GENERIC_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (cfg->level != 0) + return SQFS_ERROR_UNSUPPORTED; + + lz4 = calloc(1, sizeof(*lz4)); + base = (sqfs_compressor_t *)lz4; + if (lz4 == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lz4, lz4_destroy, lz4_create_copy); + + lz4->high_compression = (cfg->flags & SQFS_COMP_FLAG_LZ4_HC) != 0; + lz4->block_size = cfg->block_size; + + base->get_configuration = lz4_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lz4_uncomp_block : lz4_comp_block; + base->write_options = lz4_write_options; + base->read_options = lz4_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/lzma.c b/lib/sqfs/src/comp/lzma.c new file mode 100644 index 0000000..5456603 --- /dev/null +++ b/lib/sqfs/src/comp/lzma.c @@ -0,0 +1,281 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * lzma.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +#define LZMA_SIZE_OFFSET (5) +#define LZMA_SIZE_BYTES (8) +#define LZMA_HEADER_SIZE (13) + +#define MEMLIMIT (64 * 1024 * 1024) + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u32 flags; + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; +} lzma_compressor_t; + +static int lzma_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; (void)file; + return 0; +} + +static int lzma_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + (void)base; + (void)file; + return SQFS_ERROR_UNSUPPORTED; +} + +static sqfs_s32 try_compress(lzma_compressor_t *lzma, sqfs_u32 preset, + const sqfs_u8 *in, size_t size, + sqfs_u8 *out, size_t outsize) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_options_lzma opt; + int ret; + + lzma_lzma_preset(&opt, preset); + opt.dict_size = lzma->block_size; + opt.lc = lzma->lc; + opt.lp = lzma->lp; + opt.pb = lzma->pb; + + if (lzma_alone_encoder(&strm, &opt) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = in; + strm.avail_in = size; + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END) + return ret == LZMA_OK ? 0 : SQFS_ERROR_COMPRESSOR; + + if (strm.total_out > size) + return 0; + + out[LZMA_SIZE_OFFSET ] = size & 0xFF; + out[LZMA_SIZE_OFFSET + 1] = (size >> 8) & 0xFF; + out[LZMA_SIZE_OFFSET + 2] = (size >> 16) & 0xFF; + out[LZMA_SIZE_OFFSET + 3] = (size >> 24) & 0xFF; + out[LZMA_SIZE_OFFSET + 4] = 0; + out[LZMA_SIZE_OFFSET + 5] = 0; + out[LZMA_SIZE_OFFSET + 6] = 0; + out[LZMA_SIZE_OFFSET + 7] = 0; + return strm.total_out; +} + +static sqfs_s32 lzma_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + lzma_compressor_t *lzma = (lzma_compressor_t *)base; + sqfs_s32 ret, smallest; + sqfs_u32 preset; + + if (outsize < LZMA_HEADER_SIZE || size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + preset = lzma->level; + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || !(lzma->flags & SQFS_COMP_FLAG_LZMA_EXTREME)) + return ret; + + preset |= LZMA_PRESET_EXTREME; + smallest = ret; + + ret = try_compress(lzma, preset, in, size, out, outsize); + if (ret < 0 || (ret > 0 && (smallest == 0 || ret < smallest))) + return ret; + + preset &= ~LZMA_PRESET_EXTREME; + return smallest == 0 ? 0 : + try_compress(lzma, preset, in, size, out, outsize); +} + +static sqfs_s32 lzma_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u8 lzma_header[LZMA_HEADER_SIZE]; + lzma_stream strm = LZMA_STREAM_INIT; + size_t hdrsize; + int ret; + (void)base; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + if (size < sizeof(lzma_header)) + return SQFS_ERROR_CORRUPTED; + + hdrsize = (size_t)in[LZMA_SIZE_OFFSET] | + ((size_t)in[LZMA_SIZE_OFFSET + 1] << 8) | + ((size_t)in[LZMA_SIZE_OFFSET + 2] << 16) | + ((size_t)in[LZMA_SIZE_OFFSET + 3] << 24); + + if (hdrsize > outsize) + return 0; + + if (lzma_alone_decoder(&strm, MEMLIMIT) != LZMA_OK) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + memcpy(lzma_header, in, sizeof(lzma_header)); + memset(lzma_header + LZMA_SIZE_OFFSET, 0xFF, LZMA_SIZE_BYTES); + + strm.next_out = out; + strm.avail_out = outsize; + strm.next_in = lzma_header; + strm.avail_in = sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_RUN); + + if (ret != LZMA_OK || strm.avail_in != 0) { + lzma_end(&strm); + return SQFS_ERROR_COMPRESSOR; + } + + strm.next_in = in + sizeof(lzma_header); + strm.avail_in = size - sizeof(lzma_header); + + ret = lzma_code(&strm, LZMA_FINISH); + lzma_end(&strm); + + if (ret != LZMA_STREAM_END && ret != LZMA_OK) + return SQFS_ERROR_COMPRESSOR; + + if (ret == LZMA_OK) { + if (strm.total_out < hdrsize || strm.avail_in != 0) + return 0; + } + + return hdrsize; +} + +static void lzma_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const lzma_compressor_t *lzma = (const lzma_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_LZMA; + cfg->block_size = lzma->block_size; + cfg->flags = lzma->flags; + cfg->level = lzma->level; + cfg->opt.lzma.dict_size = lzma->dict_size; + cfg->opt.lzma.lc = lzma->lc; + cfg->opt.lzma.lp = lzma->lp; + cfg->opt.lzma.pb = lzma->pb; +} + +static sqfs_object_t *lzma_create_copy(const sqfs_object_t *cmp) +{ + lzma_compressor_t *copy = malloc(sizeof(*copy)); + + if (copy != NULL) + memcpy(copy, cmp, sizeof(*copy)); + + return (sqfs_object_t *)copy; +} + +static void lzma_destroy(sqfs_object_t *base) +{ + free(base); +} + +int lzma_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + lzma_compressor_t *lzma; + sqfs_u32 mask; + + mask = SQFS_COMP_FLAG_GENERIC_ALL | SQFS_COMP_FLAG_LZMA_ALL; + + if (cfg->flags & ~mask) + return SQFS_ERROR_UNSUPPORTED; + + /* XXX: values are unsigned and minimum is 0 */ + if (cfg->level > SQFS_LZMA_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc > SQFS_LZMA_MAX_LC) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lp > SQFS_LZMA_MAX_LP) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.pb > SQFS_LZMA_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.lc + cfg->opt.lzma.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size == 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size < SQFS_LZMA_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size > SQFS_LZMA_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + mask = cfg->opt.lzma.dict_size; + mask &= mask - 1; + + if (mask != 0) { + if ((mask & (mask - 1)) != 0) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.lzma.dict_size != (mask | mask >> 1)) + return SQFS_ERROR_UNSUPPORTED; + } + + lzma = calloc(1, sizeof(*lzma)); + base = (sqfs_compressor_t *)lzma; + if (lzma == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(lzma, lzma_destroy, lzma_create_copy); + + lzma->block_size = cfg->block_size; + lzma->flags = cfg->flags; + lzma->level = cfg->level; + lzma->dict_size = cfg->opt.lzma.dict_size; + lzma->lc = cfg->opt.lzma.lc; + lzma->lp = cfg->opt.lzma.lp; + lzma->pb = cfg->opt.lzma.pb; + + base->get_configuration = lzma_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + lzma_uncomp_block : lzma_comp_block; + base->write_options = lzma_write_options; + base->read_options = lzma_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/xz.c b/lib/sqfs/src/comp/xz.c new file mode 100644 index 0000000..13545ed --- /dev/null +++ b/lib/sqfs/src/comp/xz.c @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xz.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + size_t dict_size; + + sqfs_u8 level; + sqfs_u8 lc; + sqfs_u8 lp; + sqfs_u8 pb; + + int flags; +} xz_compressor_t; + +typedef struct { + sqfs_u32 dict_size; + sqfs_u32 flags; +} xz_options_t; + +static bool is_dict_size_valid(size_t size) +{ + size_t x = size & (size - 1); + + if (x == 0) + return true; + + return size == (x | (x >> 1)); +} + +static int xz_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + sqfs_u32 flags; + + if (xz->flags == 0 && xz->dict_size == xz->block_size) + return 0; + + flags = xz->flags & SQFS_COMP_FLAG_XZ_ALL; + flags &= ~SQFS_COMP_FLAG_XZ_EXTREME; + + opt.dict_size = htole32(xz->dict_size); + opt.flags = htole32(flags); + + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int xz_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + xz_options_t opt; + int ret; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.dict_size = le32toh(opt.dict_size); + opt.flags = le32toh(opt.flags); + + if (!is_dict_size_valid(opt.dict_size)) + return SQFS_ERROR_CORRUPTED; + + if (opt.flags & ~SQFS_COMP_FLAG_XZ_ALL) + return SQFS_ERROR_UNSUPPORTED; + + xz->flags = opt.flags; + xz->dict_size = opt.dict_size; + return 0; +} + +static sqfs_s32 compress(xz_compressor_t *xz, lzma_vli filter, + const sqfs_u8 *in, sqfs_u32 size, + sqfs_u8 *out, sqfs_u32 outsize, + sqfs_u32 presets) +{ + lzma_filter filters[5]; + lzma_options_lzma opt; + size_t written = 0; + lzma_ret ret; + int i = 0; + + if (lzma_lzma_preset(&opt, presets)) + return SQFS_ERROR_COMPRESSOR; + + opt.lc = xz->lc; + opt.lp = xz->lp; + opt.pb = xz->pb; + opt.dict_size = xz->dict_size; + + if (filter != LZMA_VLI_UNKNOWN) { + filters[i].id = filter; + filters[i].options = NULL; + ++i; + } + + filters[i].id = LZMA_FILTER_LZMA2; + filters[i].options = &opt; + ++i; + + filters[i].id = LZMA_VLI_UNKNOWN; + filters[i].options = NULL; + ++i; + + ret = lzma_stream_buffer_encode(filters, LZMA_CHECK_CRC32, NULL, + in, size, out, &written, outsize); + + if (ret == LZMA_OK) + return (written >= size) ? 0 : written; + + if (ret != LZMA_BUF_ERROR) + return SQFS_ERROR_COMPRESSOR; + + return 0; +} + +static lzma_vli flag_to_vli(int flag) +{ + switch (flag) { + case SQFS_COMP_FLAG_XZ_X86: + return LZMA_FILTER_X86; + case SQFS_COMP_FLAG_XZ_POWERPC: + return LZMA_FILTER_POWERPC; + case SQFS_COMP_FLAG_XZ_IA64: + return LZMA_FILTER_IA64; + case SQFS_COMP_FLAG_XZ_ARM: + return LZMA_FILTER_ARM; + case SQFS_COMP_FLAG_XZ_ARMTHUMB: + return LZMA_FILTER_ARMTHUMB; + case SQFS_COMP_FLAG_XZ_SPARC: + return LZMA_FILTER_SPARC; + default: + break; + } + + return LZMA_VLI_UNKNOWN; +} + +static sqfs_s32 xz_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + xz_compressor_t *xz = (xz_compressor_t *)base; + lzma_vli filter, selected = LZMA_VLI_UNKNOWN; + sqfs_s32 ret, smallest; + bool extreme; + size_t i; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, + outsize, xz->level); + if (ret < 0 || xz->flags == 0) + return ret; + + smallest = ret; + extreme = false; + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, LZMA_VLI_UNKNOWN, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + extreme = true; + } + } + + for (i = 1; i & SQFS_COMP_FLAG_XZ_ALL; i <<= 1) { + if ((i & SQFS_COMP_FLAG_XZ_EXTREME) || (xz->flags & i) == 0) + continue; + + filter = flag_to_vli(i); + + ret = compress(xz, filter, in, size, out, outsize, xz->level); + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = false; + } + + if (xz->flags & SQFS_COMP_FLAG_XZ_EXTREME) { + ret = compress(xz, filter, in, size, out, outsize, + xz->level | LZMA_PRESET_EXTREME); + + if (ret > 0 && (smallest == 0 || ret < smallest)) { + smallest = ret; + selected = filter; + extreme = true; + } + } + } + + if (smallest == 0) + return 0; + + return compress(xz, selected, in, size, out, outsize, + xz->level | (extreme ? LZMA_PRESET_EXTREME : 0)); +} + +static sqfs_s32 xz_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + sqfs_u64 memlimit = 65 * 1024 * 1024; + size_t dest_pos = 0; + size_t src_pos = 0; + lzma_ret ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = lzma_stream_buffer_decode(&memlimit, 0, NULL, + in, &src_pos, size, + out, &dest_pos, outsize); + + if (ret == LZMA_OK && size == src_pos) + return dest_pos; + + return SQFS_ERROR_COMPRESSOR; +} + +static void xz_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const xz_compressor_t *xz = (const xz_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_XZ; + cfg->flags = xz->flags; + cfg->block_size = xz->block_size; + cfg->level = xz->level; + cfg->opt.xz.dict_size = xz->dict_size; + cfg->opt.xz.lc = xz->lc; + cfg->opt.xz.lp = xz->lp; + cfg->opt.xz.pb = xz->pb; + + if (base->do_block == xz_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *xz_create_copy(const sqfs_object_t *cmp) +{ + xz_compressor_t *xz = malloc(sizeof(*xz)); + + if (xz == NULL) + return NULL; + + memcpy(xz, cmp, sizeof(*xz)); + return (sqfs_object_t *)xz; +} + +static void xz_destroy(sqfs_object_t *base) +{ + free(base); +} + +int xz_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + sqfs_compressor_t *base; + xz_compressor_t *xz; + + if (cfg->flags & ~(SQFS_COMP_FLAG_GENERIC_ALL | + SQFS_COMP_FLAG_XZ_ALL)) { + return SQFS_ERROR_UNSUPPORTED; + } + + if (!is_dict_size_valid(cfg->opt.xz.dict_size)) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.lc + cfg->opt.xz.lp > 4) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.pb > SQFS_XZ_MAX_PB) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level > SQFS_XZ_MAX_LEVEL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size < SQFS_XZ_MIN_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->opt.xz.dict_size > SQFS_XZ_MAX_DICT_SIZE) + return SQFS_ERROR_UNSUPPORTED; + + xz = calloc(1, sizeof(*xz)); + base = (sqfs_compressor_t *)xz; + if (xz == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(xz, xz_destroy, xz_create_copy); + + xz->flags = cfg->flags; + xz->dict_size = cfg->opt.xz.dict_size; + xz->block_size = cfg->block_size; + xz->lc = cfg->opt.xz.lc; + xz->lp = cfg->opt.xz.lp; + xz->pb = cfg->opt.xz.pb; + xz->level = cfg->level; + base->get_configuration = xz_get_configuration; + base->do_block = (cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS) ? + xz_uncomp_block : xz_comp_block; + base->write_options = xz_write_options; + base->read_options = xz_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/comp/zstd.c b/lib/sqfs/src/comp/zstd.c new file mode 100644 index 0000000..a6d7975 --- /dev/null +++ b/lib/sqfs/src/comp/zstd.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * zstd.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + sqfs_compressor_t base; + size_t block_size; + ZSTD_CCtx *zctx; + int level; +} zstd_compressor_t; + +typedef struct { + sqfs_u32 level; +} zstd_options_t; + +static int zstd_write_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + zstd_options_t opt; + + if (zstd->level == SQFS_ZSTD_DEFAULT_LEVEL) + return 0; + + opt.level = htole32(zstd->level); + return sqfs_generic_write_options(file, &opt, sizeof(opt)); +} + +static int zstd_read_options(sqfs_compressor_t *base, sqfs_file_t *file) +{ + zstd_options_t opt; + int ret; + (void)base; + + ret = sqfs_generic_read_options(file, &opt, sizeof(opt)); + if (ret) + return ret; + + opt.level = le32toh(opt.level); + return 0; +} + +static sqfs_s32 zstd_comp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + size_t ret; + + if (size >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_compressCCtx(zstd->zctx, out, outsize, in, size, + zstd->level); + + if (ZSTD_isError(ret)) { + if (ZSTD_getErrorCode(ret) == ZSTD_error_dstSize_tooSmall) + return 0; + + return SQFS_ERROR_COMPRESSOR; + } + + return ret < size ? ret : 0; +} + +static sqfs_s32 zstd_uncomp_block(sqfs_compressor_t *base, const sqfs_u8 *in, + sqfs_u32 size, sqfs_u8 *out, sqfs_u32 outsize) +{ + size_t ret; + (void)base; + + if (outsize >= 0x7FFFFFFF) + return SQFS_ERROR_ARG_INVALID; + + ret = ZSTD_decompress(out, outsize, in, size); + + if (ZSTD_isError(ret)) + return SQFS_ERROR_COMPRESSOR; + + return ret; +} + +static void zstd_get_configuration(const sqfs_compressor_t *base, + sqfs_compressor_config_t *cfg) +{ + const zstd_compressor_t *zstd = (const zstd_compressor_t *)base; + + memset(cfg, 0, sizeof(*cfg)); + cfg->id = SQFS_COMP_ZSTD; + + cfg->block_size = zstd->block_size; + cfg->level = zstd->level; + + if (base->do_block == zstd_uncomp_block) + cfg->flags |= SQFS_COMP_FLAG_UNCOMPRESS; +} + +static sqfs_object_t *zstd_create_copy(const sqfs_object_t *cmp) +{ + zstd_compressor_t *zstd = malloc(sizeof(*zstd)); + + if (zstd == NULL) + return NULL; + + memcpy(zstd, cmp, sizeof(*zstd)); + + zstd->zctx = ZSTD_createCCtx(); + + if (zstd->zctx == NULL) { + free(zstd); + return NULL; + } + + return (sqfs_object_t *)zstd; +} + +static void zstd_destroy(sqfs_object_t *base) +{ + zstd_compressor_t *zstd = (zstd_compressor_t *)base; + + ZSTD_freeCCtx(zstd->zctx); + free(zstd); +} + +int zstd_compressor_create(const sqfs_compressor_config_t *cfg, + sqfs_compressor_t **out) +{ + zstd_compressor_t *zstd; + sqfs_compressor_t *base; + + if (cfg->flags & ~SQFS_COMP_FLAG_GENERIC_ALL) + return SQFS_ERROR_UNSUPPORTED; + + if (cfg->level < 1 || cfg->level > (unsigned int)ZSTD_maxCLevel()) + return SQFS_ERROR_UNSUPPORTED; + + zstd = calloc(1, sizeof(*zstd)); + base = (sqfs_compressor_t *)zstd; + if (zstd == NULL) + return SQFS_ERROR_ALLOC; + + sqfs_object_init(zstd, zstd_destroy, zstd_create_copy); + + zstd->block_size = cfg->block_size; + zstd->level = cfg->level; + zstd->zctx = ZSTD_createCCtx(); + if (zstd->zctx == NULL) { + free(zstd); + return SQFS_ERROR_COMPRESSOR; + } + + base->get_configuration = zstd_get_configuration; + base->do_block = cfg->flags & SQFS_COMP_FLAG_UNCOMPRESS ? + zstd_uncomp_block : zstd_comp_block; + base->write_options = zstd_write_options; + base->read_options = zstd_read_options; + + *out = base; + return 0; +} diff --git a/lib/sqfs/src/data_reader.c b/lib/sqfs/src/data_reader.c new file mode 100644 index 0000000..3f0cd74 --- /dev/null +++ b/lib/sqfs/src/data_reader.c @@ -0,0 +1,374 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * data_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/data_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/frag_table.h" +#include "sqfs/block.h" +#include "sqfs/error.h" +#include "sqfs/table.h" +#include "sqfs/inode.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +struct sqfs_data_reader_t { + sqfs_object_t obj; + + sqfs_frag_table_t *frag_tbl; + sqfs_compressor_t *cmp; + sqfs_file_t *file; + + sqfs_u8 *data_block; + size_t data_blk_size; + sqfs_u64 current_block; + + sqfs_u8 *frag_block; + size_t frag_blk_size; + sqfs_u32 current_frag_index; + sqfs_u32 block_size; + + sqfs_u8 scratch[]; +}; + +static int get_block(sqfs_data_reader_t *data, sqfs_u64 off, sqfs_u32 size, + sqfs_u32 max_size, size_t *out_sz, sqfs_u8 **out) +{ + sqfs_u32 on_disk_size; + sqfs_s32 ret; + int err; + + *out = alloc_array(1, max_size); + *out_sz = max_size; + + if (*out == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail; + } + + if (SQFS_IS_SPARSE_BLOCK(size)) + return 0; + + on_disk_size = SQFS_ON_DISK_BLOCK_SIZE(size); + + if (on_disk_size > max_size) { + err = SQFS_ERROR_OVERFLOW; + goto fail; + } + + if (SQFS_IS_BLOCK_COMPRESSED(size)) { + err = data->file->read_at(data->file, off, + data->scratch, on_disk_size); + if (err) + goto fail; + + ret = data->cmp->do_block(data->cmp, data->scratch, + on_disk_size, *out, max_size); + if (ret <= 0) { + err = ret < 0 ? ret : SQFS_ERROR_OVERFLOW; + goto fail; + } + + *out_sz = ret; + } else { + err = data->file->read_at(data->file, off, + *out, on_disk_size); + if (err) + goto fail; + + *out_sz = on_disk_size; + } + + return 0; +fail: + free(*out); + *out = NULL; + *out_sz = 0; + return err; +} + +static int precache_data_block(sqfs_data_reader_t *data, sqfs_u64 location, + sqfs_u32 size) +{ + if (data->data_block != NULL && data->current_block == location) + return 0; + + free(data->data_block); + data->current_block = location; + + return get_block(data, location, size, data->block_size, + &data->data_blk_size, &data->data_block); +} + +static int precache_fragment_block(sqfs_data_reader_t *data, size_t idx) +{ + sqfs_fragment_t ent; + int ret; + + if (data->frag_block != NULL && idx == data->current_frag_index) + return 0; + + ret = sqfs_frag_table_lookup(data->frag_tbl, idx, &ent); + if (ret != 0) + return ret; + + free(data->frag_block); + data->current_frag_index = idx; + + return get_block(data, ent.start_offset, ent.size, data->block_size, + &data->frag_blk_size, &data->frag_block); +} + +static void data_reader_destroy(sqfs_object_t *obj) +{ + sqfs_data_reader_t *data = (sqfs_data_reader_t *)obj; + + sqfs_drop(data->cmp); + sqfs_drop(data->file); + sqfs_drop(data->frag_tbl); + free(data->data_block); + free(data->frag_block); + free(data); +} + +static sqfs_object_t *data_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_data_reader_t *data = (const sqfs_data_reader_t *)obj; + sqfs_data_reader_t *copy; + + copy = alloc_flex(sizeof(*data), 1, data->block_size); + if (copy == NULL) + return NULL; + + memcpy(copy, data, sizeof(*data) + data->block_size); + + copy->frag_tbl = sqfs_copy(data->frag_tbl); + if (copy->frag_tbl == NULL) + goto fail_ftbl; + + if (data->data_block != NULL) { + copy->data_block = malloc(data->data_blk_size); + if (copy->data_block == NULL) + goto fail_dblk; + + memcpy(copy->data_block, data->data_block, + data->data_blk_size); + } + + if (copy->frag_block != NULL) { + copy->frag_block = malloc(copy->frag_blk_size); + if (copy->frag_block == NULL) + goto fail_fblk; + + memcpy(copy->frag_block, data->frag_block, + data->frag_blk_size); + } + + /* duplicate references */ + copy->file = sqfs_grab(copy->file); + copy->cmp = sqfs_grab(copy->cmp); + return (sqfs_object_t *)copy; +fail_fblk: + free(copy->data_block); +fail_dblk: + sqfs_drop(copy->frag_tbl); +fail_ftbl: + free(copy); + return NULL; +} + +sqfs_data_reader_t *sqfs_data_reader_create(sqfs_file_t *file, + size_t block_size, + sqfs_compressor_t *cmp, + sqfs_u32 flags) +{ + sqfs_data_reader_t *data; + + if (flags != 0) + return NULL; + + data = alloc_flex(sizeof(*data), 1, block_size); + if (data == NULL) + return NULL; + + sqfs_object_init(data, data_reader_destroy, data_reader_copy); + + data->frag_tbl = sqfs_frag_table_create(0); + if (data->frag_tbl == NULL) { + free(data); + return NULL; + } + + data->file = sqfs_grab(file); + data->block_size = block_size; + data->cmp = sqfs_grab(cmp); + return data; +} + +int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data, + const sqfs_super_t *super) +{ + int ret; + + free(data->frag_block); + data->frag_block = NULL; + data->current_frag_index = 0; + + ret = sqfs_frag_table_read(data->frag_tbl, data->file, + super, data->cmp); + if (ret != 0) + return ret; + + data->current_frag_index = sqfs_frag_table_get_size(data->frag_tbl); + return 0; +} + +int sqfs_data_reader_get_block(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t index, size_t *size, sqfs_u8 **out) +{ + size_t i, unpacked_size; + sqfs_u64 off, filesz; + + sqfs_inode_get_file_block_start(inode, &off); + sqfs_inode_get_file_size(inode, &filesz); + + if (index >= sqfs_inode_get_file_block_count(inode)) + return SQFS_ERROR_OUT_OF_BOUNDS; + + for (i = 0; i < index; ++i) { + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + filesz -= data->block_size; + } + + unpacked_size = filesz < data->block_size ? filesz : data->block_size; + + return get_block(data, off, inode->extra[index], + unpacked_size, size, out); +} + +int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + size_t *size, sqfs_u8 **out) +{ + sqfs_u32 frag_idx, frag_off, frag_sz; + size_t block_count; + sqfs_u64 filesz; + int err; + + sqfs_inode_get_file_size(inode, &filesz); + sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); + *size = 0; + *out = NULL; + + block_count = sqfs_inode_get_file_block_count(inode); + + if (block_count > (UINT64_MAX / data->block_size)) + return SQFS_ERROR_OVERFLOW; + + if ((sqfs_u64)block_count * data->block_size >= filesz) + return 0; + + frag_sz = filesz % data->block_size; + + err = precache_fragment_block(data, frag_idx); + if (err) + return err; + + if (frag_off + frag_sz > data->block_size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + *out = alloc_array(1, frag_sz); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + *size = frag_sz; + memcpy(*out, (char *)data->frag_block + frag_off, frag_sz); + return 0; +} + +sqfs_s32 sqfs_data_reader_read(sqfs_data_reader_t *data, + const sqfs_inode_generic_t *inode, + sqfs_u64 offset, void *buffer, sqfs_u32 size) +{ + sqfs_u32 frag_idx, frag_off, diff, total = 0; + size_t i, block_count; + sqfs_u64 off, filesz; + char *ptr; + int err; + + if (size >= 0x7FFFFFFF) + size = 0x7FFFFFFE; + + /* work out file location and size */ + sqfs_inode_get_file_size(inode, &filesz); + sqfs_inode_get_frag_location(inode, &frag_idx, &frag_off); + sqfs_inode_get_file_block_start(inode, &off); + block_count = sqfs_inode_get_file_block_count(inode); + + if (offset >= filesz) + return 0; + + if ((filesz - offset) < (sqfs_u64)size) + size = filesz - offset; + + if (size == 0) + return 0; + + /* find location of the first block */ + for (i = 0; offset > data->block_size && i < block_count; ++i) { + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + offset -= data->block_size; + } + + /* copy data from blocks */ + while (i < block_count && size > 0) { + diff = data->block_size - offset; + if (size < diff) + diff = size; + + if (SQFS_IS_SPARSE_BLOCK(inode->extra[i])) { + memset(buffer, 0, diff); + } else { + err = precache_data_block(data, off, inode->extra[i]); + if (err) + return err; + + memcpy(buffer, (char *)data->data_block + offset, diff); + off += SQFS_ON_DISK_BLOCK_SIZE(inode->extra[i]); + } + + ++i; + offset = 0; + size -= diff; + total += diff; + buffer = (char *)buffer + diff; + } + + /* copy from fragment */ + if (size > 0) { + err = precache_fragment_block(data, frag_idx); + if (err) + return err; + + if ((frag_off + offset) >= data->frag_blk_size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if ((data->frag_blk_size - (frag_off + offset)) < size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + ptr = (char *)data->frag_block + frag_off + offset; + memcpy(buffer, ptr, size); + total += size; + } + + return total; +} diff --git a/lib/sqfs/src/dir_reader/dir_reader.c b/lib/sqfs/src/dir_reader/dir_reader.c new file mode 100644 index 0000000..d70f729 --- /dev/null +++ b/lib/sqfs/src/dir_reader/dir_reader.c @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * fs_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int inode_copy(const sqfs_inode_generic_t *inode, + sqfs_inode_generic_t **out) +{ + *out = alloc_flex(sizeof(*inode), 1, inode->payload_bytes_used); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + memcpy(*out, inode, sizeof(*inode) + inode->payload_bytes_used); + return 0; +} + +static int dcache_key_compare(const void *ctx, const void *l, const void *r) +{ + sqfs_u32 lhs = *((const sqfs_u32 *)l), rhs = *((const sqfs_u32 *)r); + (void)ctx; + + return lhs < rhs ? -1 : (lhs > rhs ? 1 : 0); +} + +static int dcache_add(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *inode, sqfs_u64 ref) +{ + sqfs_u32 inum = inode->base.inode_number; + + if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) + return 0; + + if (inode->base.type != SQFS_INODE_DIR && + inode->base.type != SQFS_INODE_EXT_DIR) { + return 0; + } + + if (rbtree_lookup(&rd->dcache, &inum) != NULL) + return 0; + + return rbtree_insert(&rd->dcache, &inum, &ref); +} + +static int dcache_find(sqfs_dir_reader_t *rd, sqfs_u32 inode, sqfs_u64 *ref) +{ + rbtree_node_t *node; + + if (!(rd->flags & SQFS_DIR_READER_DOT_ENTRIES)) + return SQFS_ERROR_NO_ENTRY; + + node = rbtree_lookup(&rd->dcache, &inode); + if (node == NULL) + return SQFS_ERROR_NO_ENTRY; + + *ref = *((sqfs_u64 *)rbtree_node_value(node)); + return 0; +} + +static void dir_reader_destroy(sqfs_object_t *obj) +{ + sqfs_dir_reader_t *rd = (sqfs_dir_reader_t *)obj; + + if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(&rd->dcache); + + sqfs_drop(rd->meta_inode); + sqfs_drop(rd->meta_dir); + free(rd); +} + +static sqfs_object_t *dir_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_dir_reader_t *rd = (const sqfs_dir_reader_t *)obj; + sqfs_dir_reader_t *copy = malloc(sizeof(*copy)); + + if (copy == NULL) + return NULL; + + memcpy(copy, rd, sizeof(*copy)); + + if (rd->flags & SQFS_DIR_READER_DOT_ENTRIES) { + if (rbtree_copy(&rd->dcache, ©->dcache)) + goto fail_cache; + } + + copy->meta_inode = sqfs_copy(rd->meta_inode); + if (copy->meta_inode == NULL) + goto fail_mino; + + copy->meta_dir = sqfs_copy(rd->meta_dir); + if (copy->meta_dir == NULL) + goto fail_mdir; + + return (sqfs_object_t *)copy; +fail_mdir: + sqfs_drop(copy->meta_inode); +fail_mino: + if (copy->flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(©->dcache); +fail_cache: + free(copy); + return NULL; +} + +sqfs_dir_reader_t *sqfs_dir_reader_create(const sqfs_super_t *super, + sqfs_compressor_t *cmp, + sqfs_file_t *file, + sqfs_u32 flags) +{ + sqfs_dir_reader_t *rd; + sqfs_u64 start, limit; + int ret; + + if (flags & ~SQFS_DIR_READER_ALL_FLAGS) + return NULL; + + rd = calloc(1, sizeof(*rd)); + if (rd == NULL) + return NULL; + + sqfs_object_init(rd, dir_reader_destroy, dir_reader_copy); + + if (flags & SQFS_DIR_READER_DOT_ENTRIES) { + ret = rbtree_init(&rd->dcache, sizeof(sqfs_u32), + sizeof(sqfs_u64), dcache_key_compare); + + if (ret != 0) + goto fail_dcache; + } + + start = super->inode_table_start; + limit = super->directory_table_start; + + rd->meta_inode = sqfs_meta_reader_create(file, cmp, start, limit); + if (rd->meta_inode == NULL) + goto fail_mino; + + start = super->directory_table_start; + limit = super->id_table_start; + + if (super->fragment_table_start < limit) + limit = super->fragment_table_start; + + if (super->export_table_start < limit) + limit = super->export_table_start; + + rd->meta_dir = sqfs_meta_reader_create(file, cmp, start, limit); + if (rd->meta_dir == NULL) + goto fail_mdir; + + rd->super = *super; + rd->flags = flags; + rd->state = DIR_STATE_NONE; + return rd; +fail_mdir: + sqfs_drop(rd->meta_inode); +fail_mino: + if (flags & SQFS_DIR_READER_DOT_ENTRIES) + rbtree_cleanup(&rd->dcache); +fail_dcache: + free(rd); + return NULL; +} + +int sqfs_dir_reader_open_dir(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *inode, + sqfs_u32 flags) +{ + sqfs_u32 parent; + int ret; + + if (flags & (~SQFS_DIR_OPEN_ALL_FLAGS)) + return SQFS_ERROR_UNSUPPORTED; + + ret = sqfs_readdir_state_init(&rd->it, &rd->super, inode); + if (ret) + return ret; + + if ((rd->flags & SQFS_DIR_READER_DOT_ENTRIES) && + !(flags & SQFS_DIR_OPEN_NO_DOT_ENTRIES)) { + if (inode->base.type == SQFS_INODE_EXT_DIR) { + parent = inode->data.dir_ext.parent_inode; + } else { + parent = inode->data.dir.parent_inode; + } + + if (dcache_find(rd, inode->base.inode_number, &rd->cur_ref)) + return SQFS_ERROR_NO_ENTRY; + + if (rd->cur_ref == rd->super.root_inode_ref) { + rd->parent_ref = rd->cur_ref; + } else if (dcache_find(rd, parent, &rd->parent_ref)) { + return SQFS_ERROR_NO_ENTRY; + } + + rd->state = DIR_STATE_OPENED; + } else { + rd->state = DIR_STATE_ENTRIES; + } + + rd->start_state = rd->state; + return 0; +} + +static int mk_dummy_entry(const char *str, sqfs_dir_entry_t **out) +{ + size_t len = strlen(str); + sqfs_dir_entry_t *ent; + + ent = calloc(1, sizeof(sqfs_dir_entry_t) + len + 1); + if (ent == NULL) + return SQFS_ERROR_ALLOC; + + ent->type = SQFS_INODE_DIR; + ent->size = len - 1; + + strcpy((char *)ent->name, str); + + *out = ent; + return 0; +} + +int sqfs_dir_reader_read(sqfs_dir_reader_t *rd, sqfs_dir_entry_t **out) +{ + int err; + + switch (rd->state) { + case DIR_STATE_OPENED: + err = mk_dummy_entry(".", out); + if (err == 0) { + rd->state = DIR_STATE_DOT; + rd->ent_ref = rd->cur_ref; + } + return err; + case DIR_STATE_DOT: + err = mk_dummy_entry("..", out); + if (err == 0) { + rd->state = DIR_STATE_ENTRIES; + rd->ent_ref = rd->parent_ref; + } + return err; + case DIR_STATE_ENTRIES: + break; + default: + return SQFS_ERROR_SEQUENCE; + } + + return sqfs_meta_reader_readdir(rd->meta_dir, &rd->it, + out, NULL, &rd->ent_ref); +} + +int sqfs_dir_reader_rewind(sqfs_dir_reader_t *rd) +{ + if (rd->state == DIR_STATE_NONE) + return SQFS_ERROR_SEQUENCE; + + sqfs_readdir_state_reset(&rd->it); + rd->state = rd->start_state; + return 0; +} + +int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name) +{ + sqfs_dir_entry_t *ent; + int ret; + + ret = sqfs_dir_reader_rewind(rd); + if (ret != 0) + return ret; + + do { + ret = sqfs_dir_reader_read(rd, &ent); + if (ret < 0) + return ret; + if (ret > 0) + return SQFS_ERROR_NO_ENTRY; + + ret = strcmp((const char *)ent->name, name); + free(ent); + } while (ret < 0); + + return ret == 0 ? 0 : SQFS_ERROR_NO_ENTRY; +} + +int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd, + sqfs_inode_generic_t **inode) +{ + int ret; + + ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, + rd->ent_ref >> 16, + rd->ent_ref & 0x0FFFF, inode); + if (ret != 0) + return ret; + + return dcache_add(rd, *inode, rd->ent_ref); +} + +int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd, + sqfs_inode_generic_t **inode) +{ + sqfs_u64 block_start = rd->super.root_inode_ref >> 16; + sqfs_u16 offset = rd->super.root_inode_ref & 0xFFFF; + int ret; + + ret = sqfs_meta_reader_read_inode(rd->meta_inode, &rd->super, + block_start, offset, inode); + if (ret != 0) + return ret; + + return dcache_add(rd, *inode, rd->super.root_inode_ref); +} + +int sqfs_dir_reader_find_by_path(sqfs_dir_reader_t *rd, + const sqfs_inode_generic_t *start, + const char *path, sqfs_inode_generic_t **out) +{ + sqfs_inode_generic_t *inode; + const char *ptr; + int ret = 0; + char *name; + + if (start == NULL) { + ret = sqfs_dir_reader_get_root_inode(rd, &inode); + } else { + ret = inode_copy(start, &inode); + } + + if (ret) + return ret; + + for (; *path != '\0'; path = ptr) { + if (*path == '/') { + for (ptr = path; *ptr == '/'; ++ptr) + ; + continue; + } + + ret = sqfs_dir_reader_open_dir(rd, inode, 0); + free(inode); + if (ret) + return ret; + + ptr = strchrnul(path, '/'); + + name = strndup(path, ptr - path); + if (name == NULL) + return SQFS_ERROR_ALLOC; + + ret = sqfs_dir_reader_find(rd, name); + free(name); + if (ret) + return ret; + + ret = sqfs_dir_reader_get_inode(rd, &inode); + if (ret) + return ret; + } + + *out = inode; + return 0; +} diff --git a/lib/sqfs/src/dir_reader/get_path.c b/lib/sqfs/src/dir_reader/get_path.c new file mode 100644 index 0000000..847bfd3 --- /dev/null +++ b/lib/sqfs/src/dir_reader/get_path.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * get_path.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +#include +#include + +int sqfs_tree_node_get_path(const sqfs_tree_node_t *node, char **out) +{ + const sqfs_tree_node_t *it; + size_t clen, len = 0; + char *str, *ptr; + + *out = NULL; + + if (node == NULL) + return SQFS_ERROR_ARG_INVALID; + + for (it = node; it->parent != NULL; it = it->parent) { + if (it->parent == node) + return SQFS_ERROR_LINK_LOOP; + + /* non-root nodes must have a valid name */ + clen = strlen((const char *)it->name); + + if (clen == 0) + return SQFS_ERROR_CORRUPTED; + + if (strchr((const char *)it->name, '/') != NULL) + return SQFS_ERROR_CORRUPTED; + + if (it->name[0] == '.') { + if (clen == 1 || (clen == 2 && it->name[1] == '.')) + return SQFS_ERROR_CORRUPTED; + } + + /* compute total path length */ + if (SZ_ADD_OV(clen, 1, &clen)) + return SQFS_ERROR_OVERFLOW; + + if (SZ_ADD_OV(len, clen, &len)) + return SQFS_ERROR_OVERFLOW; + } + + /* root node must not have a name */ + if (it->name[0] != '\0') + return SQFS_ERROR_ARG_INVALID; + + /* generate the path */ + if (node->parent == NULL) { + str = strdup("/"); + if (str == NULL) + return SQFS_ERROR_ALLOC; + } else { + if (SZ_ADD_OV(len, 1, &len)) + return SQFS_ERROR_OVERFLOW; + + str = malloc(len); + if (str == NULL) + return SQFS_ERROR_ALLOC; + + ptr = str + len - 1; + *ptr = '\0'; + + for (it = node; it->parent != NULL; it = it->parent) { + len = strlen((const char *)it->name); + ptr -= len; + + memcpy(ptr, (const char *)it->name, len); + *(--ptr) = '/'; + } + } + + *out = str; + return 0; +} diff --git a/lib/sqfs/src/dir_reader/internal.h b/lib/sqfs/src/dir_reader/internal.h new file mode 100644 index 0000000..471d197 --- /dev/null +++ b/lib/sqfs/src/dir_reader/internal.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * internal.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef DIR_READER_INTERNAL_H +#define DIR_READER_INTERNAL_H + +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/dir_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/id_table.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/error.h" +#include "sqfs/dir.h" +#include "util/rbtree.h" +#include "util/util.h" + +#include +#include + +enum { + DIR_STATE_NONE = 0, + DIR_STATE_OPENED = 1, + DIR_STATE_DOT = 2, + DIR_STATE_ENTRIES = 3, +}; + +struct sqfs_dir_reader_t { + sqfs_object_t base; + + sqfs_meta_reader_t *meta_dir; + sqfs_meta_reader_t *meta_inode; + sqfs_super_t super; + + sqfs_readdir_state_t it; + + sqfs_u32 flags; + + int start_state; + int state; + sqfs_u64 parent_ref; + sqfs_u64 cur_ref; + sqfs_u64 ent_ref; + rbtree_t dcache; +}; + +#endif /* DIR_READER_INTERNAL_H */ diff --git a/lib/sqfs/src/dir_reader/read_tree.c b/lib/sqfs/src/dir_reader/read_tree.c new file mode 100644 index 0000000..91cc2c0 --- /dev/null +++ b/lib/sqfs/src/dir_reader/read_tree.c @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_tree.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "internal.h" + +static int should_skip(int type, unsigned int flags) +{ + switch (type) { + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_BDEV: + return (flags & SQFS_TREE_NO_DEVICES); + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + return (flags & SQFS_TREE_NO_SLINKS); + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + return(flags & SQFS_TREE_NO_SOCKETS); + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + return (flags & SQFS_TREE_NO_FIFO); + default: + break; + } + + return 0; +} + +static bool would_be_own_parent(sqfs_tree_node_t *parent, sqfs_tree_node_t *n) +{ + sqfs_u32 inum = n->inode->base.inode_number; + + while (parent != NULL) { + if (parent->inode->base.inode_number == inum) + return true; + + parent = parent->parent; + } + + return false; +} + +static sqfs_tree_node_t *create_node(sqfs_inode_generic_t *inode, + const char *name) +{ + sqfs_tree_node_t *n; + + n = alloc_flex(sizeof(*n), 1, strlen(name) + 1); + if (n == NULL) + return NULL; + + n->inode = inode; + strcpy((char *)n->name, name); + return n; +} + +static int fill_dir(sqfs_dir_reader_t *dr, sqfs_tree_node_t *root, + unsigned int flags) +{ + sqfs_tree_node_t *n, *prev, **tail; + sqfs_inode_generic_t *inode; + sqfs_dir_entry_t *ent; + int err; + + tail = &root->children; + + for (;;) { + err = sqfs_dir_reader_read(dr, &ent); + if (err > 0) + break; + if (err < 0) + return err; + + if (should_skip(ent->type, flags)) { + free(ent); + continue; + } + + err = sqfs_dir_reader_get_inode(dr, &inode); + if (err) { + free(ent); + return err; + } + + n = create_node(inode, (const char *)ent->name); + free(ent); + + if (n == NULL) { + free(inode); + return SQFS_ERROR_ALLOC; + } + + if (would_be_own_parent(root, n)) { + free(n); + free(inode); + return SQFS_ERROR_LINK_LOOP; + } + + *tail = n; + tail = &n->next; + n->parent = root; + } + + n = root->children; + prev = NULL; + + while (n != NULL) { + if (n->inode->base.type == SQFS_INODE_DIR || + n->inode->base.type == SQFS_INODE_EXT_DIR) { + if (!(flags & SQFS_TREE_NO_RECURSE)) { + err = sqfs_dir_reader_open_dir(dr, n->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (err) + return err; + + err = fill_dir(dr, n, flags); + if (err) + return err; + } + + if (n->children == NULL && + (flags & SQFS_TREE_NO_EMPTY)) { + free(n->inode); + if (prev == NULL) { + root->children = root->children->next; + free(n); + n = root->children; + } else { + prev->next = n->next; + free(n); + n = prev->next; + } + continue; + } + } + + prev = n; + n = n->next; + } + + return 0; +} + +static int resolve_ids(sqfs_tree_node_t *root, const sqfs_id_table_t *idtbl) +{ + sqfs_tree_node_t *it; + int err; + + for (it = root->children; it != NULL; it = it->next) + resolve_ids(it, idtbl); + + err = sqfs_id_table_index_to_id(idtbl, root->inode->base.uid_idx, + &root->uid); + if (err) + return err; + + return sqfs_id_table_index_to_id(idtbl, root->inode->base.gid_idx, + &root->gid); +} + +void sqfs_dir_tree_destroy(sqfs_tree_node_t *root) +{ + sqfs_tree_node_t *it; + + if (!root) + return; + + while (root->children != NULL) { + it = root->children; + root->children = it->next; + + sqfs_dir_tree_destroy(it); + } + + free(root->inode); + free(root); +} + +int sqfs_dir_reader_get_full_hierarchy(sqfs_dir_reader_t *rd, + const sqfs_id_table_t *idtbl, + const char *path, unsigned int flags, + sqfs_tree_node_t **out) +{ + sqfs_tree_node_t *root, *tail, *new; + sqfs_inode_generic_t *inode; + sqfs_dir_entry_t *ent; + const char *ptr; + int ret; + + if (flags & ~SQFS_TREE_ALL_FLAGS) + return SQFS_ERROR_UNSUPPORTED; + + ret = sqfs_dir_reader_get_root_inode(rd, &inode); + if (ret) + return ret; + + root = tail = create_node(inode, ""); + if (root == NULL) { + free(inode); + return SQFS_ERROR_ALLOC; + } + inode = NULL; + + while (path != NULL && *path != '\0') { + if (*path == '/') { + while (*path == '/') + ++path; + continue; + } + + ret = sqfs_dir_reader_open_dir(rd, tail->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (ret) + goto fail; + + ptr = strchrnul(path, '/'); + + for (;;) { + ret = sqfs_dir_reader_read(rd, &ent); + if (ret < 0) + goto fail; + if (ret > 0) { + ret = SQFS_ERROR_NO_ENTRY; + goto fail; + } + + ret = strncmp((const char *)ent->name, + path, ptr - path); + if (ret == 0 && ent->name[ptr - path] == '\0') + break; + free(ent); + } + + ret = sqfs_dir_reader_get_inode(rd, &inode); + if (ret) { + free(ent); + goto fail; + } + + new = create_node(inode, (const char *)ent->name); + free(ent); + + if (new == NULL) { + free(inode); + ret = SQFS_ERROR_ALLOC; + goto fail; + } + + inode = NULL; + path = ptr; + + if (flags & SQFS_TREE_STORE_PARENTS) { + tail->children = new; + new->parent = tail; + tail = new; + } else { + sqfs_dir_tree_destroy(root); + root = tail = new; + } + } + + if (tail->inode->base.type == SQFS_INODE_DIR || + tail->inode->base.type == SQFS_INODE_EXT_DIR) { + ret = sqfs_dir_reader_open_dir(rd, tail->inode, + SQFS_DIR_OPEN_NO_DOT_ENTRIES); + if (ret) + goto fail; + + ret = fill_dir(rd, tail, flags); + if (ret) + goto fail; + } + + ret = resolve_ids(root, idtbl); + if (ret) + goto fail; + + *out = root; + return 0; +fail: + sqfs_dir_tree_destroy(root); + return ret; +} diff --git a/lib/sqfs/src/dir_writer.c b/lib/sqfs/src/dir_writer.c new file mode 100644 index 0000000..d2b72df --- /dev/null +++ b/lib/sqfs/src/dir_writer.c @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * dir_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#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" +#include "sqfs/dir.h" +#include "util/array.h" +#include "util/util.h" + +#include +#include + +#define DIR_INDEX_THRESHOLD (256) + +typedef struct dir_entry_t { + struct dir_entry_t *next; + sqfs_u64 inode_ref; + sqfs_u32 inode_num; + sqfs_u16 type; + size_t name_len; + char name[]; +} dir_entry_t; + +typedef struct index_ent_t { + struct index_ent_t *next; + dir_entry_t *ent; + sqfs_u64 block; + sqfs_u32 index; +} index_ent_t; + +struct sqfs_dir_writer_t { + sqfs_object_t base; + + dir_entry_t *list; + dir_entry_t *list_end; + + index_ent_t *idx; + index_ent_t *idx_end; + + sqfs_u64 dir_ref; + size_t dir_size; + size_t ent_count; + sqfs_meta_writer_t *dm; + + array_t export_tbl; +}; + +static int get_type(sqfs_u16 mode) +{ + switch (mode & S_IFMT) { + case S_IFSOCK: return SQFS_INODE_SOCKET; + case S_IFIFO: return SQFS_INODE_FIFO; + case S_IFLNK: return SQFS_INODE_SLINK; + case S_IFBLK: return SQFS_INODE_BDEV; + case S_IFCHR: return SQFS_INODE_CDEV; + case S_IFDIR: return SQFS_INODE_DIR; + case S_IFREG: return SQFS_INODE_FILE; + default: + break; + } + + return SQFS_ERROR_UNSUPPORTED; +} + +static void writer_reset(sqfs_dir_writer_t *writer) +{ + dir_entry_t *ent; + index_ent_t *idx; + + while (writer->idx != NULL) { + idx = writer->idx; + writer->idx = idx->next; + free(idx); + } + + while (writer->list != NULL) { + ent = writer->list; + writer->list = ent->next; + free(ent); + } + + writer->list_end = NULL; + writer->idx_end = NULL; + writer->dir_ref = 0; + writer->dir_size = 0; + writer->ent_count = 0; +} + +static int add_export_table_entry(sqfs_dir_writer_t *writer, + sqfs_u32 inum, sqfs_u64 iref) +{ + sqfs_u64 *ptr; + int ret; + + if (writer->export_tbl.data == NULL) + return 0; + + if (inum < 1) + return SQFS_ERROR_ARG_INVALID; + + ret = array_set_capacity(&writer->export_tbl, inum); + if (ret != 0) + return ret; + + ptr = (sqfs_u64 *)writer->export_tbl.data; + + if ((inum - 1) >= writer->export_tbl.used) { + memset(ptr + writer->export_tbl.used, 0xFF, + (inum - writer->export_tbl.used) * sizeof(*ptr)); + + writer->export_tbl.used = inum; + } + + ptr[inum - 1] = iref; + return 0; +} + +static void dir_writer_destroy(sqfs_object_t *obj) +{ + sqfs_dir_writer_t *writer = (sqfs_dir_writer_t *)obj; + + sqfs_drop(writer->dm); + writer_reset(writer); + array_cleanup(&writer->export_tbl); + free(writer); +} + +sqfs_dir_writer_t *sqfs_dir_writer_create(sqfs_meta_writer_t *dm, + sqfs_u32 flags) +{ + 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; + + sqfs_object_init(writer, dir_writer_destroy, NULL); + + if (flags & SQFS_DIR_WRITER_CREATE_EXPORT_TABLE) { + if (array_init(&writer->export_tbl, sizeof(sqfs_u64), 512)) { + free(writer); + return NULL; + } + + memset(writer->export_tbl.data, 0xFF, + writer->export_tbl.size * writer->export_tbl.count); + } + + writer->dm = sqfs_grab(dm); + return writer; +} + +int sqfs_dir_writer_begin(sqfs_dir_writer_t *writer, sqfs_u32 flags) +{ + sqfs_u32 offset; + sqfs_u64 block; + + if (flags != 0) + return SQFS_ERROR_UNSUPPORTED; + + writer_reset(writer); + + sqfs_meta_writer_get_position(writer->dm, &block, &offset); + writer->dir_ref = (block << 16) | offset; + return 0; +} + +int sqfs_dir_writer_add_entry(sqfs_dir_writer_t *writer, const char *name, + sqfs_u32 inode_num, sqfs_u64 inode_ref, + sqfs_u16 mode) +{ + dir_entry_t *ent; + int type, err; + + type = get_type(mode); + if (type < 0) + return type; + + 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; + + ent->inode_ref = inode_ref; + ent->inode_num = inode_num; + ent->type = type; + ent->name_len = strlen(name); + memcpy(ent->name, name, ent->name_len); + + if (writer->list_end == NULL) { + writer->list = writer->list_end = ent; + } else { + writer->list_end->next = ent; + writer->list_end = ent; + } + + writer->ent_count += 1; + return 0; +} + +static size_t get_conseq_entry_count(sqfs_u32 offset, dir_entry_t *head) +{ + size_t size, count = 0; + dir_entry_t *it; + sqfs_s32 diff; + + size = (offset + sizeof(sqfs_dir_header_t)) % SQFS_META_BLOCK_SIZE; + + for (it = head; it != NULL; it = it->next) { + if ((it->inode_ref >> 16) != (head->inode_ref >> 16)) + break; + + diff = it->inode_num - head->inode_num; + + if (diff > 32767 || diff < -32767) + break; + + size += sizeof(sqfs_dir_entry_t) + it->name_len; + + if (count > 0 && size > SQFS_META_BLOCK_SIZE) + break; + + count += 1; + + if (count == SQFS_MAX_DIR_ENT) + break; + } + + return count; +} + +static int add_header(sqfs_dir_writer_t *writer, size_t count, + dir_entry_t *ref, sqfs_u64 block) +{ + sqfs_dir_header_t hdr; + index_ent_t *idx; + int err; + + hdr.count = htole32(count - 1); + hdr.start_block = htole32(ref->inode_ref >> 16); + hdr.inode_number = htole32(ref->inode_num); + + err = sqfs_meta_writer_append(writer->dm, &hdr, sizeof(hdr)); + if (err) + return err; + + idx = calloc(1, sizeof(*idx)); + if (idx == NULL) + return SQFS_ERROR_ALLOC; + + idx->ent = ref; + idx->block = block; + idx->index = writer->dir_size; + + if (writer->idx_end == NULL) { + writer->idx = writer->idx_end = idx; + } else { + writer->idx_end->next = idx; + writer->idx_end = idx; + } + + writer->dir_size += sizeof(hdr); + return 0; +} + +int sqfs_dir_writer_end(sqfs_dir_writer_t *writer) +{ + dir_entry_t *it, *first; + sqfs_dir_entry_t ent; + sqfs_u16 *diff_u16; + size_t i, count; + sqfs_u32 offset; + sqfs_u64 block; + int err; + + for (it = writer->list; it != NULL; ) { + sqfs_meta_writer_get_position(writer->dm, &block, &offset); + count = get_conseq_entry_count(offset, it); + + err = add_header(writer, count, it, block); + if (err) + return err; + + first = it; + + for (i = 0; i < count; ++i) { + ent.offset = htole16(it->inode_ref & 0x0000FFFF); + ent.inode_diff = it->inode_num - first->inode_num; + ent.type = htole16(it->type); + ent.size = htole16(it->name_len - 1); + + diff_u16 = (sqfs_u16 *)&ent.inode_diff; + *diff_u16 = htole16(*diff_u16); + + err = sqfs_meta_writer_append(writer->dm, &ent, + sizeof(ent)); + if (err) + return err; + + err = sqfs_meta_writer_append(writer->dm, it->name, + it->name_len); + if (err) + return err; + + writer->dir_size += sizeof(ent) + it->name_len; + it = it->next; + } + } + + return 0; +} + +size_t sqfs_dir_writer_get_size(const sqfs_dir_writer_t *writer) +{ + return writer->dir_size; +} + +sqfs_u64 sqfs_dir_writer_get_dir_reference(const sqfs_dir_writer_t *writer) +{ + return writer->dir_ref; +} + +size_t sqfs_dir_writer_get_index_size(const sqfs_dir_writer_t *writer) +{ + size_t index_size = 0; + index_ent_t *idx; + + for (idx = writer->idx; idx != NULL; idx = idx->next) + index_size += sizeof(sqfs_dir_index_t) + idx->ent->name_len; + + return index_size; +} + +size_t sqfs_dir_writer_get_entry_count(const sqfs_dir_writer_t *writer) +{ + return writer->ent_count; +} + +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) +{ + sqfs_inode_generic_t *inode; + sqfs_dir_index_t ent; + sqfs_u64 start_block; + sqfs_u16 block_offset; + size_t index_size; + index_ent_t *idx; + sqfs_u8 *ptr; + + index_size = 0; + + for (idx = writer->idx; idx != NULL; idx = idx->next) + index_size += sizeof(ent) + idx->ent->name_len; + + inode = alloc_flex(sizeof(*inode), 1, index_size); + if (inode == NULL) + return NULL; + + inode->payload_bytes_available = index_size; + start_block = writer->dir_ref >> 16; + block_offset = writer->dir_ref & 0xFFFF; + + if (xattr != 0xFFFFFFFF || start_block > 0xFFFFFFFFUL || + writer->dir_size > (0xFFFF - 3)) { + inode->base.type = SQFS_INODE_EXT_DIR; + } else { + inode->base.type = SQFS_INODE_DIR; + } + + if (writer->ent_count >= DIR_INDEX_THRESHOLD) + inode->base.type = SQFS_INODE_EXT_DIR; + + if (inode->base.type == SQFS_INODE_DIR) { + inode->data.dir.start_block = start_block; + inode->data.dir.nlink = writer->ent_count + hlinks + 2; + inode->data.dir.size = writer->dir_size + 3; + inode->data.dir.offset = block_offset; + inode->data.dir.parent_inode = parent_ino; + } else { + inode->data.dir_ext.nlink = writer->ent_count + hlinks + 2; + inode->data.dir_ext.size = writer->dir_size + 3; + inode->data.dir_ext.start_block = start_block; + inode->data.dir_ext.parent_inode = parent_ino; + inode->data.dir_ext.offset = block_offset; + inode->data.dir_ext.xattr_idx = xattr; + inode->data.dir_ext.inodex_count = 0; + inode->payload_bytes_used = 0; + + for (idx = writer->idx; idx != NULL; idx = idx->next) { + memset(&ent, 0, sizeof(ent)); + ent.start_block = idx->block; + ent.index = idx->index; + ent.size = idx->ent->name_len - 1; + + ptr = (sqfs_u8 *)inode->extra + + inode->payload_bytes_used; + memcpy(ptr, &ent, sizeof(ent)); + memcpy(ptr + sizeof(ent), idx->ent->name, + idx->ent->name_len); + + inode->data.dir_ext.inodex_count += 1; + inode->payload_bytes_used += sizeof(ent); + inode->payload_bytes_used += idx->ent->name_len; + } + } + + 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.data == NULL) + return 0; + + size = writer->export_tbl.size * writer->export_tbl.used; + + ret = sqfs_write_table(file, cmp, writer->export_tbl.data, + size, &start); + if (ret) + return ret; + + super->export_table_start = start; + super->flags |= SQFS_FLAG_EXPORTABLE; + return 0; +} diff --git a/lib/sqfs/src/frag_table.c b/lib/sqfs/src/frag_table.c new file mode 100644 index 0000000..151df28 --- /dev/null +++ b/lib/sqfs/src/frag_table.c @@ -0,0 +1,204 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * frag_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#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 "util/array.h" + +#include +#include + +struct sqfs_frag_table_t { + sqfs_object_t base; + + array_t table; +}; + +static void frag_table_destroy(sqfs_object_t *obj) +{ + sqfs_frag_table_t *tbl = (sqfs_frag_table_t *)obj; + + array_cleanup(&tbl->table); + free(tbl); +} + +static sqfs_object_t *frag_table_copy(const sqfs_object_t *obj) +{ + const sqfs_frag_table_t *tbl = (const sqfs_frag_table_t *)obj; + sqfs_frag_table_t *copy = calloc(1, sizeof(*copy)); + + if (copy == NULL) + return NULL; + + if (array_init_copy(©->table, &tbl->table)) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +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; + + sqfs_object_init(tbl, frag_table_destroy, frag_table_copy); + + array_init(&tbl->table, sizeof(sqfs_fragment_t), 0); + return 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; + + array_cleanup(&tbl->table); + tbl->table.size = sizeof(sqfs_fragment_t); + + 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.data = raw; + tbl->table.count = super->fragment_entry_count; + tbl->table.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->table.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.data, + tbl->table.size * tbl->table.used, + &super->fragment_table_start); + if (err) + return err; + + super->fragment_entry_count = tbl->table.used; + super->flags &= ~SQFS_FLAG_NO_FRAGMENTS; + super->flags |= SQFS_FLAG_ALWAYS_FRAGMENTS; + super->flags |= SQFS_FLAG_UNCOMPRESSED_FRAGMENTS; + + for (i = 0; i < tbl->table.used; ++i) { + sqfs_u32 sz = ((sqfs_fragment_t *)tbl->table.data)[i].size; + + if (SQFS_IS_BLOCK_COMPRESSED(le32toh(sz))) { + 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) +{ + sqfs_fragment_t *frag = array_get(&tbl->table, index); + + if (frag == NULL) + return SQFS_ERROR_OUT_OF_BOUNDS; + + out->start_offset = le64toh(frag->start_offset); + out->size = le32toh(frag->size); + out->pad0 = le32toh(frag->pad0); + return 0; +} + +int sqfs_frag_table_append(sqfs_frag_table_t *tbl, sqfs_u64 location, + sqfs_u32 size, sqfs_u32 *index) +{ + sqfs_fragment_t frag; + + if (index != NULL) + *index = tbl->table.used; + + memset(&frag, 0, sizeof(frag)); + frag.start_offset = htole64(location); + frag.size = htole32(size); + + return array_append(&tbl->table, &frag); +} + +int sqfs_frag_table_set(sqfs_frag_table_t *tbl, sqfs_u32 index, + sqfs_u64 location, sqfs_u32 size) +{ + sqfs_fragment_t frag; + + memset(&frag, 0, sizeof(frag)); + frag.start_offset = htole64(location); + frag.size = htole32(size); + + return array_set(&tbl->table, index, &frag); +} + +size_t sqfs_frag_table_get_size(sqfs_frag_table_t *tbl) +{ + return tbl->table.used; +} diff --git a/lib/sqfs/src/id_table.c b/lib/sqfs/src/id_table.c new file mode 100644 index 0000000..ec3fdfe --- /dev/null +++ b/lib/sqfs/src/id_table.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * id_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/id_table.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/error.h" +#include "compat.h" +#include "util/array.h" + +#include +#include + +struct sqfs_id_table_t { + sqfs_object_t base; + + array_t ids; +}; + +static void id_table_destroy(sqfs_object_t *obj) +{ + sqfs_id_table_t *tbl = (sqfs_id_table_t *)obj; + + array_cleanup(&tbl->ids); + free(tbl); +} + +static sqfs_object_t *id_table_copy(const sqfs_object_t *obj) +{ + const sqfs_id_table_t *tbl = (const sqfs_id_table_t *)obj; + sqfs_id_table_t *copy = calloc(1, sizeof(*copy)); + + if (copy == NULL) + return NULL; + + if (array_init_copy(©->ids, &tbl->ids) != 0) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +sqfs_id_table_t *sqfs_id_table_create(sqfs_u32 flags) +{ + sqfs_id_table_t *tbl; + + if (flags != 0) + return NULL; + + tbl = calloc(1, sizeof(sqfs_id_table_t)); + + if (tbl != NULL) { + array_init(&tbl->ids, sizeof(sqfs_u32), 0); + sqfs_object_init(tbl, id_table_destroy, id_table_copy); + } + + return tbl; +} + +int sqfs_id_table_id_to_index(sqfs_id_table_t *tbl, sqfs_u32 id, sqfs_u16 *out) +{ + size_t i; + + for (i = 0; i < tbl->ids.used; ++i) { + if (((sqfs_u32 *)tbl->ids.data)[i] == id) { + *out = i; + return 0; + } + } + + if (tbl->ids.used == 0x10000) + return SQFS_ERROR_OVERFLOW; + + *out = tbl->ids.used; + return array_append(&tbl->ids, &id); +} + +int sqfs_id_table_index_to_id(const sqfs_id_table_t *tbl, sqfs_u16 index, + sqfs_u32 *out) +{ + if (index >= tbl->ids.used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + *out = ((sqfs_u32 *)tbl->ids.data)[index]; + return 0; +} + +int sqfs_id_table_read(sqfs_id_table_t *tbl, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 upper_limit, lower_limit; + void *raw_ids; + size_t i; + int ret; + + if (!super->id_count || super->id_table_start >= super->bytes_used) + return SQFS_ERROR_CORRUPTED; + + upper_limit = super->id_table_start; + lower_limit = super->directory_table_start; + + if (super->fragment_table_start > lower_limit && + super->fragment_table_start < upper_limit) { + lower_limit = super->fragment_table_start; + } + + if (super->export_table_start > lower_limit && + super->export_table_start < upper_limit) { + lower_limit = super->export_table_start; + } + + array_cleanup(&tbl->ids); + tbl->ids.size = sizeof(sqfs_u32); + + ret = sqfs_read_table(file, cmp, super->id_count * sizeof(sqfs_u32), + super->id_table_start, lower_limit, + upper_limit, &raw_ids); + if (ret) + return ret; + + for (i = 0; i < super->id_count; ++i) + ((sqfs_u32 *)raw_ids)[i] = le32toh(((sqfs_u32 *)raw_ids)[i]); + + tbl->ids.data = raw_ids; + tbl->ids.used = super->id_count; + tbl->ids.count = super->id_count; + return 0; +} + +int sqfs_id_table_write(sqfs_id_table_t *tbl, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 start; + size_t i; + int ret; + + for (i = 0; i < tbl->ids.used; ++i) { + ((sqfs_u32 *)tbl->ids.data)[i] = + htole32(((sqfs_u32 *)tbl->ids.data)[i]); + } + + super->id_count = tbl->ids.used; + + ret = sqfs_write_table(file, cmp, tbl->ids.data, + sizeof(sqfs_u32) * tbl->ids.used, &start); + + super->id_table_start = start; + + for (i = 0; i < tbl->ids.used; ++i) { + ((sqfs_u32 *)tbl->ids.data)[i] = + le32toh(((sqfs_u32 *)tbl->ids.data)[i]); + } + + return ret; +} diff --git a/lib/sqfs/src/inode.c b/lib/sqfs/src/inode.c new file mode 100644 index 0000000..ce51cf5 --- /dev/null +++ b/lib/sqfs/src/inode.c @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/inode.h" +#include "sqfs/error.h" +#include "sqfs/dir.h" + +#include +#include + +#include "util/util.h" + +static int inverse_type[] = { + [SQFS_INODE_DIR] = SQFS_INODE_EXT_DIR, + [SQFS_INODE_FILE] = SQFS_INODE_EXT_FILE, + [SQFS_INODE_SLINK] = SQFS_INODE_EXT_SLINK, + [SQFS_INODE_BDEV] = SQFS_INODE_EXT_BDEV, + [SQFS_INODE_CDEV] = SQFS_INODE_EXT_CDEV, + [SQFS_INODE_FIFO] = SQFS_INODE_EXT_FIFO, + [SQFS_INODE_SOCKET] = SQFS_INODE_EXT_SOCKET, + [SQFS_INODE_EXT_DIR] = SQFS_INODE_DIR, + [SQFS_INODE_EXT_FILE] = SQFS_INODE_FILE, + [SQFS_INODE_EXT_SLINK] = SQFS_INODE_SLINK, + [SQFS_INODE_EXT_BDEV] = SQFS_INODE_BDEV, + [SQFS_INODE_EXT_CDEV] = SQFS_INODE_CDEV, + [SQFS_INODE_EXT_FIFO] = SQFS_INODE_FIFO, + [SQFS_INODE_EXT_SOCKET] = SQFS_INODE_SOCKET, +}; + +int sqfs_inode_get_xattr_index(const sqfs_inode_generic_t *inode, + sqfs_u32 *out) +{ + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + *out = 0xFFFFFFFF; + break; + case SQFS_INODE_EXT_DIR: + *out = inode->data.dir_ext.xattr_idx; + break; + case SQFS_INODE_EXT_FILE: + *out = inode->data.file_ext.xattr_idx; + break; + case SQFS_INODE_EXT_SLINK: + *out = inode->data.slink_ext.xattr_idx; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + *out = inode->data.dev_ext.xattr_idx; + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + *out = inode->data.ipc_ext.xattr_idx; + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + return 0; +} + +int sqfs_inode_set_xattr_index(sqfs_inode_generic_t *inode, sqfs_u32 index) +{ + int err; + + if (index != 0xFFFFFFFF) { + err = sqfs_inode_make_extended(inode); + if (err) + return err; + } + + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + break; + case SQFS_INODE_EXT_DIR: + inode->data.dir_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_FILE: + inode->data.file_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_SLINK: + inode->data.slink_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + inode->data.dev_ext.xattr_idx = index; + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + inode->data.ipc_ext.xattr_idx = index; + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + return 0; +} + +int sqfs_inode_make_extended(sqfs_inode_generic_t *inode) +{ + switch (inode->base.type) { + case SQFS_INODE_DIR: { + sqfs_inode_dir_ext_t temp = { + .nlink = inode->data.dir.nlink, + .size = inode->data.dir.size, + .start_block = inode->data.dir.start_block, + .parent_inode = inode->data.dir.parent_inode, + .inodex_count = 0, + .offset = inode->data.dir.offset, + .xattr_idx = 0xFFFFFFFF, + }; + inode->data.dir_ext = temp; + break; + } + case SQFS_INODE_FILE: { + sqfs_inode_file_ext_t temp = { + .blocks_start = inode->data.file.blocks_start, + .file_size = inode->data.file.file_size, + .sparse = 0, + .nlink = 1, + .fragment_idx = inode->data.file.fragment_index, + .fragment_offset = inode->data.file.fragment_offset, + .xattr_idx = 0xFFFFFFFF, + }; + inode->data.file_ext = temp; + break; + } + case SQFS_INODE_SLINK: + inode->data.slink_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + inode->data.dev_ext.xattr_idx = 0xFFFFFFFF; + break; + case SQFS_INODE_EXT_DIR: + case SQFS_INODE_EXT_FILE: + case SQFS_INODE_EXT_SLINK: + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + return 0; + default: + return SQFS_ERROR_CORRUPTED; + } + + inode->base.type = inverse_type[inode->base.type]; + return 0; +} + +int sqfs_inode_make_basic(sqfs_inode_generic_t *inode) +{ + sqfs_u32 xattr; + int err; + + err = sqfs_inode_get_xattr_index(inode, &xattr); + if (err != 0 || xattr != 0xFFFFFFFF) + return err; + + switch (inode->base.type) { + case SQFS_INODE_DIR: + case SQFS_INODE_FILE: + case SQFS_INODE_SLINK: + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + return 0; + case SQFS_INODE_EXT_DIR: { + sqfs_inode_dir_t temp = { + .start_block = inode->data.dir_ext.start_block, + .nlink = inode->data.dir_ext.nlink, + .size = inode->data.dir_ext.size, + .offset = inode->data.dir_ext.offset, + .parent_inode = inode->data.dir_ext.parent_inode, + }; + + if (inode->data.dir_ext.size > 0x0FFFF) + return 0; + + inode->data.dir = temp; + break; + } + case SQFS_INODE_EXT_FILE: { + sqfs_inode_file_t temp = { + .blocks_start = inode->data.file_ext.blocks_start, + .fragment_index = inode->data.file_ext.fragment_idx, + .fragment_offset = inode->data.file_ext.fragment_offset, + .file_size = inode->data.file_ext.file_size, + }; + + if (inode->data.file_ext.blocks_start > 0x0FFFFFFFFUL) + return 0; + if (inode->data.file_ext.file_size > 0x0FFFFFFFFUL) + return 0; + if (inode->data.file_ext.sparse > 0) + return 0; + if (inode->data.file_ext.nlink > 1) + return 0; + + inode->data.file = temp; + break; + } + case SQFS_INODE_EXT_SLINK: + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + break; + default: + return SQFS_ERROR_CORRUPTED; + } + + inode->base.type = inverse_type[inode->base.type]; + return 0; +} + +int sqfs_inode_set_file_size(sqfs_inode_generic_t *inode, sqfs_u64 size) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.file_size = size; + + if (size < 0x0FFFFFFFFUL) + sqfs_inode_make_basic(inode); + } else if (inode->base.type == SQFS_INODE_FILE) { + if (size > 0x0FFFFFFFFUL) { + sqfs_inode_make_extended(inode); + inode->data.file_ext.file_size = size; + } else { + inode->data.file.file_size = size; + } + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_set_frag_location(sqfs_inode_generic_t *inode, + sqfs_u32 index, sqfs_u32 offset) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.fragment_idx = index; + inode->data.file_ext.fragment_offset = offset; + } else if (inode->base.type == SQFS_INODE_FILE) { + inode->data.file.fragment_index = index; + inode->data.file.fragment_offset = offset; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_set_file_block_start(sqfs_inode_generic_t *inode, + sqfs_u64 location) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + inode->data.file_ext.blocks_start = location; + + if (location < 0x0FFFFFFFFUL) + sqfs_inode_make_basic(inode); + } else if (inode->base.type == SQFS_INODE_FILE) { + if (location > 0x0FFFFFFFFUL) { + sqfs_inode_make_extended(inode); + inode->data.file_ext.blocks_start = location; + } else { + inode->data.file.blocks_start = location; + } + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_file_size(const sqfs_inode_generic_t *inode, sqfs_u64 *size) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *size = inode->data.file_ext.file_size; + } else if (inode->base.type == SQFS_INODE_FILE) { + *size = inode->data.file.file_size; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_frag_location(const sqfs_inode_generic_t *inode, + sqfs_u32 *index, sqfs_u32 *offset) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *index = inode->data.file_ext.fragment_idx; + *offset = inode->data.file_ext.fragment_offset; + } else if (inode->base.type == SQFS_INODE_FILE) { + *index = inode->data.file.fragment_index; + *offset = inode->data.file.fragment_offset; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode, + sqfs_u64 *location) +{ + if (inode->base.type == SQFS_INODE_EXT_FILE) { + *location = inode->data.file_ext.blocks_start; + } else if (inode->base.type == SQFS_INODE_FILE) { + *location = inode->data.file.blocks_start; + } else { + return SQFS_ERROR_NOT_FILE; + } + + return 0; +} + +int sqfs_inode_unpack_dir_index_entry(const sqfs_inode_generic_t *inode, + sqfs_dir_index_t **out, + size_t index) +{ + sqfs_dir_index_t ent; + const char *ptr; + size_t offset; + + if (inode->base.type != SQFS_INODE_EXT_DIR) { + if (inode->base.type == SQFS_INODE_DIR) + return SQFS_ERROR_OUT_OF_BOUNDS; + + return SQFS_ERROR_NOT_DIR; + } + + offset = 0; + ptr = (const char *)inode->extra; + + for (;;) { + if (offset >= inode->payload_bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (index == 0) + break; + + memcpy(&ent, ptr + offset, sizeof(ent)); + offset += sizeof(ent) + ent.size + 1; + index -= 1; + } + + memcpy(&ent, ptr + offset, sizeof(ent)); + + *out = alloc_flex(sizeof(ent), 1, ent.size + 2); + if (*out == NULL) + return SQFS_ERROR_ALLOC; + + memcpy(*out, &ent, sizeof(ent)); + memcpy((*out)->name, ptr + offset + sizeof(ent), ent.size + 1); + return 0; +} diff --git a/lib/sqfs/src/meta_reader.c b/lib/sqfs/src/meta_reader.c new file mode 100644 index 0000000..e431d40 --- /dev/null +++ b/lib/sqfs/src/meta_reader.c @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * meta_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +struct sqfs_meta_reader_t { + sqfs_object_t base; + + sqfs_u64 start; + sqfs_u64 limit; + size_t data_used; + + /* The location of the current block in the image */ + sqfs_u64 block_offset; + + /* The location of the next block after the current one */ + sqfs_u64 next_block; + + /* A byte offset into the uncompressed data of the current block */ + size_t offset; + + /* The underlying file descriptor to read from */ + sqfs_file_t *file; + + /* A pointer to the compressor to use for extracting data */ + sqfs_compressor_t *cmp; + + /* The raw data read from the input file */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE]; + + /* The uncompressed data read from the input file */ + sqfs_u8 scratch[SQFS_META_BLOCK_SIZE]; +}; + +static void meta_reader_destroy(sqfs_object_t *m) +{ + sqfs_meta_reader_t *mr = (sqfs_meta_reader_t *)m; + + sqfs_drop(mr->file); + sqfs_drop(mr->cmp); + free(m); +} + +static sqfs_object_t *meta_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_meta_reader_t *m = (const sqfs_meta_reader_t *)obj; + sqfs_meta_reader_t *copy = malloc(sizeof(*copy)); + + if (copy != NULL) { + memcpy(copy, m, sizeof(*m)); + + /* duplicate references */ + copy->cmp = sqfs_grab(copy->cmp); + copy->file = sqfs_grab(copy->file); + } + + return (sqfs_object_t *)copy; +} + +sqfs_meta_reader_t *sqfs_meta_reader_create(sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u64 start, sqfs_u64 limit) +{ + sqfs_meta_reader_t *m = calloc(1, sizeof(*m)); + + if (m == NULL) + return NULL; + + sqfs_object_init(m, meta_reader_destroy, meta_reader_copy); + + m->block_offset = 0xFFFFFFFFFFFFFFFFUL; + m->start = start; + m->limit = limit; + m->file = sqfs_grab(file); + m->cmp = sqfs_grab(cmp); + return m; +} + +int sqfs_meta_reader_seek(sqfs_meta_reader_t *m, sqfs_u64 block_start, + size_t offset) +{ + bool compressed; + sqfs_u16 header; + sqfs_u32 size; + sqfs_s32 ret; + int err; + + if (block_start < m->start || block_start >= m->limit) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (block_start == m->block_offset) { + if (offset >= m->data_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + m->offset = offset; + return 0; + } + + err = m->file->read_at(m->file, block_start, &header, 2); + if (err) + return err; + + header = le16toh(header); + compressed = (header & 0x8000) == 0; + size = header & 0x7FFF; + + if (size > sizeof(m->data)) + return SQFS_ERROR_CORRUPTED; + + if ((block_start + 2 + size) > m->limit) + return SQFS_ERROR_OUT_OF_BOUNDS; + + err = m->file->read_at(m->file, block_start + 2, m->data, size); + if (err) + return err; + + if (compressed) { + ret = m->cmp->do_block(m->cmp, m->data, size, + m->scratch, sizeof(m->scratch)); + + if (ret < 0) + return ret; + + memcpy(m->data, m->scratch, ret); + m->data_used = ret; + } else { + m->data_used = size; + } + + if (offset >= m->data_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + m->block_offset = block_start; + m->next_block = block_start + size + 2; + m->offset = offset; + return 0; +} + +void sqfs_meta_reader_get_position(const sqfs_meta_reader_t *m, + sqfs_u64 *block_start, size_t *offset) +{ + if (m->offset == m->data_used) { + *block_start = m->next_block; + *offset = 0; + } else { + *block_start = m->block_offset; + *offset = m->offset; + } +} + +int sqfs_meta_reader_read(sqfs_meta_reader_t *m, void *data, size_t size) +{ + size_t diff; + int ret; + + while (size != 0) { + diff = m->data_used - m->offset; + + if (diff == 0) { + ret = sqfs_meta_reader_seek(m, m->next_block, 0); + if (ret) + return ret; + diff = m->data_used; + } + + if (diff > size) + diff = size; + + memcpy(data, m->data + m->offset, diff); + + m->offset += diff; + data = (char *)data + diff; + size -= diff; + } + + return 0; +} diff --git a/lib/sqfs/src/meta_writer.c b/lib/sqfs/src/meta_writer.c new file mode 100644 index 0000000..bf3f426 --- /dev/null +++ b/lib/sqfs/src/meta_writer.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * meta_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/compressor.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include + +typedef struct meta_block_t { + struct meta_block_t *next; + + /* possibly compressed data with 2 byte header */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE + 2]; +} meta_block_t; + +struct sqfs_meta_writer_t { + sqfs_object_t base; + + /* A byte offset into the uncompressed data of the current block */ + size_t offset; + + /* The location of the current block in the file */ + size_t block_offset; + + /* The underlying file descriptor to write to */ + sqfs_file_t *file; + + /* A pointer to the compressor to use for compressing the data */ + sqfs_compressor_t *cmp; + + /* The raw data chunk that data is appended to */ + sqfs_u8 data[SQFS_META_BLOCK_SIZE]; + + sqfs_u32 flags; + meta_block_t *list; + meta_block_t *list_end; +}; + +static int write_block(sqfs_file_t *file, meta_block_t *outblk) +{ + sqfs_u16 header; + size_t count; + sqfs_u64 off; + + memcpy(&header, outblk->data, sizeof(header)); + count = le16toh(header) & 0x7FFF; + off = file->get_size(file); + + return file->write_at(file, off, outblk->data, count + 2); +} + +static void meta_writer_destroy(sqfs_object_t *obj) +{ + sqfs_meta_writer_t *m = (sqfs_meta_writer_t *)obj; + meta_block_t *blk; + + while (m->list != NULL) { + blk = m->list; + m->list = blk->next; + free(blk); + } + + sqfs_drop(m->file); + sqfs_drop(m->cmp); + free(m); +} + +sqfs_meta_writer_t *sqfs_meta_writer_create(sqfs_file_t *file, + sqfs_compressor_t *cmp, + sqfs_u32 flags) +{ + sqfs_meta_writer_t *m; + + if (flags & ~SQFS_META_WRITER_ALL_FLAGS) + return NULL; + + m = calloc(1, sizeof(*m)); + if (m == NULL) + return NULL; + + sqfs_object_init(m, meta_writer_destroy, NULL); + + m->cmp = sqfs_grab(cmp); + m->file = sqfs_grab(file); + m->flags = flags; + return m; +} + +int sqfs_meta_writer_flush(sqfs_meta_writer_t *m) +{ + meta_block_t *outblk; + sqfs_u16 header; + sqfs_u32 count; + sqfs_s32 ret; + + if (m->offset == 0) + return 0; + + outblk = calloc(1, sizeof(*outblk)); + if (outblk == NULL) + return SQFS_ERROR_ALLOC; + + ret = m->cmp->do_block(m->cmp, m->data, m->offset, + outblk->data + 2, sizeof(outblk->data) - 2); + if (ret < 0) { + free(outblk); + return ret; + } + + if (ret > 0) { + header = htole16(ret); + count = ret + 2; + } else { + header = htole16(m->offset | 0x8000); + memcpy(outblk->data + 2, m->data, m->offset); + count = m->offset + 2; + } + + memcpy(outblk->data, &header, sizeof(header)); + + ret = 0; + + if (m->flags & SQFS_META_WRITER_KEEP_IN_MEMORY) { + if (m->list == NULL) { + m->list = outblk; + } else { + m->list_end->next = outblk; + } + m->list_end = outblk; + } else { + ret = write_block(m->file, outblk); + free(outblk); + } + + memset(m->data, 0, sizeof(m->data)); + m->offset = 0; + m->block_offset += count; + return ret; +} + +int sqfs_meta_writer_append(sqfs_meta_writer_t *m, const void *data, + size_t size) +{ + size_t diff; + int ret; + + while (size != 0) { + diff = sizeof(m->data) - m->offset; + + if (diff == 0) { + ret = sqfs_meta_writer_flush(m); + if (ret) + return ret; + diff = sizeof(m->data); + } + + if (diff > size) + diff = size; + + memcpy(m->data + m->offset, data, diff); + m->offset += diff; + size -= diff; + data = (const char *)data + diff; + } + + if (m->offset == sizeof(m->data)) + return sqfs_meta_writer_flush(m); + + return 0; +} + +void sqfs_meta_writer_get_position(const sqfs_meta_writer_t *m, + sqfs_u64 *block_start, + sqfs_u32 *offset) +{ + *block_start = m->block_offset; + *offset = m->offset; +} + +void sqfs_meta_writer_reset(sqfs_meta_writer_t *m) +{ + m->block_offset = 0; + m->offset = 0; +} + +int sqfs_meta_write_write_to_file(sqfs_meta_writer_t *m) +{ + meta_block_t *blk; + int ret; + + while (m->list != NULL) { + blk = m->list; + + ret = write_block(m->file, blk); + if (ret) + return ret; + + m->list = blk->next; + free(blk); + } + + m->list_end = NULL; + return 0; +} diff --git a/lib/sqfs/src/misc.c b/lib/sqfs/src/misc.c new file mode 100644 index 0000000..74a4203 --- /dev/null +++ b/lib/sqfs/src/misc.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * misc.c + * + * Copyright (C) 2021 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/predef.h" + +#include + +void sqfs_free(void *ptr) +{ + free(ptr); +} diff --git a/lib/sqfs/src/read_inode.c b/lib/sqfs/src/read_inode.c new file mode 100644 index 0000000..12bef48 --- /dev/null +++ b/lib/sqfs/src/read_inode.c @@ -0,0 +1,424 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "util/util.h" + +#include +#include +#include + +#define SWAB16(x) x = le16toh(x) +#define SWAB32(x) x = le32toh(x) +#define SWAB64(x) x = le64toh(x) + +static int set_mode(sqfs_inode_t *inode) +{ + inode->mode &= ~S_IFMT; + + switch (inode->type) { + case SQFS_INODE_SOCKET: + case SQFS_INODE_EXT_SOCKET: + inode->mode |= S_IFSOCK; + break; + case SQFS_INODE_SLINK: + case SQFS_INODE_EXT_SLINK: + inode->mode |= S_IFLNK; + break; + case SQFS_INODE_FILE: + case SQFS_INODE_EXT_FILE: + inode->mode |= S_IFREG; + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_EXT_BDEV: + inode->mode |= S_IFBLK; + break; + case SQFS_INODE_DIR: + case SQFS_INODE_EXT_DIR: + inode->mode |= S_IFDIR; + break; + case SQFS_INODE_CDEV: + case SQFS_INODE_EXT_CDEV: + inode->mode |= S_IFCHR; + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_EXT_FIFO: + inode->mode |= S_IFIFO; + break; + default: + return SQFS_ERROR_UNSUPPORTED; + } + + return 0; +} + +static sqfs_u64 get_block_count(sqfs_u64 size, sqfs_u64 block_size, + sqfs_u32 frag_index, sqfs_u32 frag_offset) +{ + sqfs_u64 count = size / block_size; + + if ((size % block_size) != 0 && + (frag_index == 0xFFFFFFFF || frag_offset == 0xFFFFFFFF)) { + ++count; + } + + return count; +} + +static int read_inode_file(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + size_t block_size, sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_file_t file; + sqfs_u64 i, count; + int err; + + err = sqfs_meta_reader_read(ir, &file, sizeof(file)); + if (err) + return err; + + SWAB32(file.blocks_start); + SWAB32(file.fragment_index); + SWAB32(file.fragment_offset); + SWAB32(file.file_size); + + count = get_block_count(file.file_size, block_size, + file.fragment_index, file.fragment_offset); + + out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = *base; + out->data.file = file; + out->payload_bytes_available = count * sizeof(sqfs_u32); + out->payload_bytes_used = count * sizeof(sqfs_u32); + + err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); + if (err) { + free(out); + return err; + } + + for (i = 0; i < count; ++i) + SWAB32(out->extra[i]); + + *result = out; + return 0; +} + +static int read_inode_file_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + size_t block_size, sqfs_inode_generic_t **result) +{ + sqfs_inode_file_ext_t file; + sqfs_inode_generic_t *out; + sqfs_u64 i, count; + int err; + + err = sqfs_meta_reader_read(ir, &file, sizeof(file)); + if (err) + return err; + + SWAB64(file.blocks_start); + SWAB64(file.file_size); + SWAB64(file.sparse); + SWAB32(file.nlink); + SWAB32(file.fragment_idx); + SWAB32(file.fragment_offset); + SWAB32(file.xattr_idx); + + count = get_block_count(file.file_size, block_size, + file.fragment_idx, file.fragment_offset); + + out = alloc_flex(sizeof(*out), sizeof(sqfs_u32), count); + if (out == NULL) { + return errno == EOVERFLOW ? SQFS_ERROR_OVERFLOW : + SQFS_ERROR_ALLOC; + } + + out->base = *base; + out->data.file_ext = file; + out->payload_bytes_available = count * sizeof(sqfs_u32); + out->payload_bytes_used = count * sizeof(sqfs_u32); + + err = sqfs_meta_reader_read(ir, out->extra, count * sizeof(sqfs_u32)); + if (err) { + free(out); + return err; + } + + for (i = 0; i < count; ++i) + SWAB32(out->extra[i]); + + *result = out; + return 0; +} + +static int read_inode_slink(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_slink_t slink; + size_t size; + int err; + + err = sqfs_meta_reader_read(ir, &slink, sizeof(slink)); + if (err) + return err; + + SWAB32(slink.nlink); + SWAB32(slink.target_size); + + if (SZ_ADD_OV(slink.target_size, 1, &size) || + SZ_ADD_OV(sizeof(*out), size, &size)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->payload_bytes_available = size - sizeof(*out); + out->payload_bytes_used = size - sizeof(*out) - 1; + out->base = *base; + out->data.slink = slink; + + err = sqfs_meta_reader_read(ir, (void *)out->extra, slink.target_size); + if (err) { + free(out); + return err; + } + + *result = out; + return 0; +} + +static int read_inode_slink_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + sqfs_u32 xattr; + int err; + + err = read_inode_slink(ir, base, result); + if (err) + return err; + + err = sqfs_meta_reader_read(ir, &xattr, sizeof(xattr)); + if (err) { + free(*result); + return err; + } + + (*result)->data.slink_ext.xattr_idx = le32toh(xattr); + return 0; +} + +static int read_inode_dir_ext(sqfs_meta_reader_t *ir, sqfs_inode_t *base, + sqfs_inode_generic_t **result) +{ + size_t i, new_sz, index_max, index_used; + sqfs_inode_generic_t *out, *new; + sqfs_inode_dir_ext_t dir; + sqfs_dir_index_t ent; + int err; + + err = sqfs_meta_reader_read(ir, &dir, sizeof(dir)); + if (err) + return err; + + SWAB32(dir.nlink); + SWAB32(dir.size); + SWAB32(dir.start_block); + SWAB32(dir.parent_inode); + SWAB16(dir.inodex_count); + SWAB16(dir.offset); + SWAB32(dir.xattr_idx); + + index_max = dir.size ? 128 : 0; + index_used = 0; + + out = alloc_flex(sizeof(*out), 1, index_max); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = *base; + out->data.dir_ext = dir; + + if (dir.size == 0) { + *result = out; + return 0; + } + + for (i = 0; i < dir.inodex_count; ++i) { + err = sqfs_meta_reader_read(ir, &ent, sizeof(ent)); + if (err) { + free(out); + return err; + } + + SWAB32(ent.start_block); + SWAB32(ent.index); + SWAB32(ent.size); + + new_sz = index_max; + while (sizeof(ent) + ent.size + 1 > new_sz - index_used) { + if (SZ_MUL_OV(new_sz, 2, &new_sz)) { + free(out); + return SQFS_ERROR_OVERFLOW; + } + } + + if (new_sz > index_max) { + new = realloc(out, sizeof(*out) + new_sz); + if (new == NULL) { + free(out); + return SQFS_ERROR_ALLOC; + } + out = new; + index_max = new_sz; + } + + memcpy((char *)out->extra + index_used, &ent, sizeof(ent)); + index_used += sizeof(ent); + + err = sqfs_meta_reader_read(ir, (char *)out->extra + index_used, + ent.size + 1); + if (err) { + free(out); + return err; + } + + index_used += ent.size + 1; + } + + out->payload_bytes_used = index_used; + out->payload_bytes_available = index_used; + *result = out; + return 0; +} + +int sqfs_meta_reader_read_inode(sqfs_meta_reader_t *ir, + const sqfs_super_t *super, + sqfs_u64 block_start, size_t offset, + sqfs_inode_generic_t **result) +{ + sqfs_inode_generic_t *out; + sqfs_inode_t inode; + int err; + + /* read base inode */ + block_start += super->inode_table_start; + + err = sqfs_meta_reader_seek(ir, block_start, offset); + if (err) + return err; + + err = sqfs_meta_reader_read(ir, &inode, sizeof(inode)); + if (err) + return err; + + SWAB16(inode.type); + SWAB16(inode.mode); + SWAB16(inode.uid_idx); + SWAB16(inode.gid_idx); + SWAB32(inode.mod_time); + SWAB32(inode.inode_number); + + err = set_mode(&inode); + if (err) + return err; + + /* inode types where the size is variable */ + switch (inode.type) { + case SQFS_INODE_FILE: + return read_inode_file(ir, &inode, super->block_size, result); + case SQFS_INODE_SLINK: + return read_inode_slink(ir, &inode, result); + case SQFS_INODE_EXT_FILE: + return read_inode_file_ext(ir, &inode, super->block_size, + result); + case SQFS_INODE_EXT_SLINK: + return read_inode_slink_ext(ir, &inode, result); + case SQFS_INODE_EXT_DIR: + return read_inode_dir_ext(ir, &inode, result); + default: + break; + } + + /* everything else */ + out = calloc(1, sizeof(*out)); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + out->base = inode; + + switch (inode.type) { + case SQFS_INODE_DIR: + err = sqfs_meta_reader_read(ir, &out->data.dir, + sizeof(out->data.dir)); + if (err) + goto fail_free; + + SWAB32(out->data.dir.start_block); + SWAB32(out->data.dir.nlink); + SWAB16(out->data.dir.size); + SWAB16(out->data.dir.offset); + SWAB32(out->data.dir.parent_inode); + break; + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: + err = sqfs_meta_reader_read(ir, &out->data.dev, + sizeof(out->data.dev)); + if (err) + goto fail_free; + SWAB32(out->data.dev.nlink); + SWAB32(out->data.dev.devno); + break; + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: + err = sqfs_meta_reader_read(ir, &out->data.ipc, + sizeof(out->data.ipc)); + if (err) + goto fail_free; + SWAB32(out->data.ipc.nlink); + break; + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: + err = sqfs_meta_reader_read(ir, &out->data.dev_ext, + sizeof(out->data.dev_ext)); + if (err) + goto fail_free; + SWAB32(out->data.dev_ext.nlink); + SWAB32(out->data.dev_ext.devno); + SWAB32(out->data.dev_ext.xattr_idx); + break; + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: + err = sqfs_meta_reader_read(ir, &out->data.ipc_ext, + sizeof(out->data.ipc_ext)); + if (err) + goto fail_free; + SWAB32(out->data.ipc_ext.nlink); + SWAB32(out->data.ipc_ext.xattr_idx); + break; + default: + err = SQFS_ERROR_UNSUPPORTED; + goto fail_free; + } + + *result = out; + return 0; +fail_free: + free(out); + return err; +} diff --git a/lib/sqfs/src/read_super.c b/lib/sqfs/src/read_super.c new file mode 100644 index 0000000..11bc314 --- /dev/null +++ b/lib/sqfs/src/read_super.c @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/error.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_super_read(sqfs_super_t *super, sqfs_file_t *file) +{ + size_t block_size = 0; + sqfs_super_t temp; + int i, ret; + + ret = file->read_at(file, 0, &temp, sizeof(temp)); + if (ret) + return ret; + + temp.magic = le32toh(temp.magic); + temp.inode_count = le32toh(temp.inode_count); + temp.modification_time = le32toh(temp.modification_time); + temp.block_size = le32toh(temp.block_size); + temp.fragment_entry_count = le32toh(temp.fragment_entry_count); + temp.compression_id = le16toh(temp.compression_id); + temp.block_log = le16toh(temp.block_log); + temp.flags = le16toh(temp.flags); + temp.id_count = le16toh(temp.id_count); + temp.version_major = le16toh(temp.version_major); + temp.version_minor = le16toh(temp.version_minor); + temp.root_inode_ref = le64toh(temp.root_inode_ref); + temp.bytes_used = le64toh(temp.bytes_used); + temp.id_table_start = le64toh(temp.id_table_start); + temp.xattr_id_table_start = le64toh(temp.xattr_id_table_start); + temp.inode_table_start = le64toh(temp.inode_table_start); + temp.directory_table_start = le64toh(temp.directory_table_start); + temp.fragment_table_start = le64toh(temp.fragment_table_start); + temp.export_table_start = le64toh(temp.export_table_start); + + if (temp.magic != SQFS_MAGIC) + return SFQS_ERROR_SUPER_MAGIC; + + if ((temp.version_major != SQFS_VERSION_MAJOR) || + (temp.version_minor != SQFS_VERSION_MINOR)) + return SFQS_ERROR_SUPER_VERSION; + + if ((temp.block_size - 1) & temp.block_size) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_size < SQFS_MIN_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_size > SQFS_MAX_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (temp.block_log < 12 || temp.block_log > 20) + return SQFS_ERROR_CORRUPTED; + + block_size = 1; + + for (i = 0; i < temp.block_log; ++i) + block_size <<= 1; + + if (temp.block_size != block_size) + return SQFS_ERROR_CORRUPTED; + + if (temp.compression_id < SQFS_COMP_MIN || + temp.compression_id > SQFS_COMP_MAX) + return SQFS_ERROR_UNSUPPORTED; + + if (temp.id_count == 0) + return SQFS_ERROR_CORRUPTED; + + memcpy(super, &temp, sizeof(temp)); + return 0; +} diff --git a/lib/sqfs/src/read_table.c b/lib/sqfs/src/read_table.c new file mode 100644 index 0000000..c6a9bbe --- /dev/null +++ b/lib/sqfs/src/read_table.c @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * read_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/table.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_read_table(sqfs_file_t *file, sqfs_compressor_t *cmp, + size_t table_size, sqfs_u64 location, sqfs_u64 lower_limit, + sqfs_u64 upper_limit, void **out) +{ + size_t diff, block_count, blk_idx = 0; + sqfs_u64 start, *locations; + sqfs_meta_reader_t *m; + void *data, *ptr; + int err; + + data = malloc(table_size); + if (data == NULL) + return SQFS_ERROR_ALLOC; + + /* restore list from image */ + block_count = table_size / SQFS_META_BLOCK_SIZE; + + if ((table_size % SQFS_META_BLOCK_SIZE) != 0) + ++block_count; + + locations = alloc_array(sizeof(sqfs_u64), block_count); + + if (locations == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail_data; + } + + err = file->read_at(file, location, locations, + sizeof(sqfs_u64) * block_count); + if (err) + goto fail_idx; + + /* Read the actual data */ + m = sqfs_meta_reader_create(file, cmp, lower_limit, upper_limit); + if (m == NULL) { + err = SQFS_ERROR_ALLOC; + goto fail_idx; + } + + ptr = data; + + while (table_size > 0) { + start = le64toh(locations[blk_idx++]); + + err = sqfs_meta_reader_seek(m, start, 0); + if (err) + goto fail; + + diff = SQFS_META_BLOCK_SIZE; + if (diff > table_size) + diff = table_size; + + err = sqfs_meta_reader_read(m, ptr, diff); + if (err) + goto fail; + + ptr = (char *)ptr + diff; + table_size -= diff; + } + + sqfs_drop(m); + free(locations); + *out = data; + return 0; +fail: + sqfs_drop(m); +fail_idx: + free(locations); +fail_data: + free(data); + *out = NULL; + return err; +} diff --git a/lib/sqfs/src/readdir.c b/lib/sqfs/src/readdir.c new file mode 100644 index 0000000..e2dbcd4 --- /dev/null +++ b/lib/sqfs/src/readdir.c @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * readdir.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_reader.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "compat.h" + +#include +#include + +int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m, + sqfs_dir_header_t *hdr) +{ + int err = sqfs_meta_reader_read(m, hdr, sizeof(*hdr)); + if (err) + return err; + + hdr->count = le32toh(hdr->count); + hdr->start_block = le32toh(hdr->start_block); + hdr->inode_number = le32toh(hdr->inode_number); + + if (hdr->count > (SQFS_MAX_DIR_ENT - 1)) + return SQFS_ERROR_CORRUPTED; + + return 0; +} + +int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m, + sqfs_dir_entry_t **result) +{ + sqfs_dir_entry_t ent, *out; + sqfs_u16 *diff_u16; + int err; + + err = sqfs_meta_reader_read(m, &ent, sizeof(ent)); + if (err) + return err; + + diff_u16 = (sqfs_u16 *)&ent.inode_diff; + *diff_u16 = le16toh(*diff_u16); + + ent.offset = le16toh(ent.offset); + ent.type = le16toh(ent.type); + ent.size = le16toh(ent.size); + + out = calloc(1, sizeof(*out) + ent.size + 2); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = ent; + err = sqfs_meta_reader_read(m, out->name, ent.size + 1); + if (err) { + free(out); + return err; + } + + *result = out; + return 0; +} + +int sqfs_readdir_state_init(sqfs_readdir_state_t *s, const sqfs_super_t *super, + const sqfs_inode_generic_t *inode) +{ + memset(s, 0, sizeof(*s)); + + if (inode->base.type == SQFS_INODE_DIR) { + s->init.block = inode->data.dir.start_block; + s->init.offset = inode->data.dir.offset; + s->init.size = inode->data.dir.size; + } else if (inode->base.type == SQFS_INODE_EXT_DIR) { + s->init.block = inode->data.dir_ext.start_block; + s->init.offset = inode->data.dir_ext.offset; + s->init.size = inode->data.dir_ext.size; + } else { + return SQFS_ERROR_NOT_DIR; + } + + s->init.block += super->directory_table_start; + s->current = s->init; + return 0; +} + +int sqfs_meta_reader_readdir(sqfs_meta_reader_t *m, sqfs_readdir_state_t *it, + sqfs_dir_entry_t **ent, + sqfs_u32 *inum, sqfs_u64 *iref) +{ + size_t count; + int ret; + + if (it->entries == 0) { + sqfs_dir_header_t hdr; + + if (it->current.size <= sizeof(hdr)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, + it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_header(m, &hdr); + if (ret != 0) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(hdr); + it->entries = hdr.count + 1; + it->inum_base = hdr.inode_number; + it->inode_block = hdr.start_block; + } + + if (it->current.size <= sizeof(**ent)) + goto out_eof; + + ret = sqfs_meta_reader_seek(m, it->current.block, it->current.offset); + if (ret != 0) + return ret; + + ret = sqfs_meta_reader_read_dir_ent(m, ent); + if (ret) + return ret; + + sqfs_meta_reader_get_position(m, &it->current.block, + &it->current.offset); + + it->current.size -= sizeof(**ent); + it->entries -= 1; + + count = (*ent)->size + 1; + + if (count >= it->current.size) { + it->current.size = 0; + } else { + it->current.size -= count; + } + + if (inum != NULL) + *inum = it->inum_base + (*ent)->inode_diff; + + if (iref != NULL) { + *iref = (sqfs_u64)it->inode_block << 16UL; + *iref |= (*ent)->offset; + } + + return 0; +out_eof: + it->current.size = 0; + it->entries = 0; + return 1; +} diff --git a/lib/sqfs/src/super.c b/lib/sqfs/src/super.c new file mode 100644 index 0000000..470c06a --- /dev/null +++ b/lib/sqfs/src/super.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/error.h" + +#include + +int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, + SQFS_COMPRESSOR compressor) +{ + unsigned int i; + + if (block_size & (block_size - 1)) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (block_size < SQFS_MIN_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + if (block_size > SQFS_MAX_BLOCK_SIZE) + return SQFS_ERROR_SUPER_BLOCK_SIZE; + + memset(super, 0, sizeof(*super)); + super->magic = SQFS_MAGIC; + super->modification_time = mtime; + super->block_size = block_size; + super->compression_id = compressor; + super->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS; + super->flags |= SQFS_FLAG_NO_DUPLICATES; + super->version_major = SQFS_VERSION_MAJOR; + super->version_minor = SQFS_VERSION_MINOR; + super->bytes_used = sizeof(*super); + super->id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->inode_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->directory_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->export_table_start = 0xFFFFFFFFFFFFFFFFUL; + + for (i = block_size; i != 0x01; i >>= 1) + super->block_log += 1; + + return 0; +} diff --git a/lib/sqfs/src/unix/io_file.c b/lib/sqfs/src/unix/io_file.c new file mode 100644 index 0000000..e1fb9db --- /dev/null +++ b/lib/sqfs/src/unix/io_file.c @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * io_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/io.h" +#include "sqfs/error.h" + +#include +#include +#include +#include +#include +#include + + +typedef struct { + sqfs_file_t base; + + bool readonly; + sqfs_u64 size; + int fd; +} sqfs_file_stdio_t; + + +static void stdio_destroy(sqfs_object_t *base) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + close(file->fd); + free(file); +} + +static sqfs_object_t *stdio_copy(const sqfs_object_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + sqfs_file_stdio_t *copy; + int err; + + if (!file->readonly) { + errno = ENOTSUP; + return NULL; + } + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, file, sizeof(*file)); + + copy->fd = dup(file->fd); + if (copy->fd < 0) { + err = errno; + free(copy); + copy = NULL; + errno = err; + } + + return (sqfs_object_t *)copy; +} + +static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, + void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + ssize_t ret; + + while (size > 0) { + ret = pread(file->fd, buffer, size, offset); + + if (ret < 0) { + if (errno == EINTR) + continue; + return SQFS_ERROR_IO; + } + + if (ret == 0) + return SQFS_ERROR_OUT_OF_BOUNDS; + + buffer = (char *)buffer + ret; + size -= ret; + offset += ret; + } + + return 0; +} + +static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, + const void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + ssize_t ret; + + while (size > 0) { + ret = pwrite(file->fd, buffer, size, offset); + + if (ret < 0) { + if (errno == EINTR) + continue; + return SQFS_ERROR_IO; + } + + if (ret == 0) + return SQFS_ERROR_OUT_OF_BOUNDS; + + buffer = (const char *)buffer + ret; + size -= ret; + offset += ret; + } + + if (offset >= file->size) + file->size = offset; + + return 0; +} + +static sqfs_u64 stdio_get_size(const sqfs_file_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + + return file->size; +} + +static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + if (ftruncate(file->fd, size)) + return SQFS_ERROR_IO; + + file->size = size; + return 0; +} + + +sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) +{ + sqfs_file_stdio_t *file; + int open_mode, temp; + sqfs_file_t *base; + struct stat sb; + + if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { + errno = EINVAL; + return NULL; + } + + file = calloc(1, sizeof(*file)); + base = (sqfs_file_t *)file; + if (file == NULL) + return NULL; + + sqfs_object_init(file, stdio_destroy, stdio_copy); + + if (flags & SQFS_FILE_OPEN_READ_ONLY) { + file->readonly = true; + open_mode = O_RDONLY; + } else { + file->readonly = false; + open_mode = O_CREAT | O_RDWR; + + if (flags & SQFS_FILE_OPEN_OVERWRITE) { + open_mode |= O_TRUNC; + } else { + open_mode |= O_EXCL; + } + } + + file->fd = open(filename, open_mode, 0644); + if (file->fd < 0) { + temp = errno; + free(file); + errno = temp; + return NULL; + } + + if (fstat(file->fd, &sb)) { + temp = errno; + close(file->fd); + free(file); + errno = temp; + return NULL; + } + + file->size = sb.st_size; + + base->read_at = stdio_read_at; + base->write_at = stdio_write_at; + base->get_size = stdio_get_size; + base->truncate = stdio_truncate; + return base; +} diff --git a/lib/sqfs/src/win32/io_file.c b/lib/sqfs/src/win32/io_file.c new file mode 100644 index 0000000..548a246 --- /dev/null +++ b/lib/sqfs/src/win32/io_file.c @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * io_file.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/io.h" +#include "sqfs/error.h" + +#include + +#define WIN32_LEAN_AND_MEAN +#include + + +typedef struct { + sqfs_file_t base; + + bool readonly; + sqfs_u64 size; + HANDLE fd; +} sqfs_file_stdio_t; + + +static void stdio_destroy(sqfs_object_t *base) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + + CloseHandle(file->fd); + free(file); +} + +static sqfs_object_t *stdio_copy(const sqfs_object_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + sqfs_file_stdio_t *copy; + BOOL ret; + + if (!file->readonly) { + SetLastError(ERROR_NOT_SUPPORTED); + return NULL; + } + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, file, sizeof(*file)); + + ret = DuplicateHandle(GetCurrentProcess(), file->fd, + GetCurrentProcess(), ©->fd, + 0, FALSE, DUPLICATE_SAME_ACCESS); + + if (!ret) { + free(copy); + return NULL; + } + + return (sqfs_object_t *)copy; +} + +static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, + void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + DWORD actually_read; + LARGE_INTEGER pos; + + if (offset >= file->size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + if (size == 0) + return 0; + + if ((offset + size - 1) >= file->size) + return SQFS_ERROR_OUT_OF_BOUNDS; + + pos.QuadPart = offset; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + while (size > 0) { + if (!ReadFile(file->fd, buffer, size, &actually_read, NULL)) + return SQFS_ERROR_IO; + + size -= actually_read; + buffer = (char *)buffer + actually_read; + } + + return 0; +} + +static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, + const void *buffer, size_t size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + DWORD actually_read; + LARGE_INTEGER pos; + + if (size == 0) + return 0; + + pos.QuadPart = offset; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + while (size > 0) { + if (!WriteFile(file->fd, buffer, size, &actually_read, NULL)) + return SQFS_ERROR_IO; + + size -= actually_read; + buffer = (char *)buffer + actually_read; + offset += actually_read; + + if (offset > file->size) + file->size = offset; + } + + return 0; +} + +static sqfs_u64 stdio_get_size(const sqfs_file_t *base) +{ + const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; + + return file->size; +} + +static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) +{ + sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; + LARGE_INTEGER pos; + + pos.QuadPart = size; + + if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) + return SQFS_ERROR_IO; + + if (!SetEndOfFile(file->fd)) + return SQFS_ERROR_IO; + + file->size = size; + return 0; +} + + +sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) +{ + int access_flags, creation_mode, share_mode; + sqfs_file_stdio_t *file; + LARGE_INTEGER size; + sqfs_file_t *base; + WCHAR *wpath = NULL; + DWORD length; + + if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { + SetLastError(ERROR_INVALID_PARAMETER); + return NULL; + } + + if (!(flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM)) { + length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); + if (length <= 0) + return NULL; + + wpath = calloc(sizeof(wpath[0]), length + 1); + if (wpath == NULL) { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, + wpath, length + 1); + wpath[length] = '\0'; + } + + file = calloc(1, sizeof(*file)); + base = (sqfs_file_t *)file; + if (file == NULL) { + free(wpath); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + sqfs_object_init(file, stdio_destroy, stdio_copy); + + if (flags & SQFS_FILE_OPEN_READ_ONLY) { + file->readonly = true; + access_flags = GENERIC_READ; + creation_mode = OPEN_EXISTING; + share_mode = FILE_SHARE_READ; + } else { + file->readonly = false; + access_flags = GENERIC_READ | GENERIC_WRITE; + share_mode = 0; + + if (flags & SQFS_FILE_OPEN_OVERWRITE) { + creation_mode = CREATE_ALWAYS; + } else { + creation_mode = CREATE_NEW; + } + } + + if (flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM) { + file->fd = CreateFileA(filename, access_flags, share_mode, NULL, + creation_mode, FILE_ATTRIBUTE_NORMAL, + NULL); + } else { + file->fd = CreateFileW(wpath, access_flags, share_mode, NULL, + creation_mode, FILE_ATTRIBUTE_NORMAL, + NULL); + } + + free(wpath); + + if (file->fd == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } + + if (!GetFileSizeEx(file->fd, &size)) { + free(file); + return NULL; + } + + file->size = size.QuadPart; + base->read_at = stdio_read_at; + base->write_at = stdio_write_at; + base->get_size = stdio_get_size; + base->truncate = stdio_truncate; + return base; +} diff --git a/lib/sqfs/src/write_inode.c b/lib/sqfs/src/write_inode.c new file mode 100644 index 0000000..118b713 --- /dev/null +++ b/lib/sqfs/src/write_inode.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_inode.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/error.h" +#include "sqfs/inode.h" +#include "sqfs/dir.h" +#include "compat.h" + +#include +#include + +#if defined(_WIN32) || defined(__WINDOWS__) +# include +# ifdef _MSC_VER +# define alloca _alloca +# endif +#elif defined(HAVE_ALLOCA_H) +# include +#endif + +static int write_block_sizes(sqfs_meta_writer_t *ir, + const sqfs_inode_generic_t *n) +{ + sqfs_u32 *sizes; + size_t i; + + if (n->payload_bytes_used < sizeof(sizes[0])) + return 0; + + if ((n->payload_bytes_used % sizeof(sizes[0])) != 0) + return SQFS_ERROR_CORRUPTED; + + sizes = alloca(n->payload_bytes_used); + + for (i = 0; i < (n->payload_bytes_used / sizeof(sizes[0])); ++i) + sizes[i] = htole32(n->extra[i]); + + return sqfs_meta_writer_append(ir, sizes, n->payload_bytes_used); +} + +static int write_dir_index(sqfs_meta_writer_t *ir, const sqfs_u8 *data, + size_t count) +{ + sqfs_dir_index_t ent; + size_t len; + int err; + + while (count > sizeof(ent)) { + memcpy(&ent, data, sizeof(ent)); + data += sizeof(ent); + count -= sizeof(ent); + len = ent.size + 1; + + if (len > count) + return SQFS_ERROR_CORRUPTED; + + ent.start_block = htole32(ent.start_block); + ent.index = htole32(ent.index); + ent.size = htole32(ent.size); + + err = sqfs_meta_writer_append(ir, &ent, sizeof(ent)); + if (err) + return err; + + err = sqfs_meta_writer_append(ir, data, len); + if (err) + return err; + + data += len; + count -= len; + } + + return 0; +} + +int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *ir, + const sqfs_inode_generic_t *n) +{ + sqfs_inode_t base; + int ret; + + base.type = htole16(n->base.type); + base.mode = htole16(n->base.mode & ~SQFS_INODE_MODE_MASK); + base.uid_idx = htole16(n->base.uid_idx); + base.gid_idx = htole16(n->base.gid_idx); + base.mod_time = htole32(n->base.mod_time); + base.inode_number = htole32(n->base.inode_number); + + ret = sqfs_meta_writer_append(ir, &base, sizeof(base)); + if (ret) + return ret; + + switch (n->base.type) { + case SQFS_INODE_DIR: { + sqfs_inode_dir_t dir = { + .start_block = htole32(n->data.dir.start_block), + .nlink = htole32(n->data.dir.nlink), + .size = htole16(n->data.dir.size), + .offset = htole16(n->data.dir.offset), + .parent_inode = htole32(n->data.dir.parent_inode), + }; + return sqfs_meta_writer_append(ir, &dir, sizeof(dir)); + } + case SQFS_INODE_FILE: { + sqfs_inode_file_t file = { + .blocks_start = htole32(n->data.file.blocks_start), + .fragment_index = htole32(n->data.file.fragment_index), + .fragment_offset = + htole32(n->data.file.fragment_offset), + .file_size = htole32(n->data.file.file_size), + }; + ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); + if (ret) + return ret; + return write_block_sizes(ir, n); + } + case SQFS_INODE_SLINK: { + sqfs_inode_slink_t slink = { + .nlink = htole32(n->data.slink.nlink), + .target_size = htole32(n->data.slink.target_size), + }; + ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); + if (ret) + return ret; + return sqfs_meta_writer_append(ir, n->extra, + n->data.slink.target_size); + } + case SQFS_INODE_BDEV: + case SQFS_INODE_CDEV: { + sqfs_inode_dev_t dev = { + .nlink = htole32(n->data.dev.nlink), + .devno = htole32(n->data.dev.devno), + }; + return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); + } + case SQFS_INODE_FIFO: + case SQFS_INODE_SOCKET: { + sqfs_inode_ipc_t ipc = { + .nlink = htole32(n->data.ipc.nlink), + }; + return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); + } + case SQFS_INODE_EXT_DIR: { + sqfs_inode_dir_ext_t dir = { + .nlink = htole32(n->data.dir_ext.nlink), + .size = htole32(n->data.dir_ext.size), + .start_block = htole32(n->data.dir_ext.start_block), + .parent_inode = htole32(n->data.dir_ext.parent_inode), + .inodex_count = htole16(n->data.dir_ext.inodex_count), + .offset = htole16(n->data.dir_ext.offset), + .xattr_idx = htole32(n->data.dir_ext.xattr_idx), + }; + ret = sqfs_meta_writer_append(ir, &dir, sizeof(dir)); + if (ret) + return ret; + return write_dir_index(ir, (const sqfs_u8 *)n->extra, + n->payload_bytes_used); + } + case SQFS_INODE_EXT_FILE: { + sqfs_inode_file_ext_t file = { + .blocks_start = htole64(n->data.file_ext.blocks_start), + .file_size = htole64(n->data.file_ext.file_size), + .sparse = htole64(n->data.file_ext.sparse), + .nlink = htole32(n->data.file_ext.nlink), + .fragment_idx = htole32(n->data.file_ext.fragment_idx), + .fragment_offset = + htole32(n->data.file_ext.fragment_offset), + .xattr_idx = htole32(n->data.file_ext.xattr_idx), + }; + ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); + if (ret) + return ret; + return write_block_sizes(ir, n); + } + case SQFS_INODE_EXT_SLINK: { + sqfs_inode_slink_t slink = { + .nlink = htole32(n->data.slink_ext.nlink), + .target_size = htole32(n->data.slink_ext.target_size), + }; + sqfs_u32 xattr = htole32(n->data.slink_ext.xattr_idx); + + ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); + if (ret) + return ret; + ret = sqfs_meta_writer_append(ir, n->extra, + n->data.slink_ext.target_size); + if (ret) + return ret; + return sqfs_meta_writer_append(ir, &xattr, sizeof(xattr)); + } + case SQFS_INODE_EXT_BDEV: + case SQFS_INODE_EXT_CDEV: { + sqfs_inode_dev_ext_t dev = { + .nlink = htole32(n->data.dev_ext.nlink), + .devno = htole32(n->data.dev_ext.devno), + .xattr_idx = htole32(n->data.dev_ext.xattr_idx), + }; + return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); + } + case SQFS_INODE_EXT_FIFO: + case SQFS_INODE_EXT_SOCKET: { + sqfs_inode_ipc_ext_t ipc = { + .nlink = htole32(n->data.ipc_ext.nlink), + .xattr_idx = htole32(n->data.ipc_ext.xattr_idx), + }; + return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); + } + default: + break; + } + + return SQFS_ERROR_UNSUPPORTED; +} diff --git a/lib/sqfs/src/write_super.c b/lib/sqfs/src/write_super.c new file mode 100644 index 0000000..35127da --- /dev/null +++ b/lib/sqfs/src/write_super.c @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_super.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/super.h" +#include "sqfs/io.h" +#include "compat.h" + +int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file) +{ + sqfs_super_t copy; + + copy.magic = htole32(super->magic); + copy.inode_count = htole32(super->inode_count); + copy.modification_time = htole32(super->modification_time); + copy.block_size = htole32(super->block_size); + copy.fragment_entry_count = htole32(super->fragment_entry_count); + copy.compression_id = htole16(super->compression_id); + copy.block_log = htole16(super->block_log); + copy.flags = htole16(super->flags); + copy.id_count = htole16(super->id_count); + copy.version_major = htole16(super->version_major); + copy.version_minor = htole16(super->version_minor); + copy.root_inode_ref = htole64(super->root_inode_ref); + copy.bytes_used = htole64(super->bytes_used); + copy.id_table_start = htole64(super->id_table_start); + copy.xattr_id_table_start = htole64(super->xattr_id_table_start); + copy.inode_table_start = htole64(super->inode_table_start); + copy.directory_table_start = htole64(super->directory_table_start); + copy.fragment_table_start = htole64(super->fragment_table_start); + copy.export_table_start = htole64(super->export_table_start); + + return file->write_at(file, 0, ©, sizeof(copy)); +} diff --git a/lib/sqfs/src/write_table.c b/lib/sqfs/src/write_table.c new file mode 100644 index 0000000..6f28a75 --- /dev/null +++ b/lib/sqfs/src/write_table.c @@ -0,0 +1,81 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_table.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/meta_writer.h" +#include "sqfs/error.h" +#include "sqfs/super.h" +#include "sqfs/table.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include + +int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, + const void *data, size_t table_size, sqfs_u64 *start) +{ + size_t block_count, list_size, diff, blkidx = 0; + sqfs_u64 off, *locations; + sqfs_meta_writer_t *m; + int ret; + + block_count = table_size / SQFS_META_BLOCK_SIZE; + if ((table_size % SQFS_META_BLOCK_SIZE) != 0) + ++block_count; + + locations = alloc_array(sizeof(sqfs_u64), block_count); + + if (locations == NULL) + return SQFS_ERROR_ALLOC; + + /* Write actual data */ + m = sqfs_meta_writer_create(file, cmp, 0); + if (m == NULL) { + ret = SQFS_ERROR_ALLOC; + goto out_idx; + } + + while (table_size > 0) { + locations[blkidx++] = htole64(file->get_size(file)); + + diff = SQFS_META_BLOCK_SIZE; + if (diff > table_size) + diff = table_size; + + ret = sqfs_meta_writer_append(m, data, diff); + if (ret) + goto out; + + data = (const char *)data + diff; + table_size -= diff; + } + + ret = sqfs_meta_writer_flush(m); + if (ret) + goto out; + + /* write location list */ + *start = file->get_size(file); + + list_size = sizeof(sqfs_u64) * block_count; + + off = file->get_size(file); + + ret = file->write_at(file, off, locations, list_size); + if (ret) + goto out; + + /* cleanup */ + ret = 0; +out: + sqfs_drop(m); +out_idx: + free(locations); + return ret; +} diff --git a/lib/sqfs/src/xattr/xattr.c b/lib/sqfs/src/xattr/xattr.c new file mode 100644 index 0000000..29ecebf --- /dev/null +++ b/lib/sqfs/src/xattr/xattr.c @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * write_xattr.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" + +#include + +static const struct { + const char *prefix; + SQFS_XATTR_TYPE type; +} xattr_types[] = { + { "user.", SQFS_XATTR_USER }, + { "trusted.", SQFS_XATTR_TRUSTED }, + { "security.", SQFS_XATTR_SECURITY }, +}; + +int sqfs_get_xattr_prefix_id(const char *key) +{ + size_t i, len; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + len = strlen(xattr_types[i].prefix); + + if (strncmp(key, xattr_types[i].prefix, len) == 0 && + strlen(key) > len) { + return xattr_types[i].type; + } + } + + return SQFS_ERROR_UNSUPPORTED; +} + +const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) +{ + size_t i; + + for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { + if (xattr_types[i].type == id) + return xattr_types[i].prefix; + } + + return NULL; +} diff --git a/lib/sqfs/src/xattr/xattr_reader.c b/lib/sqfs/src/xattr/xattr_reader.c new file mode 100644 index 0000000..9e3ea76 --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_reader.c @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_reader.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_reader.h" +#include "sqfs/meta_reader.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" +#include "util/util.h" + +#include +#include +#include + +struct sqfs_xattr_reader_t { + sqfs_object_t base; + + sqfs_u64 xattr_start; + sqfs_u64 xattr_end; + + size_t num_id_blocks; + size_t num_ids; + + sqfs_u64 *id_block_starts; + + sqfs_meta_reader_t *idrd; + sqfs_meta_reader_t *kvrd; +}; + +static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; + sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); + + if (copy == NULL) + return NULL; + + memcpy(copy, xr, sizeof(*xr)); + + if (xr->kvrd != NULL) { + copy->kvrd = sqfs_copy(xr->kvrd); + if (copy->kvrd == NULL) + goto fail; + } + + if (xr->idrd != NULL) { + copy->idrd = sqfs_copy(xr->idrd); + if (copy->idrd == NULL) + goto fail; + } + + if (xr->id_block_starts != NULL) { + copy->id_block_starts = alloc_array(sizeof(sqfs_u64), + xr->num_id_blocks); + if (copy->id_block_starts == NULL) + goto fail; + + memcpy(copy->id_block_starts, xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + } + + return (sqfs_object_t *)copy; +fail: + sqfs_drop(copy->idrd); + sqfs_drop(copy->kvrd); + free(copy); + return NULL; +} + +static void xattr_reader_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; + + sqfs_drop(xr->kvrd); + sqfs_drop(xr->idrd); + free(xr->id_block_starts); + free(xr); +} + +int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, + sqfs_file_t *file, sqfs_compressor_t *cmp) +{ + sqfs_xattr_id_table_t idtbl; + size_t i; + int err; + + /* sanity check */ + if (super->flags & SQFS_FLAG_NO_XATTRS) + return 0; + + if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) + return 0; + + if (super->xattr_id_table_start >= super->bytes_used) + return SQFS_ERROR_OUT_OF_BOUNDS; + + /* cleanup pre-existing data */ + xr->idrd = sqfs_drop(xr->idrd); + xr->kvrd = sqfs_drop(xr->kvrd); + + free(xr->id_block_starts); + xr->id_block_starts = NULL; + + /* read the locations table */ + err = file->read_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + xr->xattr_start = le64toh(idtbl.xattr_table_start); + xr->num_ids = le32toh(idtbl.xattr_ids); + xr->num_id_blocks = + (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; + + if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) + xr->num_id_blocks += 1; + + xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); + if (xr->id_block_starts == NULL) { + if (errno == EOVERFLOW) + return SQFS_ERROR_OVERFLOW; + return SQFS_ERROR_ALLOC; + } + + err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), + xr->id_block_starts, + sizeof(sqfs_u64) * xr->num_id_blocks); + if (err) + goto fail_blocks; + + for (i = 0; i < xr->num_id_blocks; ++i) { + xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); + + if (xr->id_block_starts[i] > super->bytes_used) { + err = SQFS_ERROR_OUT_OF_BOUNDS; + goto fail_blocks; + } + } + + /* create the meta data readers */ + xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->idrd == NULL) + goto fail_blocks; + + xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, + super->bytes_used); + if (xr->kvrd == NULL) + goto fail_idrd; + + xr->xattr_end = super->bytes_used; + return 0; +fail_idrd: + xr->idrd = sqfs_drop(xr->idrd); +fail_blocks: + free(xr->id_block_starts); + xr->id_block_starts = NULL; + return err; +} + +int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, + sqfs_xattr_entry_t **key_out) +{ + sqfs_xattr_entry_t key, *out; + const char *prefix; + size_t plen, total; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); + if (ret) + return ret; + + key.type = le16toh(key.type); + key.size = le16toh(key.size); + + prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); + if (prefix == NULL) + return SQFS_ERROR_UNSUPPORTED; + + plen = strlen(prefix); + + if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || + SZ_ADD_OV(sizeof(*out), total, &total)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, total); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = key; + memcpy(out->key, prefix, plen); + + ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); + if (ret) { + free(out); + return ret; + } + + *key_out = out; + return 0; +} + +int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, + const sqfs_xattr_entry_t *key, + sqfs_xattr_value_t **val_out) +{ + size_t offset, new_offset, size; + sqfs_xattr_value_t value, *out; + sqfs_u64 ref, start, new_start; + int ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (ret) + return ret; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); + if (ret) + return ret; + + sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); + + new_start = xr->xattr_start + (ref >> 16); + if (new_start >= xr->xattr_end) + return SQFS_ERROR_OUT_OF_BOUNDS; + + new_offset = ref & 0xFFFF; + if (new_offset >= SQFS_META_BLOCK_SIZE) + return SQFS_ERROR_OUT_OF_BOUNDS; + + ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); + if (ret) + return ret; + } + + value.size = le32toh(value.size); + + if (SZ_ADD_OV(sizeof(*out), value.size, &size) || + SZ_ADD_OV(size, 1, &size)) { + return SQFS_ERROR_OVERFLOW; + } + + out = calloc(1, size); + if (out == NULL) + return SQFS_ERROR_ALLOC; + + *out = value; + + ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); + if (ret) + goto fail; + + if (key->type & SQFS_XATTR_FLAG_OOL) { + ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); + if (ret) + goto fail; + } + + *val_out = out; + return 0; +fail: + free(out); + return ret; +} + +int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, + const sqfs_xattr_id_t *desc) +{ + sqfs_u32 offset = desc->xattr & 0xFFFF; + sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); + + return sqfs_meta_reader_seek(xr->kvrd, block, offset); +} + +int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, + sqfs_xattr_id_t *desc) +{ + size_t block, offset; + int ret; + + memset(desc, 0, sizeof(*desc)); + + if (idx == 0xFFFFFFFF) + return 0; + + if (xr->kvrd == NULL || xr->idrd == NULL) + return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; + + if (idx >= xr->num_ids) + return SQFS_ERROR_OUT_OF_BOUNDS; + + offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; + block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; + + ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], + offset); + if (ret) + return ret; + + ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); + if (ret) + return ret; + + desc->xattr = le64toh(desc->xattr); + desc->count = le32toh(desc->count); + desc->size = le32toh(desc->size); + return 0; +} + +sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) +{ + sqfs_xattr_reader_t *xr; + + if (flags != 0) + return NULL; + + xr = calloc(1, sizeof(*xr)); + if (xr == NULL) + return NULL; + + sqfs_object_init(xr, xattr_reader_destroy, xattr_reader_copy); + return xr; +} diff --git a/lib/sqfs/src/xattr/xattr_writer.c b/lib/sqfs/src/xattr/xattr_writer.c new file mode 100644 index 0000000..39e1b05 --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer.c @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) +{ + const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; + sqfs_xattr_writer_t *copy; + kv_block_desc_t *it; + + copy = calloc(1, sizeof(*copy)); + if (copy == NULL) + return NULL; + + memcpy(copy, xwr, sizeof(*xwr)); + + if (str_table_copy(©->keys, &xwr->keys)) + goto fail_keys; + + if (str_table_copy(©->values, &xwr->values)) + goto fail_values; + + if (array_init_copy(©->kv_pairs, &xwr->kv_pairs)) + goto fail_pairs; + + if (rbtree_copy(&xwr->kv_block_tree, ©->kv_block_tree) != 0) + goto fail_tree; + + for (it = xwr->kv_block_first; it != NULL; it = it->next) { + rbtree_node_t *n = rbtree_lookup(©->kv_block_tree, it); + + if (copy->kv_block_last == NULL) { + copy->kv_block_first = rbtree_node_key(n); + copy->kv_block_last = copy->kv_block_first; + } else { + copy->kv_block_last->next = rbtree_node_key(n); + copy->kv_block_last = copy->kv_block_last->next; + } + + copy->kv_block_last->next = NULL; + } + + return (sqfs_object_t *)copy; +fail_tree: + array_cleanup(©->kv_pairs); +fail_pairs: + str_table_cleanup(©->values); +fail_values: + str_table_cleanup(©->keys); +fail_keys: + free(copy); + return NULL; +} + +static void xattr_writer_destroy(sqfs_object_t *obj) +{ + sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; + + rbtree_cleanup(&xwr->kv_block_tree); + array_cleanup(&xwr->kv_pairs); + str_table_cleanup(&xwr->values); + str_table_cleanup(&xwr->keys); + free(xwr); +} + +static int block_compare(const void *context, + const void *lhs, const void *rhs) +{ + const sqfs_xattr_writer_t *xwr = context; + const kv_block_desc_t *l = lhs, *r = rhs; + + if (l->count != r->count) + return l->count < r->count ? -1 : 1; + + if (l->start == r->start) + return 0; + + return memcmp((sqfs_u64 *)xwr->kv_pairs.data + l->start, + (sqfs_u64 *)xwr->kv_pairs.data + r->start, + l->count * xwr->kv_pairs.size); +} + +sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags) +{ + sqfs_xattr_writer_t *xwr; + + if (flags != 0) + return NULL; + + xwr = calloc(1, sizeof(*xwr)); + if (xwr == NULL) + return NULL; + + sqfs_object_init(xwr, xattr_writer_destroy, xattr_writer_copy); + + if (str_table_init(&xwr->keys)) + goto fail_keys; + + if (str_table_init(&xwr->values)) + goto fail_values; + + if (array_init(&xwr->kv_pairs, sizeof(sqfs_u64), + XATTR_INITIAL_PAIR_CAP)) { + goto fail_pairs; + } + + if (rbtree_init(&xwr->kv_block_tree, sizeof(kv_block_desc_t), + sizeof(sqfs_u32), block_compare)) { + goto fail_tree; + } + + xwr->kv_block_tree.key_context = xwr; + return xwr; +fail_tree: + array_cleanup(&xwr->kv_pairs); +fail_pairs: + str_table_cleanup(&xwr->values); +fail_values: + str_table_cleanup(&xwr->keys); +fail_keys: + free(xwr); + return NULL; +} diff --git a/lib/sqfs/src/xattr/xattr_writer.h b/lib/sqfs/src/xattr/xattr_writer.h new file mode 100644 index 0000000..792cfae --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer.h + * + * Copyright (C) 2019 David Oberhollenzer + */ +#ifndef XATTR_WRITER_H +#define XATTR_WRITER_H + +#define SQFS_BUILDING_DLL +#include "config.h" + +#include "sqfs/xattr_writer.h" +#include "sqfs/meta_writer.h" +#include "sqfs/super.h" +#include "sqfs/xattr.h" +#include "sqfs/error.h" +#include "sqfs/block.h" +#include "sqfs/io.h" + +#include "util/str_table.h" +#include "util/rbtree.h" +#include "util/array.h" +#include "util/util.h" + +#include +#include +#include + + +#define XATTR_INITIAL_PAIR_CAP 128 + +#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) +#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) +#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) + + +typedef struct kv_block_desc_t { + struct kv_block_desc_t *next; + size_t start; + size_t count; + + sqfs_u64 start_ref; + size_t size_bytes; +} kv_block_desc_t; + +struct sqfs_xattr_writer_t { + sqfs_object_t base; + + str_table_t keys; + str_table_t values; + + array_t kv_pairs; + + size_t kv_start; + + rbtree_t kv_block_tree; + kv_block_desc_t *kv_block_first; + kv_block_desc_t *kv_block_last; + size_t num_blocks; +}; + +#endif /* XATTR_WRITER_H */ diff --git a/lib/sqfs/src/xattr/xattr_writer_flush.c b/lib/sqfs/src/xattr/xattr_writer_flush.c new file mode 100644 index 0000000..a06463f --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer_flush.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_flush.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static void *from_base32(const char *input, size_t *size_out) +{ + sqfs_u8 lo, hi, *out, *ptr; + size_t len; + + len = strlen(input); + *size_out = len / 2; + + out = malloc(*size_out); + if (out == NULL) + return NULL; + + ptr = out; + + while (*input != '\0') { + lo = strchr(hexmap, *(input++)) - hexmap; + hi = strchr(hexmap, *(input++)) - hexmap; + + *(ptr++) = lo | (hi << 4); + } + + return out; +} + +static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, + bool value_is_ool) +{ + sqfs_xattr_entry_t kent; + int type, err; + size_t len; + + type = sqfs_get_xattr_prefix_id(key); + assert(type >= 0); + + key = strchr(key, '.'); + assert(key != NULL); + ++key; + len = strlen(key); + + if (value_is_ool) + type |= SQFS_XATTR_FLAG_OOL; + + memset(&kent, 0, sizeof(kent)); + kent.type = htole16(type); + kent.size = htole16(len); + + err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, key, len); + if (err) + return err; + + return sizeof(kent) + len; +} + +static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, + sqfs_u64 *value_ref_out) +{ + sqfs_xattr_value_t vent; + sqfs_u32 offset; + sqfs_u64 block; + size_t size; + void *value; + int err; + + value = from_base32(value_str, &size); + if (value == NULL) + return SQFS_ERROR_ALLOC; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(size); + + sqfs_meta_writer_get_position(mw, &block, &offset); + *value_ref_out = (block << 16) | (offset & 0xFFFF); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + goto fail; + + err = sqfs_meta_writer_append(mw, value, size); + if (err) + goto fail; + + free(value); + return sizeof(vent) + size; +fail: + free(value); + return err; +} + +static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) +{ + sqfs_xattr_value_t vent; + sqfs_u64 ref; + int err; + + memset(&vent, 0, sizeof(vent)); + vent.size = htole32(sizeof(location)); + ref = htole64(location); + + err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); + if (err) + return err; + + err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); + if (err) + return err; + + return sizeof(vent) + sizeof(ref); +} + +static bool should_store_ool(const char *val_str, size_t refcount) +{ + if (refcount < 2) + return false; + + /* + Storing in line needs this many bytes: refcount * len + + Storing out-of-line needs this many: len + (refcount - 1) * 8 + + Out-of-line prefereable iff refcount > 1 and: + refcount * len > len + (refcount - 1) * 8 + => refcount * len - len > (refcount - 1) * 8 + => (refcount - 1) * len > (refcount - 1) * 8 + => len > 8 + */ + return (strlen(val_str) / 2) > sizeof(sqfs_u64); +} + +static int write_block_pairs(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw, + const kv_block_desc_t *blk, + sqfs_u64 *ool_locations) +{ + const char *key_str, *value_str; + sqfs_s32 diff, total = 0; + size_t i, refcount; + sqfs_u64 ref; + + for (i = 0; i < blk->count; ++i) { + sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[blk->start + i]; + sqfs_u32 key_idx = GET_KEY(ent); + sqfs_u32 val_idx = GET_VALUE(ent); + + key_str = str_table_get_string(&xwr->keys, key_idx); + value_str = str_table_get_string(&xwr->values, val_idx); + + if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { + diff = write_key(mw, key_str, false); + if (diff < 0) + return diff; + total += diff; + + diff = write_value(mw, value_str, &ref); + if (diff < 0) + return diff; + total += diff; + + refcount = str_table_get_ref_count(&xwr->values, + val_idx); + + if (should_store_ool(value_str, refcount)) + ool_locations[val_idx] = ref; + } else { + diff = write_key(mw, key_str, true); + if (diff < 0) + return diff; + total += diff; + + diff = write_value_ool(mw, ool_locations[val_idx]); + if (diff < 0) + return diff; + total += diff; + } + } + + return total; +} + +static int write_kv_pairs(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw) +{ + sqfs_u64 block, *ool_locations; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_s32 size; + size_t i; + + ool_locations = alloc_array(sizeof(ool_locations[0]), + str_table_count(&xwr->values)); + if (ool_locations == NULL) + return SQFS_ERROR_ALLOC; + + for (i = 0; i < str_table_count(&xwr->values); ++i) + ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; + + for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { + sqfs_meta_writer_get_position(mw, &block, &offset); + blk->start_ref = (block << 16) | (offset & 0xFFFF); + + size = write_block_pairs(xwr, mw, blk, ool_locations); + if (size < 0) { + free(ool_locations); + return size; + } + + blk->size_bytes = size; + } + + free(ool_locations); + return sqfs_meta_writer_flush(mw); +} + +static int write_id_table(const sqfs_xattr_writer_t *xwr, + sqfs_meta_writer_t *mw, + sqfs_u64 *locations) +{ + sqfs_xattr_id_t id_ent; + kv_block_desc_t *blk; + sqfs_u32 offset; + sqfs_u64 block; + size_t i = 0; + int err; + + locations[i++] = 0; + + for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { + memset(&id_ent, 0, sizeof(id_ent)); + id_ent.xattr = htole64(blk->start_ref); + id_ent.count = htole32(blk->count); + id_ent.size = htole32(blk->size_bytes); + + err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); + if (err) + return err; + + sqfs_meta_writer_get_position(mw, &block, &offset); + if (block != locations[i - 1]) + locations[i++] = block; + } + + return sqfs_meta_writer_flush(mw); +} + +static int write_location_table(const sqfs_xattr_writer_t *xwr, + sqfs_u64 kv_start, sqfs_file_t *file, + const sqfs_super_t *super, sqfs_u64 *locations, + size_t loc_count) +{ + sqfs_xattr_id_table_t idtbl; + int err; + + memset(&idtbl, 0, sizeof(idtbl)); + idtbl.xattr_table_start = htole64(kv_start); + idtbl.xattr_ids = htole32(xwr->num_blocks); + + err = file->write_at(file, super->xattr_id_table_start, + &idtbl, sizeof(idtbl)); + if (err) + return err; + + return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), + locations, sizeof(locations[0]) * loc_count); +} + +static int alloc_location_table(const sqfs_xattr_writer_t *xwr, + sqfs_u64 **tbl_out, size_t *szout) +{ + sqfs_u64 *locations; + size_t size, count; + + if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) + return SQFS_ERROR_OVERFLOW; + + count = size / SQFS_META_BLOCK_SIZE; + if (size % SQFS_META_BLOCK_SIZE) + ++count; + + locations = alloc_array(sizeof(sqfs_u64), count); + if (locations == NULL) + return SQFS_ERROR_ALLOC; + + *tbl_out = locations; + *szout = count; + return 0; +} + +int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, + sqfs_super_t *super, sqfs_compressor_t *cmp) +{ + sqfs_u64 *locations = NULL, kv_start, id_start; + sqfs_meta_writer_t *mw; + size_t i, count; + int err; + + if (xwr->kv_pairs.used == 0 || xwr->num_blocks == 0) { + super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; + super->flags |= SQFS_FLAG_NO_XATTRS; + return 0; + } + + mw = sqfs_meta_writer_create(file, cmp, 0); + if (mw == NULL) + return SQFS_ERROR_ALLOC; + + kv_start = file->get_size(file); + err = write_kv_pairs(xwr, mw); + if (err) + goto out; + + sqfs_meta_writer_reset(mw); + + id_start = file->get_size(file); + err = alloc_location_table(xwr, &locations, &count); + if (err) + goto out; + + err = write_id_table(xwr, mw, locations); + if (err) + goto out; + + super->xattr_id_table_start = file->get_size(file); + super->flags &= ~SQFS_FLAG_NO_XATTRS; + + for (i = 0; i < count; ++i) + locations[i] = htole64(locations[i] + id_start); + + err = write_location_table(xwr, kv_start, file, super, + locations, count); +out: + free(locations); + sqfs_drop(mw); + return err; +} diff --git a/lib/sqfs/src/xattr/xattr_writer_record.c b/lib/sqfs/src/xattr/xattr_writer_record.c new file mode 100644 index 0000000..81bbf6b --- /dev/null +++ b/lib/sqfs/src/xattr/xattr_writer_record.c @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* + * xattr_writer_record.c + * + * Copyright (C) 2019 David Oberhollenzer + */ +#include "xattr_writer.h" + +static const char *hexmap = "0123456789ABCDEF"; + +static char *to_base32(const void *input, size_t size) +{ + const sqfs_u8 *in = input; + char *out, *ptr; + size_t i; + + out = malloc(2 * size + 1); + if (out == NULL) + return NULL; + + ptr = out; + + for (i = 0; i < size; ++i) { + *(ptr++) = hexmap[ in[i] & 0x0F]; + *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; + } + + *ptr = '\0'; + return out; +} + +static int compare_u64(const void *a, const void *b) +{ + sqfs_u64 lhs = *((const sqfs_u64 *)a); + sqfs_u64 rhs = *((const sqfs_u64 *)b); + + return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); +} + +int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags) +{ + if (flags != 0) + return SQFS_ERROR_UNSUPPORTED; + + xwr->kv_start = xwr->kv_pairs.used; + return 0; +} + +int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, + const void *value, size_t size) +{ + size_t i, key_index, old_value_index, value_index; + sqfs_u64 kv_pair; + char *value_str; + int err; + + if (sqfs_get_xattr_prefix_id(key) < 0) + return SQFS_ERROR_UNSUPPORTED; + + err = str_table_get_index(&xwr->keys, key, &key_index); + if (err) + return err; + + value_str = to_base32(value, size); + if (value_str == NULL) + return SQFS_ERROR_ALLOC; + + err = str_table_get_index(&xwr->values, value_str, &value_index); + free(value_str); + if (err) + return err; + + str_table_add_ref(&xwr->values, value_index); + + if (sizeof(size_t) > sizeof(sqfs_u32)) { + if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) + return SQFS_ERROR_OVERFLOW; + } + + kv_pair = MK_PAIR(key_index, value_index); + + for (i = xwr->kv_start; i < xwr->kv_pairs.used; ++i) { + sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[i]; + + if (ent == kv_pair) + return 0; + + if (GET_KEY(ent) == key_index) { + old_value_index = GET_VALUE(ent); + + str_table_del_ref(&xwr->values, old_value_index); + + ((sqfs_u64 *)xwr->kv_pairs.data)[i] = kv_pair; + return 0; + } + } + + return array_append(&xwr->kv_pairs, &kv_pair); +} + +int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) +{ + kv_block_desc_t blk; + rbtree_node_t *n; + sqfs_u32 index; + int ret; + + memset(&blk, 0, sizeof(blk)); + blk.start = xwr->kv_start; + blk.count = xwr->kv_pairs.used - xwr->kv_start; + + if (blk.count == 0) { + *out = 0xFFFFFFFF; + return 0; + } + + array_sort_range(&xwr->kv_pairs, blk.start, blk.count, compare_u64); + + n = rbtree_lookup(&xwr->kv_block_tree, &blk); + + if (n != NULL) { + index = *((sqfs_u32 *)rbtree_node_value(n)); + xwr->kv_pairs.used = xwr->kv_start; + } else { + index = xwr->num_blocks; + + ret = rbtree_insert(&xwr->kv_block_tree, &blk, &index); + if (ret != 0) + return ret; + + xwr->num_blocks += 1; + n = rbtree_lookup(&xwr->kv_block_tree, &blk); + + if (xwr->kv_block_last == NULL) { + xwr->kv_block_first = rbtree_node_key(n); + xwr->kv_block_last = xwr->kv_block_first; + } else { + xwr->kv_block_last->next = rbtree_node_key(n); + xwr->kv_block_last = xwr->kv_block_last->next; + } + } + + *out = index; + return 0; +} diff --git a/lib/sqfs/super.c b/lib/sqfs/super.c deleted file mode 100644 index 470c06a..0000000 --- a/lib/sqfs/super.c +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/error.h" - -#include - -int sqfs_super_init(sqfs_super_t *super, size_t block_size, sqfs_u32 mtime, - SQFS_COMPRESSOR compressor) -{ - unsigned int i; - - if (block_size & (block_size - 1)) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (block_size < SQFS_MIN_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - if (block_size > SQFS_MAX_BLOCK_SIZE) - return SQFS_ERROR_SUPER_BLOCK_SIZE; - - memset(super, 0, sizeof(*super)); - super->magic = SQFS_MAGIC; - super->modification_time = mtime; - super->block_size = block_size; - super->compression_id = compressor; - super->flags = SQFS_FLAG_NO_FRAGMENTS | SQFS_FLAG_NO_XATTRS; - super->flags |= SQFS_FLAG_NO_DUPLICATES; - super->version_major = SQFS_VERSION_MAJOR; - super->version_minor = SQFS_VERSION_MINOR; - super->bytes_used = sizeof(*super); - super->id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->inode_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->directory_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->fragment_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->export_table_start = 0xFFFFFFFFFFFFFFFFUL; - - for (i = block_size; i != 0x01; i >>= 1) - super->block_log += 1; - - return 0; -} diff --git a/lib/sqfs/unix/io_file.c b/lib/sqfs/unix/io_file.c deleted file mode 100644 index e1fb9db..0000000 --- a/lib/sqfs/unix/io_file.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * io_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/io.h" -#include "sqfs/error.h" - -#include -#include -#include -#include -#include -#include - - -typedef struct { - sqfs_file_t base; - - bool readonly; - sqfs_u64 size; - int fd; -} sqfs_file_stdio_t; - - -static void stdio_destroy(sqfs_object_t *base) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - close(file->fd); - free(file); -} - -static sqfs_object_t *stdio_copy(const sqfs_object_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - sqfs_file_stdio_t *copy; - int err; - - if (!file->readonly) { - errno = ENOTSUP; - return NULL; - } - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, file, sizeof(*file)); - - copy->fd = dup(file->fd); - if (copy->fd < 0) { - err = errno; - free(copy); - copy = NULL; - errno = err; - } - - return (sqfs_object_t *)copy; -} - -static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, - void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - ssize_t ret; - - while (size > 0) { - ret = pread(file->fd, buffer, size, offset); - - if (ret < 0) { - if (errno == EINTR) - continue; - return SQFS_ERROR_IO; - } - - if (ret == 0) - return SQFS_ERROR_OUT_OF_BOUNDS; - - buffer = (char *)buffer + ret; - size -= ret; - offset += ret; - } - - return 0; -} - -static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, - const void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - ssize_t ret; - - while (size > 0) { - ret = pwrite(file->fd, buffer, size, offset); - - if (ret < 0) { - if (errno == EINTR) - continue; - return SQFS_ERROR_IO; - } - - if (ret == 0) - return SQFS_ERROR_OUT_OF_BOUNDS; - - buffer = (const char *)buffer + ret; - size -= ret; - offset += ret; - } - - if (offset >= file->size) - file->size = offset; - - return 0; -} - -static sqfs_u64 stdio_get_size(const sqfs_file_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - - return file->size; -} - -static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - if (ftruncate(file->fd, size)) - return SQFS_ERROR_IO; - - file->size = size; - return 0; -} - - -sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) -{ - sqfs_file_stdio_t *file; - int open_mode, temp; - sqfs_file_t *base; - struct stat sb; - - if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { - errno = EINVAL; - return NULL; - } - - file = calloc(1, sizeof(*file)); - base = (sqfs_file_t *)file; - if (file == NULL) - return NULL; - - sqfs_object_init(file, stdio_destroy, stdio_copy); - - if (flags & SQFS_FILE_OPEN_READ_ONLY) { - file->readonly = true; - open_mode = O_RDONLY; - } else { - file->readonly = false; - open_mode = O_CREAT | O_RDWR; - - if (flags & SQFS_FILE_OPEN_OVERWRITE) { - open_mode |= O_TRUNC; - } else { - open_mode |= O_EXCL; - } - } - - file->fd = open(filename, open_mode, 0644); - if (file->fd < 0) { - temp = errno; - free(file); - errno = temp; - return NULL; - } - - if (fstat(file->fd, &sb)) { - temp = errno; - close(file->fd); - free(file); - errno = temp; - return NULL; - } - - file->size = sb.st_size; - - base->read_at = stdio_read_at; - base->write_at = stdio_write_at; - base->get_size = stdio_get_size; - base->truncate = stdio_truncate; - return base; -} diff --git a/lib/sqfs/win32/io_file.c b/lib/sqfs/win32/io_file.c deleted file mode 100644 index 548a246..0000000 --- a/lib/sqfs/win32/io_file.c +++ /dev/null @@ -1,237 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * io_file.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/io.h" -#include "sqfs/error.h" - -#include - -#define WIN32_LEAN_AND_MEAN -#include - - -typedef struct { - sqfs_file_t base; - - bool readonly; - sqfs_u64 size; - HANDLE fd; -} sqfs_file_stdio_t; - - -static void stdio_destroy(sqfs_object_t *base) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - - CloseHandle(file->fd); - free(file); -} - -static sqfs_object_t *stdio_copy(const sqfs_object_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - sqfs_file_stdio_t *copy; - BOOL ret; - - if (!file->readonly) { - SetLastError(ERROR_NOT_SUPPORTED); - return NULL; - } - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, file, sizeof(*file)); - - ret = DuplicateHandle(GetCurrentProcess(), file->fd, - GetCurrentProcess(), ©->fd, - 0, FALSE, DUPLICATE_SAME_ACCESS); - - if (!ret) { - free(copy); - return NULL; - } - - return (sqfs_object_t *)copy; -} - -static int stdio_read_at(sqfs_file_t *base, sqfs_u64 offset, - void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - DWORD actually_read; - LARGE_INTEGER pos; - - if (offset >= file->size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - if (size == 0) - return 0; - - if ((offset + size - 1) >= file->size) - return SQFS_ERROR_OUT_OF_BOUNDS; - - pos.QuadPart = offset; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - while (size > 0) { - if (!ReadFile(file->fd, buffer, size, &actually_read, NULL)) - return SQFS_ERROR_IO; - - size -= actually_read; - buffer = (char *)buffer + actually_read; - } - - return 0; -} - -static int stdio_write_at(sqfs_file_t *base, sqfs_u64 offset, - const void *buffer, size_t size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - DWORD actually_read; - LARGE_INTEGER pos; - - if (size == 0) - return 0; - - pos.QuadPart = offset; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - while (size > 0) { - if (!WriteFile(file->fd, buffer, size, &actually_read, NULL)) - return SQFS_ERROR_IO; - - size -= actually_read; - buffer = (char *)buffer + actually_read; - offset += actually_read; - - if (offset > file->size) - file->size = offset; - } - - return 0; -} - -static sqfs_u64 stdio_get_size(const sqfs_file_t *base) -{ - const sqfs_file_stdio_t *file = (const sqfs_file_stdio_t *)base; - - return file->size; -} - -static int stdio_truncate(sqfs_file_t *base, sqfs_u64 size) -{ - sqfs_file_stdio_t *file = (sqfs_file_stdio_t *)base; - LARGE_INTEGER pos; - - pos.QuadPart = size; - - if (!SetFilePointerEx(file->fd, pos, NULL, FILE_BEGIN)) - return SQFS_ERROR_IO; - - if (!SetEndOfFile(file->fd)) - return SQFS_ERROR_IO; - - file->size = size; - return 0; -} - - -sqfs_file_t *sqfs_open_file(const char *filename, sqfs_u32 flags) -{ - int access_flags, creation_mode, share_mode; - sqfs_file_stdio_t *file; - LARGE_INTEGER size; - sqfs_file_t *base; - WCHAR *wpath = NULL; - DWORD length; - - if (flags & ~SQFS_FILE_OPEN_ALL_FLAGS) { - SetLastError(ERROR_INVALID_PARAMETER); - return NULL; - } - - if (!(flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM)) { - length = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0); - if (length <= 0) - return NULL; - - wpath = calloc(sizeof(wpath[0]), length + 1); - if (wpath == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - MultiByteToWideChar(CP_UTF8, 0, filename, -1, - wpath, length + 1); - wpath[length] = '\0'; - } - - file = calloc(1, sizeof(*file)); - base = (sqfs_file_t *)file; - if (file == NULL) { - free(wpath); - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - sqfs_object_init(file, stdio_destroy, stdio_copy); - - if (flags & SQFS_FILE_OPEN_READ_ONLY) { - file->readonly = true; - access_flags = GENERIC_READ; - creation_mode = OPEN_EXISTING; - share_mode = FILE_SHARE_READ; - } else { - file->readonly = false; - access_flags = GENERIC_READ | GENERIC_WRITE; - share_mode = 0; - - if (flags & SQFS_FILE_OPEN_OVERWRITE) { - creation_mode = CREATE_ALWAYS; - } else { - creation_mode = CREATE_NEW; - } - } - - if (flags & SQFS_FILE_OPEN_NO_CHARSET_XFRM) { - file->fd = CreateFileA(filename, access_flags, share_mode, NULL, - creation_mode, FILE_ATTRIBUTE_NORMAL, - NULL); - } else { - file->fd = CreateFileW(wpath, access_flags, share_mode, NULL, - creation_mode, FILE_ATTRIBUTE_NORMAL, - NULL); - } - - free(wpath); - - if (file->fd == INVALID_HANDLE_VALUE) { - free(file); - return NULL; - } - - if (!GetFileSizeEx(file->fd, &size)) { - free(file); - return NULL; - } - - file->size = size.QuadPart; - base->read_at = stdio_read_at; - base->write_at = stdio_write_at; - base->get_size = stdio_get_size; - base->truncate = stdio_truncate; - return base; -} diff --git a/lib/sqfs/write_inode.c b/lib/sqfs/write_inode.c deleted file mode 100644 index 118b713..0000000 --- a/lib/sqfs/write_inode.c +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_inode.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/error.h" -#include "sqfs/inode.h" -#include "sqfs/dir.h" -#include "compat.h" - -#include -#include - -#if defined(_WIN32) || defined(__WINDOWS__) -# include -# ifdef _MSC_VER -# define alloca _alloca -# endif -#elif defined(HAVE_ALLOCA_H) -# include -#endif - -static int write_block_sizes(sqfs_meta_writer_t *ir, - const sqfs_inode_generic_t *n) -{ - sqfs_u32 *sizes; - size_t i; - - if (n->payload_bytes_used < sizeof(sizes[0])) - return 0; - - if ((n->payload_bytes_used % sizeof(sizes[0])) != 0) - return SQFS_ERROR_CORRUPTED; - - sizes = alloca(n->payload_bytes_used); - - for (i = 0; i < (n->payload_bytes_used / sizeof(sizes[0])); ++i) - sizes[i] = htole32(n->extra[i]); - - return sqfs_meta_writer_append(ir, sizes, n->payload_bytes_used); -} - -static int write_dir_index(sqfs_meta_writer_t *ir, const sqfs_u8 *data, - size_t count) -{ - sqfs_dir_index_t ent; - size_t len; - int err; - - while (count > sizeof(ent)) { - memcpy(&ent, data, sizeof(ent)); - data += sizeof(ent); - count -= sizeof(ent); - len = ent.size + 1; - - if (len > count) - return SQFS_ERROR_CORRUPTED; - - ent.start_block = htole32(ent.start_block); - ent.index = htole32(ent.index); - ent.size = htole32(ent.size); - - err = sqfs_meta_writer_append(ir, &ent, sizeof(ent)); - if (err) - return err; - - err = sqfs_meta_writer_append(ir, data, len); - if (err) - return err; - - data += len; - count -= len; - } - - return 0; -} - -int sqfs_meta_writer_write_inode(sqfs_meta_writer_t *ir, - const sqfs_inode_generic_t *n) -{ - sqfs_inode_t base; - int ret; - - base.type = htole16(n->base.type); - base.mode = htole16(n->base.mode & ~SQFS_INODE_MODE_MASK); - base.uid_idx = htole16(n->base.uid_idx); - base.gid_idx = htole16(n->base.gid_idx); - base.mod_time = htole32(n->base.mod_time); - base.inode_number = htole32(n->base.inode_number); - - ret = sqfs_meta_writer_append(ir, &base, sizeof(base)); - if (ret) - return ret; - - switch (n->base.type) { - case SQFS_INODE_DIR: { - sqfs_inode_dir_t dir = { - .start_block = htole32(n->data.dir.start_block), - .nlink = htole32(n->data.dir.nlink), - .size = htole16(n->data.dir.size), - .offset = htole16(n->data.dir.offset), - .parent_inode = htole32(n->data.dir.parent_inode), - }; - return sqfs_meta_writer_append(ir, &dir, sizeof(dir)); - } - case SQFS_INODE_FILE: { - sqfs_inode_file_t file = { - .blocks_start = htole32(n->data.file.blocks_start), - .fragment_index = htole32(n->data.file.fragment_index), - .fragment_offset = - htole32(n->data.file.fragment_offset), - .file_size = htole32(n->data.file.file_size), - }; - ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); - if (ret) - return ret; - return write_block_sizes(ir, n); - } - case SQFS_INODE_SLINK: { - sqfs_inode_slink_t slink = { - .nlink = htole32(n->data.slink.nlink), - .target_size = htole32(n->data.slink.target_size), - }; - ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); - if (ret) - return ret; - return sqfs_meta_writer_append(ir, n->extra, - n->data.slink.target_size); - } - case SQFS_INODE_BDEV: - case SQFS_INODE_CDEV: { - sqfs_inode_dev_t dev = { - .nlink = htole32(n->data.dev.nlink), - .devno = htole32(n->data.dev.devno), - }; - return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); - } - case SQFS_INODE_FIFO: - case SQFS_INODE_SOCKET: { - sqfs_inode_ipc_t ipc = { - .nlink = htole32(n->data.ipc.nlink), - }; - return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); - } - case SQFS_INODE_EXT_DIR: { - sqfs_inode_dir_ext_t dir = { - .nlink = htole32(n->data.dir_ext.nlink), - .size = htole32(n->data.dir_ext.size), - .start_block = htole32(n->data.dir_ext.start_block), - .parent_inode = htole32(n->data.dir_ext.parent_inode), - .inodex_count = htole16(n->data.dir_ext.inodex_count), - .offset = htole16(n->data.dir_ext.offset), - .xattr_idx = htole32(n->data.dir_ext.xattr_idx), - }; - ret = sqfs_meta_writer_append(ir, &dir, sizeof(dir)); - if (ret) - return ret; - return write_dir_index(ir, (const sqfs_u8 *)n->extra, - n->payload_bytes_used); - } - case SQFS_INODE_EXT_FILE: { - sqfs_inode_file_ext_t file = { - .blocks_start = htole64(n->data.file_ext.blocks_start), - .file_size = htole64(n->data.file_ext.file_size), - .sparse = htole64(n->data.file_ext.sparse), - .nlink = htole32(n->data.file_ext.nlink), - .fragment_idx = htole32(n->data.file_ext.fragment_idx), - .fragment_offset = - htole32(n->data.file_ext.fragment_offset), - .xattr_idx = htole32(n->data.file_ext.xattr_idx), - }; - ret = sqfs_meta_writer_append(ir, &file, sizeof(file)); - if (ret) - return ret; - return write_block_sizes(ir, n); - } - case SQFS_INODE_EXT_SLINK: { - sqfs_inode_slink_t slink = { - .nlink = htole32(n->data.slink_ext.nlink), - .target_size = htole32(n->data.slink_ext.target_size), - }; - sqfs_u32 xattr = htole32(n->data.slink_ext.xattr_idx); - - ret = sqfs_meta_writer_append(ir, &slink, sizeof(slink)); - if (ret) - return ret; - ret = sqfs_meta_writer_append(ir, n->extra, - n->data.slink_ext.target_size); - if (ret) - return ret; - return sqfs_meta_writer_append(ir, &xattr, sizeof(xattr)); - } - case SQFS_INODE_EXT_BDEV: - case SQFS_INODE_EXT_CDEV: { - sqfs_inode_dev_ext_t dev = { - .nlink = htole32(n->data.dev_ext.nlink), - .devno = htole32(n->data.dev_ext.devno), - .xattr_idx = htole32(n->data.dev_ext.xattr_idx), - }; - return sqfs_meta_writer_append(ir, &dev, sizeof(dev)); - } - case SQFS_INODE_EXT_FIFO: - case SQFS_INODE_EXT_SOCKET: { - sqfs_inode_ipc_ext_t ipc = { - .nlink = htole32(n->data.ipc_ext.nlink), - .xattr_idx = htole32(n->data.ipc_ext.xattr_idx), - }; - return sqfs_meta_writer_append(ir, &ipc, sizeof(ipc)); - } - default: - break; - } - - return SQFS_ERROR_UNSUPPORTED; -} diff --git a/lib/sqfs/write_super.c b/lib/sqfs/write_super.c deleted file mode 100644 index 35127da..0000000 --- a/lib/sqfs/write_super.c +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_super.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/super.h" -#include "sqfs/io.h" -#include "compat.h" - -int sqfs_super_write(const sqfs_super_t *super, sqfs_file_t *file) -{ - sqfs_super_t copy; - - copy.magic = htole32(super->magic); - copy.inode_count = htole32(super->inode_count); - copy.modification_time = htole32(super->modification_time); - copy.block_size = htole32(super->block_size); - copy.fragment_entry_count = htole32(super->fragment_entry_count); - copy.compression_id = htole16(super->compression_id); - copy.block_log = htole16(super->block_log); - copy.flags = htole16(super->flags); - copy.id_count = htole16(super->id_count); - copy.version_major = htole16(super->version_major); - copy.version_minor = htole16(super->version_minor); - copy.root_inode_ref = htole64(super->root_inode_ref); - copy.bytes_used = htole64(super->bytes_used); - copy.id_table_start = htole64(super->id_table_start); - copy.xattr_id_table_start = htole64(super->xattr_id_table_start); - copy.inode_table_start = htole64(super->inode_table_start); - copy.directory_table_start = htole64(super->directory_table_start); - copy.fragment_table_start = htole64(super->fragment_table_start); - copy.export_table_start = htole64(super->export_table_start); - - return file->write_at(file, 0, ©, sizeof(copy)); -} diff --git a/lib/sqfs/write_table.c b/lib/sqfs/write_table.c deleted file mode 100644 index 6f28a75..0000000 --- a/lib/sqfs/write_table.c +++ /dev/null @@ -1,81 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_table.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/meta_writer.h" -#include "sqfs/error.h" -#include "sqfs/super.h" -#include "sqfs/table.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include - -int sqfs_write_table(sqfs_file_t *file, sqfs_compressor_t *cmp, - const void *data, size_t table_size, sqfs_u64 *start) -{ - size_t block_count, list_size, diff, blkidx = 0; - sqfs_u64 off, *locations; - sqfs_meta_writer_t *m; - int ret; - - block_count = table_size / SQFS_META_BLOCK_SIZE; - if ((table_size % SQFS_META_BLOCK_SIZE) != 0) - ++block_count; - - locations = alloc_array(sizeof(sqfs_u64), block_count); - - if (locations == NULL) - return SQFS_ERROR_ALLOC; - - /* Write actual data */ - m = sqfs_meta_writer_create(file, cmp, 0); - if (m == NULL) { - ret = SQFS_ERROR_ALLOC; - goto out_idx; - } - - while (table_size > 0) { - locations[blkidx++] = htole64(file->get_size(file)); - - diff = SQFS_META_BLOCK_SIZE; - if (diff > table_size) - diff = table_size; - - ret = sqfs_meta_writer_append(m, data, diff); - if (ret) - goto out; - - data = (const char *)data + diff; - table_size -= diff; - } - - ret = sqfs_meta_writer_flush(m); - if (ret) - goto out; - - /* write location list */ - *start = file->get_size(file); - - list_size = sizeof(sqfs_u64) * block_count; - - off = file->get_size(file); - - ret = file->write_at(file, off, locations, list_size); - if (ret) - goto out; - - /* cleanup */ - ret = 0; -out: - sqfs_drop(m); -out_idx: - free(locations); - return ret; -} diff --git a/lib/sqfs/xattr/xattr.c b/lib/sqfs/xattr/xattr.c deleted file mode 100644 index 29ecebf..0000000 --- a/lib/sqfs/xattr/xattr.c +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * write_xattr.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" - -#include - -static const struct { - const char *prefix; - SQFS_XATTR_TYPE type; -} xattr_types[] = { - { "user.", SQFS_XATTR_USER }, - { "trusted.", SQFS_XATTR_TRUSTED }, - { "security.", SQFS_XATTR_SECURITY }, -}; - -int sqfs_get_xattr_prefix_id(const char *key) -{ - size_t i, len; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - len = strlen(xattr_types[i].prefix); - - if (strncmp(key, xattr_types[i].prefix, len) == 0 && - strlen(key) > len) { - return xattr_types[i].type; - } - } - - return SQFS_ERROR_UNSUPPORTED; -} - -const char *sqfs_get_xattr_prefix(SQFS_XATTR_TYPE id) -{ - size_t i; - - for (i = 0; i < sizeof(xattr_types) / sizeof(xattr_types[0]); ++i) { - if (xattr_types[i].type == id) - return xattr_types[i].prefix; - } - - return NULL; -} diff --git a/lib/sqfs/xattr/xattr_reader.c b/lib/sqfs/xattr/xattr_reader.c deleted file mode 100644 index 9e3ea76..0000000 --- a/lib/sqfs/xattr/xattr_reader.c +++ /dev/null @@ -1,336 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_reader.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_reader.h" -#include "sqfs/meta_reader.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" -#include "util/util.h" - -#include -#include -#include - -struct sqfs_xattr_reader_t { - sqfs_object_t base; - - sqfs_u64 xattr_start; - sqfs_u64 xattr_end; - - size_t num_id_blocks; - size_t num_ids; - - sqfs_u64 *id_block_starts; - - sqfs_meta_reader_t *idrd; - sqfs_meta_reader_t *kvrd; -}; - -static sqfs_object_t *xattr_reader_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_reader_t *xr = (const sqfs_xattr_reader_t *)obj; - sqfs_xattr_reader_t *copy = malloc(sizeof(*copy)); - - if (copy == NULL) - return NULL; - - memcpy(copy, xr, sizeof(*xr)); - - if (xr->kvrd != NULL) { - copy->kvrd = sqfs_copy(xr->kvrd); - if (copy->kvrd == NULL) - goto fail; - } - - if (xr->idrd != NULL) { - copy->idrd = sqfs_copy(xr->idrd); - if (copy->idrd == NULL) - goto fail; - } - - if (xr->id_block_starts != NULL) { - copy->id_block_starts = alloc_array(sizeof(sqfs_u64), - xr->num_id_blocks); - if (copy->id_block_starts == NULL) - goto fail; - - memcpy(copy->id_block_starts, xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - } - - return (sqfs_object_t *)copy; -fail: - sqfs_drop(copy->idrd); - sqfs_drop(copy->kvrd); - free(copy); - return NULL; -} - -static void xattr_reader_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_reader_t *xr = (sqfs_xattr_reader_t *)obj; - - sqfs_drop(xr->kvrd); - sqfs_drop(xr->idrd); - free(xr->id_block_starts); - free(xr); -} - -int sqfs_xattr_reader_load(sqfs_xattr_reader_t *xr, const sqfs_super_t *super, - sqfs_file_t *file, sqfs_compressor_t *cmp) -{ - sqfs_xattr_id_table_t idtbl; - size_t i; - int err; - - /* sanity check */ - if (super->flags & SQFS_FLAG_NO_XATTRS) - return 0; - - if (super->xattr_id_table_start == 0xFFFFFFFFFFFFFFFF) - return 0; - - if (super->xattr_id_table_start >= super->bytes_used) - return SQFS_ERROR_OUT_OF_BOUNDS; - - /* cleanup pre-existing data */ - xr->idrd = sqfs_drop(xr->idrd); - xr->kvrd = sqfs_drop(xr->kvrd); - - free(xr->id_block_starts); - xr->id_block_starts = NULL; - - /* read the locations table */ - err = file->read_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - xr->xattr_start = le64toh(idtbl.xattr_table_start); - xr->num_ids = le32toh(idtbl.xattr_ids); - xr->num_id_blocks = - (xr->num_ids * sizeof(sqfs_xattr_id_t)) / SQFS_META_BLOCK_SIZE; - - if ((xr->num_ids * sizeof(sqfs_xattr_id_t)) % SQFS_META_BLOCK_SIZE) - xr->num_id_blocks += 1; - - xr->id_block_starts = alloc_array(sizeof(sqfs_u64), xr->num_id_blocks); - if (xr->id_block_starts == NULL) { - if (errno == EOVERFLOW) - return SQFS_ERROR_OVERFLOW; - return SQFS_ERROR_ALLOC; - } - - err = file->read_at(file, super->xattr_id_table_start + sizeof(idtbl), - xr->id_block_starts, - sizeof(sqfs_u64) * xr->num_id_blocks); - if (err) - goto fail_blocks; - - for (i = 0; i < xr->num_id_blocks; ++i) { - xr->id_block_starts[i] = le64toh(xr->id_block_starts[i]); - - if (xr->id_block_starts[i] > super->bytes_used) { - err = SQFS_ERROR_OUT_OF_BOUNDS; - goto fail_blocks; - } - } - - /* create the meta data readers */ - xr->idrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->idrd == NULL) - goto fail_blocks; - - xr->kvrd = sqfs_meta_reader_create(file, cmp, super->id_table_start, - super->bytes_used); - if (xr->kvrd == NULL) - goto fail_idrd; - - xr->xattr_end = super->bytes_used; - return 0; -fail_idrd: - xr->idrd = sqfs_drop(xr->idrd); -fail_blocks: - free(xr->id_block_starts); - xr->id_block_starts = NULL; - return err; -} - -int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr, - sqfs_xattr_entry_t **key_out) -{ - sqfs_xattr_entry_t key, *out; - const char *prefix; - size_t plen, total; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &key, sizeof(key)); - if (ret) - return ret; - - key.type = le16toh(key.type); - key.size = le16toh(key.size); - - prefix = sqfs_get_xattr_prefix(key.type & SQFS_XATTR_PREFIX_MASK); - if (prefix == NULL) - return SQFS_ERROR_UNSUPPORTED; - - plen = strlen(prefix); - - if (SZ_ADD_OV(plen, key.size, &total) || SZ_ADD_OV(total, 1, &total) || - SZ_ADD_OV(sizeof(*out), total, &total)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, total); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = key; - memcpy(out->key, prefix, plen); - - ret = sqfs_meta_reader_read(xr->kvrd, out->key + plen, key.size); - if (ret) { - free(out); - return ret; - } - - *key_out = out; - return 0; -} - -int sqfs_xattr_reader_read_value(sqfs_xattr_reader_t *xr, - const sqfs_xattr_entry_t *key, - sqfs_xattr_value_t **val_out) -{ - size_t offset, new_offset, size; - sqfs_xattr_value_t value, *out; - sqfs_u64 ref, start, new_start; - int ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); - if (ret) - return ret; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_read(xr->kvrd, &ref, sizeof(ref)); - if (ret) - return ret; - - sqfs_meta_reader_get_position(xr->kvrd, &start, &offset); - - new_start = xr->xattr_start + (ref >> 16); - if (new_start >= xr->xattr_end) - return SQFS_ERROR_OUT_OF_BOUNDS; - - new_offset = ref & 0xFFFF; - if (new_offset >= SQFS_META_BLOCK_SIZE) - return SQFS_ERROR_OUT_OF_BOUNDS; - - ret = sqfs_meta_reader_seek(xr->kvrd, new_start, new_offset); - if (ret) - return ret; - - ret = sqfs_meta_reader_read(xr->kvrd, &value, sizeof(value)); - if (ret) - return ret; - } - - value.size = le32toh(value.size); - - if (SZ_ADD_OV(sizeof(*out), value.size, &size) || - SZ_ADD_OV(size, 1, &size)) { - return SQFS_ERROR_OVERFLOW; - } - - out = calloc(1, size); - if (out == NULL) - return SQFS_ERROR_ALLOC; - - *out = value; - - ret = sqfs_meta_reader_read(xr->kvrd, out->value, value.size); - if (ret) - goto fail; - - if (key->type & SQFS_XATTR_FLAG_OOL) { - ret = sqfs_meta_reader_seek(xr->kvrd, start, offset); - if (ret) - goto fail; - } - - *val_out = out; - return 0; -fail: - free(out); - return ret; -} - -int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr, - const sqfs_xattr_id_t *desc) -{ - sqfs_u32 offset = desc->xattr & 0xFFFF; - sqfs_u64 block = xr->xattr_start + (desc->xattr >> 16); - - return sqfs_meta_reader_seek(xr->kvrd, block, offset); -} - -int sqfs_xattr_reader_get_desc(sqfs_xattr_reader_t *xr, sqfs_u32 idx, - sqfs_xattr_id_t *desc) -{ - size_t block, offset; - int ret; - - memset(desc, 0, sizeof(*desc)); - - if (idx == 0xFFFFFFFF) - return 0; - - if (xr->kvrd == NULL || xr->idrd == NULL) - return idx == 0 ? 0 : SQFS_ERROR_OUT_OF_BOUNDS; - - if (idx >= xr->num_ids) - return SQFS_ERROR_OUT_OF_BOUNDS; - - offset = (idx * sizeof(*desc)) % SQFS_META_BLOCK_SIZE; - block = (idx * sizeof(*desc)) / SQFS_META_BLOCK_SIZE; - - ret = sqfs_meta_reader_seek(xr->idrd, xr->id_block_starts[block], - offset); - if (ret) - return ret; - - ret = sqfs_meta_reader_read(xr->idrd, desc, sizeof(*desc)); - if (ret) - return ret; - - desc->xattr = le64toh(desc->xattr); - desc->count = le32toh(desc->count); - desc->size = le32toh(desc->size); - return 0; -} - -sqfs_xattr_reader_t *sqfs_xattr_reader_create(sqfs_u32 flags) -{ - sqfs_xattr_reader_t *xr; - - if (flags != 0) - return NULL; - - xr = calloc(1, sizeof(*xr)); - if (xr == NULL) - return NULL; - - sqfs_object_init(xr, xattr_reader_destroy, xattr_reader_copy); - return xr; -} diff --git a/lib/sqfs/xattr/xattr_writer.c b/lib/sqfs/xattr/xattr_writer.c deleted file mode 100644 index 39e1b05..0000000 --- a/lib/sqfs/xattr/xattr_writer.c +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static sqfs_object_t *xattr_writer_copy(const sqfs_object_t *obj) -{ - const sqfs_xattr_writer_t *xwr = (const sqfs_xattr_writer_t *)obj; - sqfs_xattr_writer_t *copy; - kv_block_desc_t *it; - - copy = calloc(1, sizeof(*copy)); - if (copy == NULL) - return NULL; - - memcpy(copy, xwr, sizeof(*xwr)); - - if (str_table_copy(©->keys, &xwr->keys)) - goto fail_keys; - - if (str_table_copy(©->values, &xwr->values)) - goto fail_values; - - if (array_init_copy(©->kv_pairs, &xwr->kv_pairs)) - goto fail_pairs; - - if (rbtree_copy(&xwr->kv_block_tree, ©->kv_block_tree) != 0) - goto fail_tree; - - for (it = xwr->kv_block_first; it != NULL; it = it->next) { - rbtree_node_t *n = rbtree_lookup(©->kv_block_tree, it); - - if (copy->kv_block_last == NULL) { - copy->kv_block_first = rbtree_node_key(n); - copy->kv_block_last = copy->kv_block_first; - } else { - copy->kv_block_last->next = rbtree_node_key(n); - copy->kv_block_last = copy->kv_block_last->next; - } - - copy->kv_block_last->next = NULL; - } - - return (sqfs_object_t *)copy; -fail_tree: - array_cleanup(©->kv_pairs); -fail_pairs: - str_table_cleanup(©->values); -fail_values: - str_table_cleanup(©->keys); -fail_keys: - free(copy); - return NULL; -} - -static void xattr_writer_destroy(sqfs_object_t *obj) -{ - sqfs_xattr_writer_t *xwr = (sqfs_xattr_writer_t *)obj; - - rbtree_cleanup(&xwr->kv_block_tree); - array_cleanup(&xwr->kv_pairs); - str_table_cleanup(&xwr->values); - str_table_cleanup(&xwr->keys); - free(xwr); -} - -static int block_compare(const void *context, - const void *lhs, const void *rhs) -{ - const sqfs_xattr_writer_t *xwr = context; - const kv_block_desc_t *l = lhs, *r = rhs; - - if (l->count != r->count) - return l->count < r->count ? -1 : 1; - - if (l->start == r->start) - return 0; - - return memcmp((sqfs_u64 *)xwr->kv_pairs.data + l->start, - (sqfs_u64 *)xwr->kv_pairs.data + r->start, - l->count * xwr->kv_pairs.size); -} - -sqfs_xattr_writer_t *sqfs_xattr_writer_create(sqfs_u32 flags) -{ - sqfs_xattr_writer_t *xwr; - - if (flags != 0) - return NULL; - - xwr = calloc(1, sizeof(*xwr)); - if (xwr == NULL) - return NULL; - - sqfs_object_init(xwr, xattr_writer_destroy, xattr_writer_copy); - - if (str_table_init(&xwr->keys)) - goto fail_keys; - - if (str_table_init(&xwr->values)) - goto fail_values; - - if (array_init(&xwr->kv_pairs, sizeof(sqfs_u64), - XATTR_INITIAL_PAIR_CAP)) { - goto fail_pairs; - } - - if (rbtree_init(&xwr->kv_block_tree, sizeof(kv_block_desc_t), - sizeof(sqfs_u32), block_compare)) { - goto fail_tree; - } - - xwr->kv_block_tree.key_context = xwr; - return xwr; -fail_tree: - array_cleanup(&xwr->kv_pairs); -fail_pairs: - str_table_cleanup(&xwr->values); -fail_values: - str_table_cleanup(&xwr->keys); -fail_keys: - free(xwr); - return NULL; -} diff --git a/lib/sqfs/xattr/xattr_writer.h b/lib/sqfs/xattr/xattr_writer.h deleted file mode 100644 index 792cfae..0000000 --- a/lib/sqfs/xattr/xattr_writer.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer.h - * - * Copyright (C) 2019 David Oberhollenzer - */ -#ifndef XATTR_WRITER_H -#define XATTR_WRITER_H - -#define SQFS_BUILDING_DLL -#include "config.h" - -#include "sqfs/xattr_writer.h" -#include "sqfs/meta_writer.h" -#include "sqfs/super.h" -#include "sqfs/xattr.h" -#include "sqfs/error.h" -#include "sqfs/block.h" -#include "sqfs/io.h" - -#include "util/str_table.h" -#include "util/rbtree.h" -#include "util/array.h" -#include "util/util.h" - -#include -#include -#include - - -#define XATTR_INITIAL_PAIR_CAP 128 - -#define MK_PAIR(key, value) (((sqfs_u64)(key) << 32UL) | (sqfs_u64)(value)) -#define GET_KEY(pair) ((pair >> 32UL) & 0x0FFFFFFFFUL) -#define GET_VALUE(pair) (pair & 0x0FFFFFFFFUL) - - -typedef struct kv_block_desc_t { - struct kv_block_desc_t *next; - size_t start; - size_t count; - - sqfs_u64 start_ref; - size_t size_bytes; -} kv_block_desc_t; - -struct sqfs_xattr_writer_t { - sqfs_object_t base; - - str_table_t keys; - str_table_t values; - - array_t kv_pairs; - - size_t kv_start; - - rbtree_t kv_block_tree; - kv_block_desc_t *kv_block_first; - kv_block_desc_t *kv_block_last; - size_t num_blocks; -}; - -#endif /* XATTR_WRITER_H */ diff --git a/lib/sqfs/xattr/xattr_writer_flush.c b/lib/sqfs/xattr/xattr_writer_flush.c deleted file mode 100644 index a06463f..0000000 --- a/lib/sqfs/xattr/xattr_writer_flush.c +++ /dev/null @@ -1,347 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer_flush.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static const char *hexmap = "0123456789ABCDEF"; - -static void *from_base32(const char *input, size_t *size_out) -{ - sqfs_u8 lo, hi, *out, *ptr; - size_t len; - - len = strlen(input); - *size_out = len / 2; - - out = malloc(*size_out); - if (out == NULL) - return NULL; - - ptr = out; - - while (*input != '\0') { - lo = strchr(hexmap, *(input++)) - hexmap; - hi = strchr(hexmap, *(input++)) - hexmap; - - *(ptr++) = lo | (hi << 4); - } - - return out; -} - -static sqfs_s32 write_key(sqfs_meta_writer_t *mw, const char *key, - bool value_is_ool) -{ - sqfs_xattr_entry_t kent; - int type, err; - size_t len; - - type = sqfs_get_xattr_prefix_id(key); - assert(type >= 0); - - key = strchr(key, '.'); - assert(key != NULL); - ++key; - len = strlen(key); - - if (value_is_ool) - type |= SQFS_XATTR_FLAG_OOL; - - memset(&kent, 0, sizeof(kent)); - kent.type = htole16(type); - kent.size = htole16(len); - - err = sqfs_meta_writer_append(mw, &kent, sizeof(kent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, key, len); - if (err) - return err; - - return sizeof(kent) + len; -} - -static sqfs_s32 write_value(sqfs_meta_writer_t *mw, const char *value_str, - sqfs_u64 *value_ref_out) -{ - sqfs_xattr_value_t vent; - sqfs_u32 offset; - sqfs_u64 block; - size_t size; - void *value; - int err; - - value = from_base32(value_str, &size); - if (value == NULL) - return SQFS_ERROR_ALLOC; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(size); - - sqfs_meta_writer_get_position(mw, &block, &offset); - *value_ref_out = (block << 16) | (offset & 0xFFFF); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - goto fail; - - err = sqfs_meta_writer_append(mw, value, size); - if (err) - goto fail; - - free(value); - return sizeof(vent) + size; -fail: - free(value); - return err; -} - -static sqfs_s32 write_value_ool(sqfs_meta_writer_t *mw, sqfs_u64 location) -{ - sqfs_xattr_value_t vent; - sqfs_u64 ref; - int err; - - memset(&vent, 0, sizeof(vent)); - vent.size = htole32(sizeof(location)); - ref = htole64(location); - - err = sqfs_meta_writer_append(mw, &vent, sizeof(vent)); - if (err) - return err; - - err = sqfs_meta_writer_append(mw, &ref, sizeof(ref)); - if (err) - return err; - - return sizeof(vent) + sizeof(ref); -} - -static bool should_store_ool(const char *val_str, size_t refcount) -{ - if (refcount < 2) - return false; - - /* - Storing in line needs this many bytes: refcount * len - - Storing out-of-line needs this many: len + (refcount - 1) * 8 - - Out-of-line prefereable iff refcount > 1 and: - refcount * len > len + (refcount - 1) * 8 - => refcount * len - len > (refcount - 1) * 8 - => (refcount - 1) * len > (refcount - 1) * 8 - => len > 8 - */ - return (strlen(val_str) / 2) > sizeof(sqfs_u64); -} - -static int write_block_pairs(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw, - const kv_block_desc_t *blk, - sqfs_u64 *ool_locations) -{ - const char *key_str, *value_str; - sqfs_s32 diff, total = 0; - size_t i, refcount; - sqfs_u64 ref; - - for (i = 0; i < blk->count; ++i) { - sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[blk->start + i]; - sqfs_u32 key_idx = GET_KEY(ent); - sqfs_u32 val_idx = GET_VALUE(ent); - - key_str = str_table_get_string(&xwr->keys, key_idx); - value_str = str_table_get_string(&xwr->values, val_idx); - - if (ool_locations[val_idx] == 0xFFFFFFFFFFFFFFFFUL) { - diff = write_key(mw, key_str, false); - if (diff < 0) - return diff; - total += diff; - - diff = write_value(mw, value_str, &ref); - if (diff < 0) - return diff; - total += diff; - - refcount = str_table_get_ref_count(&xwr->values, - val_idx); - - if (should_store_ool(value_str, refcount)) - ool_locations[val_idx] = ref; - } else { - diff = write_key(mw, key_str, true); - if (diff < 0) - return diff; - total += diff; - - diff = write_value_ool(mw, ool_locations[val_idx]); - if (diff < 0) - return diff; - total += diff; - } - } - - return total; -} - -static int write_kv_pairs(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw) -{ - sqfs_u64 block, *ool_locations; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_s32 size; - size_t i; - - ool_locations = alloc_array(sizeof(ool_locations[0]), - str_table_count(&xwr->values)); - if (ool_locations == NULL) - return SQFS_ERROR_ALLOC; - - for (i = 0; i < str_table_count(&xwr->values); ++i) - ool_locations[i] = 0xFFFFFFFFFFFFFFFFUL; - - for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { - sqfs_meta_writer_get_position(mw, &block, &offset); - blk->start_ref = (block << 16) | (offset & 0xFFFF); - - size = write_block_pairs(xwr, mw, blk, ool_locations); - if (size < 0) { - free(ool_locations); - return size; - } - - blk->size_bytes = size; - } - - free(ool_locations); - return sqfs_meta_writer_flush(mw); -} - -static int write_id_table(const sqfs_xattr_writer_t *xwr, - sqfs_meta_writer_t *mw, - sqfs_u64 *locations) -{ - sqfs_xattr_id_t id_ent; - kv_block_desc_t *blk; - sqfs_u32 offset; - sqfs_u64 block; - size_t i = 0; - int err; - - locations[i++] = 0; - - for (blk = xwr->kv_block_first; blk != NULL; blk = blk->next) { - memset(&id_ent, 0, sizeof(id_ent)); - id_ent.xattr = htole64(blk->start_ref); - id_ent.count = htole32(blk->count); - id_ent.size = htole32(blk->size_bytes); - - err = sqfs_meta_writer_append(mw, &id_ent, sizeof(id_ent)); - if (err) - return err; - - sqfs_meta_writer_get_position(mw, &block, &offset); - if (block != locations[i - 1]) - locations[i++] = block; - } - - return sqfs_meta_writer_flush(mw); -} - -static int write_location_table(const sqfs_xattr_writer_t *xwr, - sqfs_u64 kv_start, sqfs_file_t *file, - const sqfs_super_t *super, sqfs_u64 *locations, - size_t loc_count) -{ - sqfs_xattr_id_table_t idtbl; - int err; - - memset(&idtbl, 0, sizeof(idtbl)); - idtbl.xattr_table_start = htole64(kv_start); - idtbl.xattr_ids = htole32(xwr->num_blocks); - - err = file->write_at(file, super->xattr_id_table_start, - &idtbl, sizeof(idtbl)); - if (err) - return err; - - return file->write_at(file, super->xattr_id_table_start + sizeof(idtbl), - locations, sizeof(locations[0]) * loc_count); -} - -static int alloc_location_table(const sqfs_xattr_writer_t *xwr, - sqfs_u64 **tbl_out, size_t *szout) -{ - sqfs_u64 *locations; - size_t size, count; - - if (SZ_MUL_OV(xwr->num_blocks, sizeof(sqfs_xattr_id_t), &size)) - return SQFS_ERROR_OVERFLOW; - - count = size / SQFS_META_BLOCK_SIZE; - if (size % SQFS_META_BLOCK_SIZE) - ++count; - - locations = alloc_array(sizeof(sqfs_u64), count); - if (locations == NULL) - return SQFS_ERROR_ALLOC; - - *tbl_out = locations; - *szout = count; - return 0; -} - -int sqfs_xattr_writer_flush(const sqfs_xattr_writer_t *xwr, sqfs_file_t *file, - sqfs_super_t *super, sqfs_compressor_t *cmp) -{ - sqfs_u64 *locations = NULL, kv_start, id_start; - sqfs_meta_writer_t *mw; - size_t i, count; - int err; - - if (xwr->kv_pairs.used == 0 || xwr->num_blocks == 0) { - super->xattr_id_table_start = 0xFFFFFFFFFFFFFFFFUL; - super->flags |= SQFS_FLAG_NO_XATTRS; - return 0; - } - - mw = sqfs_meta_writer_create(file, cmp, 0); - if (mw == NULL) - return SQFS_ERROR_ALLOC; - - kv_start = file->get_size(file); - err = write_kv_pairs(xwr, mw); - if (err) - goto out; - - sqfs_meta_writer_reset(mw); - - id_start = file->get_size(file); - err = alloc_location_table(xwr, &locations, &count); - if (err) - goto out; - - err = write_id_table(xwr, mw, locations); - if (err) - goto out; - - super->xattr_id_table_start = file->get_size(file); - super->flags &= ~SQFS_FLAG_NO_XATTRS; - - for (i = 0; i < count; ++i) - locations[i] = htole64(locations[i] + id_start); - - err = write_location_table(xwr, kv_start, file, super, - locations, count); -out: - free(locations); - sqfs_drop(mw); - return err; -} diff --git a/lib/sqfs/xattr/xattr_writer_record.c b/lib/sqfs/xattr/xattr_writer_record.c deleted file mode 100644 index 81bbf6b..0000000 --- a/lib/sqfs/xattr/xattr_writer_record.c +++ /dev/null @@ -1,145 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* - * xattr_writer_record.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "xattr_writer.h" - -static const char *hexmap = "0123456789ABCDEF"; - -static char *to_base32(const void *input, size_t size) -{ - const sqfs_u8 *in = input; - char *out, *ptr; - size_t i; - - out = malloc(2 * size + 1); - if (out == NULL) - return NULL; - - ptr = out; - - for (i = 0; i < size; ++i) { - *(ptr++) = hexmap[ in[i] & 0x0F]; - *(ptr++) = hexmap[(in[i] >> 4) & 0x0F]; - } - - *ptr = '\0'; - return out; -} - -static int compare_u64(const void *a, const void *b) -{ - sqfs_u64 lhs = *((const sqfs_u64 *)a); - sqfs_u64 rhs = *((const sqfs_u64 *)b); - - return (lhs < rhs ? -1 : (lhs > rhs ? 1 : 0)); -} - -int sqfs_xattr_writer_begin(sqfs_xattr_writer_t *xwr, sqfs_u32 flags) -{ - if (flags != 0) - return SQFS_ERROR_UNSUPPORTED; - - xwr->kv_start = xwr->kv_pairs.used; - return 0; -} - -int sqfs_xattr_writer_add(sqfs_xattr_writer_t *xwr, const char *key, - const void *value, size_t size) -{ - size_t i, key_index, old_value_index, value_index; - sqfs_u64 kv_pair; - char *value_str; - int err; - - if (sqfs_get_xattr_prefix_id(key) < 0) - return SQFS_ERROR_UNSUPPORTED; - - err = str_table_get_index(&xwr->keys, key, &key_index); - if (err) - return err; - - value_str = to_base32(value, size); - if (value_str == NULL) - return SQFS_ERROR_ALLOC; - - err = str_table_get_index(&xwr->values, value_str, &value_index); - free(value_str); - if (err) - return err; - - str_table_add_ref(&xwr->values, value_index); - - if (sizeof(size_t) > sizeof(sqfs_u32)) { - if (key_index > 0x0FFFFFFFFUL || value_index > 0x0FFFFFFFFUL) - return SQFS_ERROR_OVERFLOW; - } - - kv_pair = MK_PAIR(key_index, value_index); - - for (i = xwr->kv_start; i < xwr->kv_pairs.used; ++i) { - sqfs_u64 ent = ((sqfs_u64 *)xwr->kv_pairs.data)[i]; - - if (ent == kv_pair) - return 0; - - if (GET_KEY(ent) == key_index) { - old_value_index = GET_VALUE(ent); - - str_table_del_ref(&xwr->values, old_value_index); - - ((sqfs_u64 *)xwr->kv_pairs.data)[i] = kv_pair; - return 0; - } - } - - return array_append(&xwr->kv_pairs, &kv_pair); -} - -int sqfs_xattr_writer_end(sqfs_xattr_writer_t *xwr, sqfs_u32 *out) -{ - kv_block_desc_t blk; - rbtree_node_t *n; - sqfs_u32 index; - int ret; - - memset(&blk, 0, sizeof(blk)); - blk.start = xwr->kv_start; - blk.count = xwr->kv_pairs.used - xwr->kv_start; - - if (blk.count == 0) { - *out = 0xFFFFFFFF; - return 0; - } - - array_sort_range(&xwr->kv_pairs, blk.start, blk.count, compare_u64); - - n = rbtree_lookup(&xwr->kv_block_tree, &blk); - - if (n != NULL) { - index = *((sqfs_u32 *)rbtree_node_value(n)); - xwr->kv_pairs.used = xwr->kv_start; - } else { - index = xwr->num_blocks; - - ret = rbtree_insert(&xwr->kv_block_tree, &blk, &index); - if (ret != 0) - return ret; - - xwr->num_blocks += 1; - n = rbtree_lookup(&xwr->kv_block_tree, &blk); - - if (xwr->kv_block_last == NULL) { - xwr->kv_block_first = rbtree_node_key(n); - xwr->kv_block_last = xwr->kv_block_first; - } else { - xwr->kv_block_last->next = rbtree_node_key(n); - xwr->kv_block_last = xwr->kv_block_last->next; - } - } - - *out = index; - return 0; -} -- cgit v1.2.3