From f9d242aed57ce2d41b4b4c5615b1a345d0ad6ee4 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Fri, 30 Jul 2021 16:46:10 +0100 Subject: Add a simple example to show how to extract a single text file Licensed under 0BSD: https://opensource.org/licenses/0BSD Signed-off-by: Luca Boccassi --- .gitignore | 1 + COPYING.md | 5 +- extras/Makemodule.am | 5 +- extras/extract_one.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++ licenses/0BSD.txt | 10 +++ 5 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 extras/extract_one.c create mode 100644 licenses/0BSD.txt diff --git a/.gitignore b/.gitignore index 05afa84..df055d5 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ Doxyfile tests/*.sh /mknastyfs /mk42sqfs +/extract_one /list_files /sqfsbrowse /xattr_benchmark diff --git a/COPYING.md b/COPYING.md index 19f8c5d..d4e73b0 100644 --- a/COPYING.md +++ b/COPYING.md @@ -44,7 +44,7 @@ Although the existing squashfs-tools and the Linux kernel implementation have been used for testing, the source code in this package is neither based on, nor derived from either of them. -## Documentation and the Build System +## Documentation, examples and the Build System The auto-tools based build system has in large parts been hacked together by copy & pasting from various tutorials and other projects (mostly util-linux, @@ -61,6 +61,9 @@ If you use those as a basis for writing about SquashFS or this package, please cite your sources and mark verbatim quotations as such. I won't be angry if you don't, but a thesis supervisor, reviewer or fellow Wikipedian might be. +The `extras/extract_one.c` example file is licensed under the **0BSD license**, +a copy of which can be found in `licenses/0BSD.txt` + # Binary Packages with 3rd Party Libraries If this file is included in a binary release package, additional 3rd party diff --git a/extras/Makemodule.am b/extras/Makemodule.am index d3b80f2..9ebe037 100644 --- a/extras/Makemodule.am +++ b/extras/Makemodule.am @@ -7,6 +7,9 @@ mk42sqfs_LDADD = libsquashfs.la list_files_SOURCES = extras/list_files.c list_files_LDADD = libsquashfs.la +extract_one_SOURCES = extras/extract_one.c +extract_one_LDADD = libsquashfs.la + if WITH_READLINE sqfsbrowse_SOURCES = extras/browse.c sqfsbrowse_CFLAGS = $(AM_CFLAGS) $(READLINE_CFLAGS) @@ -15,4 +18,4 @@ sqfsbrowse_LDADD = libsquashfs.la $(READLINE_LIBS) noinst_PROGRAMS += sqfsbrowse endif -noinst_PROGRAMS += mknastyfs mk42sqfs list_files +noinst_PROGRAMS += mknastyfs mk42sqfs list_files extract_one diff --git a/extras/extract_one.c b/extras/extract_one.c new file mode 100644 index 0000000..fcd8124 --- /dev/null +++ b/extras/extract_one.c @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: 0BSD */ +/* + * extract_one.c + * + * Copyright (C) 2021 Luca Boccassi + */ + +#include "sqfs/compressor.h" +#include "sqfs/data_reader.h" +#include "sqfs/dir_reader.h" +#include "sqfs/error.h" +#include "sqfs/id_table.h" +#include "sqfs/io.h" +#include "sqfs/inode.h" +#include "sqfs/super.h" +#include "sqfs/xattr_reader.h" + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + sqfs_xattr_reader_t *xattr = NULL; + sqfs_data_reader_t *data = NULL; + sqfs_dir_reader_t *dirrd = NULL; + sqfs_compressor_t *cmp = NULL; + sqfs_id_table_t *idtbl = NULL; + sqfs_tree_node_t *n = NULL; + sqfs_file_t *file = NULL; + sqfs_u8 *p, *output = NULL; + sqfs_compressor_config_t cfg; + sqfs_super_t super; + sqfs_u64 file_size; + int status = EXIT_FAILURE, ret; + + if (argc != 3) { + fputs("Usage: extract_one \n", stderr); + return EXIT_FAILURE; + } + + file = sqfs_open_file(argv[1], SQFS_FILE_OPEN_READ_ONLY); + if (file == NULL) { + perror(argv[1]); + return EXIT_FAILURE; + } + + ret = sqfs_super_read(&super, file); + if (ret) { + fprintf(stderr, "%s: error reading super block.\n", argv[1]); + goto out; + } + + sqfs_compressor_config_init(&cfg, super.compression_id, + super.block_size, + SQFS_COMP_FLAG_UNCOMPRESS); + + ret = sqfs_compressor_create(&cfg, &cmp); + if (ret != 0) { + fprintf(stderr, "%s: error creating compressor: %d.\n", argv[1], ret); + goto out; + } + + if (!(super.flags & SQFS_FLAG_NO_XATTRS)) { + xattr = sqfs_xattr_reader_create(0); + if (xattr == NULL) { + fprintf(stderr, "%s: error creating xattr reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_xattr_reader_load(xattr, &super, file, cmp); + if (ret) { + fprintf(stderr, "%s: error loading xattr reader: %d.\n", argv[1], ret); + goto out; + } + } + + idtbl = sqfs_id_table_create(0); + if (idtbl == NULL) { + fprintf(stderr, "%s: error creating ID table: %d.\n", argv[1], ret); + goto out; + } + + ret = sqfs_id_table_read(idtbl, file, &super, cmp); + if (ret) { + fprintf(stderr, "%s: error loading ID table: %d.\n", argv[1], ret); + goto out; + } + + dirrd = sqfs_dir_reader_create(&super, cmp, file, 0); + if (dirrd == NULL) { + fprintf(stderr, "%s: error creating dir reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); + goto out; + } + + data = sqfs_data_reader_create(file, super.block_size, cmp, 0); + if (data == NULL) { + fprintf(stderr, "%s: error creating data reader: %d.\n", argv[1], SQFS_ERROR_ALLOC); + goto out; + } + + ret = sqfs_data_reader_load_fragment_table(data, &super); + if (ret) { + fprintf(stderr, "%s: error loading fragment table: %d.\n", argv[1], ret); + goto out; + } + + ret = sqfs_dir_reader_get_full_hierarchy(dirrd, idtbl, argv[2], 0, &n); + if (ret) { + fprintf(stderr, "%s: error reading filesystem hierarchy: %d.\n", argv[1], ret); + goto out; + } + + if (!S_ISREG(n->inode->base.mode)) { + fprintf(stderr, "/%s is not a file\n", argv[2]); + goto out; + } + + sqfs_inode_get_file_size(n->inode, &file_size); + + output = p = malloc(file_size); + if (output == NULL) { + fprintf(stderr, "malloc failed: %d\n", errno); + goto out; + } + + for (size_t i = 0; i < sqfs_inode_get_file_block_count(n->inode); ++i) { + size_t chunk_size, read = (file_size < super.block_size) ? file_size : super.block_size; + sqfs_u8 *chunk; + + ret = sqfs_data_reader_get_block(data, n->inode, i, &chunk_size, &chunk); + if (ret) { + fprintf(stderr, "reading data block: %d\n", ret); + goto out; + } + + memcpy(p, chunk, chunk_size); + p += chunk_size; + free(chunk); + + file_size -= read; + } + + if (file_size > 0) { + size_t chunk_size; + sqfs_u8 *chunk; + + ret = sqfs_data_reader_get_fragment(data, n->inode, &chunk_size, &chunk); + if (ret) { + fprintf(stderr, "reading fragment block: %d\n", ret); + goto out; + } + + memcpy(p, chunk, chunk_size); + free(chunk); + } + + /* for example simplicity, assume this is a text file */ + fprintf(stdout, "%s\n", (char *)output); + + status = EXIT_SUCCESS; +out: + sqfs_dir_tree_destroy(n); + sqfs_destroy(data); + sqfs_destroy(dirrd); + sqfs_destroy(idtbl); + sqfs_destroy(xattr); + sqfs_destroy(cmp); + sqfs_destroy(file); + free(output); + + return status; +} diff --git a/licenses/0BSD.txt b/licenses/0BSD.txt new file mode 100644 index 0000000..ac85e0e --- /dev/null +++ b/licenses/0BSD.txt @@ -0,0 +1,10 @@ +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. -- cgit v1.2.3