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");