From fa7f378bf627ddcfd7a93a000149e4d8c3810bf5 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Tue, 30 Apr 2019 01:24:16 +0200 Subject: Initial commit Signed-off-by: David Oberhollenzer --- mkfs/Makemodule.am | 5 + mkfs/mksquashfs.c | 70 +++++++++++++ mkfs/options.c | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++ mkfs/options.h | 20 ++++ 4 files changed, 390 insertions(+) create mode 100644 mkfs/Makemodule.am create mode 100644 mkfs/mksquashfs.c create mode 100644 mkfs/options.c create mode 100644 mkfs/options.h (limited to 'mkfs') diff --git a/mkfs/Makemodule.am b/mkfs/Makemodule.am new file mode 100644 index 0000000..1d062b9 --- /dev/null +++ b/mkfs/Makemodule.am @@ -0,0 +1,5 @@ +mksquashfs_SOURCES = mkfs/mksquashfs.c mkfs/options.c mkfs/options.h +mksquashfs_SOURCES += include/squashfs.h +mksquashfs_LDADD = libfstree.a + +bin_PROGRAMS += mksquashfs diff --git a/mkfs/mksquashfs.c b/mkfs/mksquashfs.c new file mode 100644 index 0000000..bc2a215 --- /dev/null +++ b/mkfs/mksquashfs.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "squashfs.h" +#include "options.h" +#include "fstree.h" + +#include +#include +#include +#include +#include +#include + +static void print_tree(int level, tree_node_t *node) +{ + tree_node_t *n; + int i; + + for (i = 1; i < level; ++i) { + fputs("| ", stdout); + } + + if (level) + fputs("+- ", stdout); + + if (S_ISDIR(node->mode)) { + fprintf(stdout, "%s/ (%u, %u, 0%o)\n", node->name, + node->uid, node->gid, node->mode & 07777); + + for (n = node->data.dir->children; n != NULL; n = n->next) { + print_tree(level + 1, n); + } + + if (node->data.dir->children != NULL) { + for (i = 0; i < level; ++i) + fputs("| ", stdout); + + if (level) + fputc('\n', stdout); + } + } else { + fprintf(stdout, "%s (%u, %u, 0%o)\n", node->name, + node->uid, node->gid, node->mode & 07777); + } +} + + +int main(int argc, char **argv) +{ + options_t opt; + fstree_t fs; + + process_command_line(&opt, argc, argv); + + if (fstree_init(&fs, opt.blksz, opt.def_mtime, opt.def_mode, + opt.def_uid, opt.def_gid)) { + return EXIT_FAILURE; + } + + if (fstree_from_file(&fs, opt.infile)) { + fstree_cleanup(&fs); + return EXIT_FAILURE; + } + + fstree_sort(&fs); + + print_tree(0, fs.root); + + fstree_cleanup(&fs); + return EXIT_SUCCESS; +} diff --git a/mkfs/options.c b/mkfs/options.c new file mode 100644 index 0000000..513e76a --- /dev/null +++ b/mkfs/options.c @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include "squashfs.h" +#include "options.h" +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct option long_opts[] = { + { "compressor", required_argument, NULL, 'c' }, + { "block-size", required_argument, NULL, 'b' }, + { "dev-block-size", required_argument, NULL, 'B' }, + { "defaults", required_argument, NULL, 'd' }, + { "force", no_argument, NULL, 'f' }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, +}; + +static const char *short_opts = "c:b:B:d:fhV"; + +enum { + DEF_UID = 0, + DEF_GID, + DEF_MODE, + DEF_MTIME, +}; + +static const char *defaults[] = { + [DEF_UID] = "uid", + [DEF_GID] = "gid", + [DEF_MODE] = "mode", + [DEF_MTIME] = "mtime", + NULL +}; + +#define LICENSE_SHORT "GPLv3+" +#define LICENSE_LONG "GNU GPL version 3 or later" +#define LICENSE_URL "https://gnu.org/licenses/gpl.html" + +extern char *__progname; + +static const char *version_string = +"%s (%s) %s\n" +"Copyright (c) 2019 David Oberhollenzer\n" +"License " LICENSE_SHORT ": " LICENSE_LONG " <" LICENSE_URL ">.\n" +"This is free software: you are free to change and redistribute it.\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +"\n" +"Written by David Oberhollenzer.\n"; + +static const char *help_string = +"Usage: %s [OPTIONS] \n" +"\n" +" is a file containing newline separated entries that describe\n" +"the files to be included in the squashfs image:\n" +"\n" +"# a comment\n" +"file []\n" +"dir \n" +"nod \n" +"slink \n" +"pipe \n" +"sock \n" +"\n" +" Absolute path of the entry in the image.\n" +" If given, location of the input file. Either absolute or relative\n" +" to the description file. If omitted, the image path is used,\n" +" relative to the description file.\n" +" Symlink target.\n" +" Mode/permissions of the entry.\n" +" Numeric user id.\n" +" Numeric group id.\n" +" Device type (b=block, c=character).\n" +" Major number of a device special file.\n" +" Minor number of a device special file.\n" +"\n" +"Example:\n" +"# A simple squashfs image\n" +"dir /dev 0755 0 0\n" +"nod /dev/console 0600 0 0 c 5 1\n" +"dir /root 0700 0 0\n" +"dir /sbin 0755 0 0\n" +"\n" +"# Add a file. Input is relative to this listing.\n" +"file /sbin/init 0755 0 0 ../init/sbin/init\n" +"\n" +"# Read from ./bin/bash. /bin is created implicitly with default attributes.\n" +"file /bin/bash 0755 0 0" +"\n" +"Possible options:\n" +"\n" +" --compressor, -c Select the compressor to use.\n" +" directories (defaults to 'xz').\n" +" --block-size, -b Block size to use for Squashfs image.\n" +" Defaults to %u.\n" +" --dev-block-size, -B Device block size to padd the image to.\n" +" Defaults to %u.\n" +" --defaults, -d A comma seperated list of default values for\n" +" implicitly created directories.\n" +"\n" +" Possible options:\n" +" uid= 0 if not set.\n" +" gid= 0 if not set.\n" +" mode= 0755 if not set.\n" +" mtime= 0 if not set.\n" +"\n" +" --force, -f Overwrite the output file if it exists.\n" +" --help, -h Print help text and exit.\n" +" --version, -V Print version information and exit.\n" +"\n"; + +static const char *compressors[] = { + [SQFS_COMP_GZIP] = "gzip", + [SQFS_COMP_LZMA] = "lzma", + [SQFS_COMP_LZO] = "lzo", + [SQFS_COMP_XZ] = "xz", + [SQFS_COMP_LZ4] = "lz4", + [SQFS_COMP_ZSTD] = "zstd", +}; + +static long read_number(const char *name, const char *str, long min, long max) +{ + long base = 10, result = 0; + int x; + + if (str[0] == '0') { + if (str[1] == 'x' || str[1] == 'X') { + base = 16; + str += 2; + } else { + base = 8; + } + } + + if (!isxdigit(*str)) + goto fail_num; + + while (isxdigit(*str)) { + x = *(str++); + + if (isupper(x)) { + x = x - 'A' + 10; + } else if (islower(x)) { + x = x - 'a' + 10; + } else { + x -= '0'; + } + + if (x >= base) + goto fail_num; + + if (result > (LONG_MAX - x) / base) + goto fail_ov; + + result = result * base + x; + } + + if (result < min) + goto fail_uf; + + if (result > max) + goto fail_ov; + + return result; +fail_num: + fprintf(stderr, "%s: expected numeric value > 0\n", name); + goto fail; +fail_uf: + fprintf(stderr, "%s: number to small\n", name); + goto fail; +fail_ov: + fprintf(stderr, "%s: number to large\n", name); + goto fail; +fail: + exit(EXIT_FAILURE); +} + +static void process_defaults(options_t *opt, char *subopts) +{ + char *value; + int i; + + while (*subopts != '\0') { + i = getsubopt(&subopts, (char *const *)defaults, &value); + + if (value == NULL) { + fprintf(stderr, "Missing value for option %s\n", + defaults[i]); + exit(EXIT_FAILURE); + } + + switch (i) { + case DEF_UID: + opt->def_uid = read_number("Default user ID", value, + 0, 0xFFFFFFFF); + break; + case DEF_GID: + opt->def_gid = read_number("Default group ID", value, + 0, 0xFFFFFFFF); + break; + case DEF_MODE: + opt->def_mode = read_number("Default permissions", + value, 0, 0xFFFFFFFF); + break; + case DEF_MTIME: + opt->def_mtime = read_number("Default mtime", value, + 0, 0xFFFFFFFF); + break; + default: + fprintf(stderr, "Unknown option '%s'\n", value); + exit(EXIT_FAILURE); + } + } +} + +void process_command_line(options_t *opt, int argc, char **argv) +{ + int i; + + opt->def_uid = 0; + opt->def_gid = 0; + opt->def_mode = 0755; + opt->def_mtime = 0; + opt->outmode = O_WRONLY | O_CREAT | O_EXCL; + opt->compressor = SQFS_COMP_XZ; + opt->blksz = SQFS_DEFAULT_BLOCK_SIZE; + opt->devblksz = SQFS_DEVBLK_SIZE; + opt->infile = NULL; + opt->outfile = NULL; + + for (;;) { + i = getopt_long(argc, argv, short_opts, long_opts, NULL); + if (i == -1) + break; + + switch (i) { + case 'c': + for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) { + if (strcmp(compressors[i], optarg) == 0) { + opt->compressor = i; + break; + } + } + break; + case 'b': + opt->blksz = read_number("Block size", optarg, + 1024, 0xFFFFFFFF); + break; + case 'B': + opt->devblksz = read_number("Device block size", optarg, + 4096, 0xFFFFFFFF); + break; + case 'd': + process_defaults(opt, optarg); + break; + case 'f': + opt->outmode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case 'h': + printf(help_string, __progname, + SQFS_DEFAULT_BLOCK_SIZE, SQFS_DEVBLK_SIZE); + + fputs("Available compressors:\n", stdout); + + for (i = SQFS_COMP_MIN; i <= SQFS_COMP_MAX; ++i) + printf("\t%s\n", compressors[i]); + + exit(EXIT_SUCCESS); + case 'V': + printf(version_string, __progname, + PACKAGE_NAME, PACKAGE_VERSION); + exit(EXIT_SUCCESS); + default: + goto fail_arg; + } + } + + if ((optind + 1) >= argc) { + fputs("Missing arguments: input and output files.\n", stderr); + goto fail_arg; + } + + opt->infile = argv[optind++]; + opt->outfile = argv[optind++]; + return; +fail_arg: + fprintf(stderr, "Try `%s --help' for more information.\n", __progname); + exit(EXIT_FAILURE); +} diff --git a/mkfs/options.h b/mkfs/options.h new file mode 100644 index 0000000..dc11250 --- /dev/null +++ b/mkfs/options.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#ifndef OPTIONS_H +#define OPTIONS_H + +typedef struct { + unsigned int def_uid; + unsigned int def_gid; + unsigned int def_mode; + unsigned int def_mtime; + int outmode; + int compressor; + int blksz; + int devblksz; + const char *infile; + const char *outfile; +} options_t; + +void process_command_line(options_t *opt, int argc, char **argv); + +#endif /* OPTIONS_H */ -- cgit v1.2.3