From 8d5b52dec3fd36704521347c3f94875fe6d9da2d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 26 Jan 2024 17:58:23 +0100 Subject: [PATCH] Add support for max_version Instead of specifying just the version we now specify the min and max version, and composefs tries to keep the version as low as possible but may bump it if needed for newer features. This is necessary as we now record the version in the image, and we want to avoid unnecessarily storing a later version in the image, as would change the digest of images where this is not needed. We also introduce new macros for the default max version to use in the tooling, instead of just using the max version. This allows more careful bumping of the default later. Signed-off-by: Alexander Larsson --- libcomposefs/lcfs-writer.c | 32 +++++++++++++++++++++++++++++++- libcomposefs/lcfs-writer.h | 7 ++++++- tests/test-checksums.sh | 2 +- tools/mkcomposefs.c | 37 +++++++++++++++++++++++++++---------- 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/libcomposefs/lcfs-writer.c b/libcomposefs/lcfs-writer.c index bbe57564..cb17a6b5 100644 --- a/libcomposefs/lcfs-writer.c +++ b/libcomposefs/lcfs-writer.c @@ -36,6 +36,7 @@ #include #include #include +#include static void lcfs_node_remove_all_children(struct lcfs_node_s *node); static void lcfs_node_destroy(struct lcfs_node_s *node); @@ -328,6 +329,27 @@ static int lcfs_close(struct lcfs_ctx_s *ctx) return 0; } +static void lcfs_write_update_version(struct lcfs_node_s *node, + struct lcfs_write_options_s *options) +{ + /* Version 1 changed how whiteouts are handled */ + if (options->version < 1 && options->max_version >= 1) { + int type = node->inode.st_mode & S_IFMT; + + if (type == S_IFCHR && node->inode.st_rdev == makedev(0, 0)) { + options->version = 1; + } + } + + for (size_t i = 0; i < node->children_size; ++i) { + struct lcfs_node_s *child = node->children[i]; + if (child->link_to != NULL) { + continue; + } + lcfs_write_update_version(child, options); + } +} + int lcfs_write_to(struct lcfs_node_s *root, struct lcfs_write_options_s *options) { enum lcfs_format_t format = options->format; @@ -340,11 +362,19 @@ int lcfs_write_to(struct lcfs_node_s *root, struct lcfs_write_options_s *options return -1; } - if (options->version > LCFS_VERSION_MAX) { + if (options->version > LCFS_VERSION_MAX || + options->max_version > LCFS_VERSION_MAX) { errno = EINVAL; return -1; } + if (options->max_version < options->version) { + options->max_version = options->version; + } + + /* Update options->version up to options->max_version if needed */ + lcfs_write_update_version(root, options); + ctx = lcfs_new_ctx(root, options); if (ctx == NULL) { errno = ENOMEM; diff --git a/libcomposefs/lcfs-writer.h b/libcomposefs/lcfs-writer.h index 534a4a53..d2979db2 100644 --- a/libcomposefs/lcfs-writer.h +++ b/libcomposefs/lcfs-writer.h @@ -54,6 +54,10 @@ enum lcfs_flags_t { * 1 - Mark xwhitouts using the new opaque=x format as needed by Linux 6.8 */ +/* Default value used by tooling, update with care */ +#define LCFS_DEFAULT_VERSION_MIN 0 +#define LCFS_DEFAULT_VERSION_MAX 1 + typedef ssize_t (*lcfs_read_cb)(void *file, void *buf, size_t count); typedef ssize_t (*lcfs_write_cb)(void *file, void *buf, size_t count); @@ -64,7 +68,8 @@ struct lcfs_write_options_s { uint8_t *digest_out; void *file; lcfs_write_cb file_write_cb; - uint32_t reserved[4]; + uint32_t max_version; + uint32_t reserved[3]; void *reserved2[4]; }; diff --git a/tests/test-checksums.sh b/tests/test-checksums.sh index 7c120494..6e015e04 100755 --- a/tests/test-checksums.sh +++ b/tests/test-checksums.sh @@ -23,7 +23,7 @@ for format in erofs ; do VERSION_ARG="" if test -f $ASSET_DIR/$file.version ; then VERSION="$(cat $ASSET_DIR/$file.version)" - VERSION_ARG="--format-version=$VERSION" + VERSION_ARG="--min-version=$VERSION --max-version=$VERSION" fi echo Verifying $file $VERSION_ARG diff --git a/tools/mkcomposefs.c b/tools/mkcomposefs.c index 6a999d18..6abfab36 100644 --- a/tools/mkcomposefs.c +++ b/tools/mkcomposefs.c @@ -326,8 +326,9 @@ static void usage(const char *argv0) " --print-digest Print the digest of the image\n" " --print-digest-only Print the digest of the image, don't write image\n" " --from-file The source is a dump file, not a directory\n" - " --format-version=N Use this format version (default=%d)\n", - bin, LCFS_VERSION_MAX); + " --min-version=N Use this minimal format version (default=%d)\n" + " --max-version=N Use this maxium format version (default=%d)\n", + bin, LCFS_DEFAULT_VERSION_MIN, LCFS_DEFAULT_VERSION_MAX); } #define OPT_SKIP_XATTRS 102 @@ -338,7 +339,8 @@ static void usage(const char *argv0) #define OPT_PRINT_DIGEST_ONLY 111 #define OPT_USER_XATTRS 112 #define OPT_FROM_FILE 113 -#define OPT_FORMAT_VERSION 114 +#define OPT_MIN_VERSION 114 +#define OPT_MAX_VERSION 115 static ssize_t write_cb(void *_file, void *buf, size_t count) { @@ -881,10 +883,16 @@ int main(int argc, char **argv) val: OPT_FROM_FILE }, { - name: "format-version", + name: "max-version", has_arg: required_argument, flag: NULL, - val: OPT_FORMAT_VERSION + val: OPT_MAX_VERSION + }, + { + name: "min-version", + has_arg: required_argument, + flag: NULL, + val: OPT_MIN_VERSION }, {}, }; @@ -903,7 +911,8 @@ int main(int argc, char **argv) int opt; FILE *out_file; char *failed_path; - long format_version = LCFS_VERSION_MAX; + long min_version = LCFS_DEFAULT_VERSION_MIN; + long max_version = LCFS_DEFAULT_VERSION_MAX; char *end; /* We always compute the digest and reference by digest */ @@ -935,10 +944,17 @@ int main(int argc, char **argv) case OPT_FROM_FILE: from_file = true; break; - case OPT_FORMAT_VERSION: - format_version = strtol(optarg, &end, 10); + case OPT_MIN_VERSION: + min_version = strtol(optarg, &end, 10); + if (*optarg == 0 || *end != 0) { + fprintf(stderr, "Invalid min version %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case OPT_MAX_VERSION: + max_version = strtol(optarg, &end, 10); if (*optarg == 0 || *end != 0) { - fprintf(stderr, "Invalid format version %s\n", optarg); + fprintf(stderr, "Invalid max version %s\n", optarg); exit(EXIT_FAILURE); } break; @@ -1035,7 +1051,8 @@ int main(int argc, char **argv) options.digest_out = digest; options.format = LCFS_FORMAT_EROFS; - options.version = (int)format_version; + options.version = (int)min_version; + options.max_version = (int)max_version; if (lcfs_write_to(root, &options) < 0) err(EXIT_FAILURE, "cannot write file");