From 74a45c7bc13664935e2e6a0a39c9f2727a8f33a2 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Mon, 22 Jan 2024 12:19:00 +0100 Subject: [PATCH] Add new version 1 that uses the new xwhiteout marking approach Linux upstream is making some change to how xwhiteout directories are marked. They are now marked with an opaque xattr with content "x". See: https://lore.kernel.org/linux-unionfs/CAOQ4uxh5x_-1j8HViCutVkghA1Uh-va+kJshCuvB+ep7WjmOFg@mail.gmail.com/T/#t This creates a new image format version, 1, which adds these xattrs (and the old ones for backwards compat with linux 6.7). In addition, the tests are updated to handle this, and we create a new testcase that validates both v0 and v1 keeps working for whiteout files. Note: This changes the output of the CLI tools to use the new version by default, and you need to pass --verison-format=0 to get the old version. This is ok at this point, as most images are unaffected by the version due to lack of whiteouts, and not many people use composefs yet. --- libcomposefs/lcfs-internal.h | 2 ++ libcomposefs/lcfs-writer-erofs.c | 27 ++++++++++++++++++++------- libcomposefs/lcfs-writer.h | 6 +++++- tests/Makefile.am | 2 +- tests/assets/special.dump.sha256 | 2 +- tests/assets/special.dump.version | 1 + tests/assets/special_v1.dump | 1 + tests/assets/special_v1.dump.sha256 | 1 + tests/assets/special_v1.dump.version | 1 + tests/dumpdir | 2 ++ 10 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 tests/assets/special.dump.version create mode 120000 tests/assets/special_v1.dump create mode 100644 tests/assets/special_v1.dump.sha256 create mode 100644 tests/assets/special_v1.dump.version diff --git a/libcomposefs/lcfs-internal.h b/libcomposefs/lcfs-internal.h index 5c3d566a..9f3957e6 100644 --- a/libcomposefs/lcfs-internal.h +++ b/libcomposefs/lcfs-internal.h @@ -58,11 +58,13 @@ typedef int errint_t; #define OVERLAY_XATTR_ESCAPED_WHITEOUT OVERLAY_XATTR_ESCAPE_PREFIX "whiteout" #define OVERLAY_XATTR_ESCAPED_WHITEOUTS OVERLAY_XATTR_ESCAPE_PREFIX "whiteouts" +#define OVERLAY_XATTR_ESCAPED_OPAQUE OVERLAY_XATTR_ESCAPE_PREFIX "opaque" #define OVERLAY_XATTR_USERXATTR_WHITEOUT \ OVERLAY_XATTR_USERXATTR_PREFIX "whiteout" #define OVERLAY_XATTR_USERXATTR_WHITEOUTS \ OVERLAY_XATTR_USERXATTR_PREFIX "whiteouts" +#define OVERLAY_XATTR_USERXATTR_OPAQUE OVERLAY_XATTR_USERXATTR_PREFIX "opaque" #define ALIGN_TO(_offset, _align_size) \ (((_offset) + _align_size - 1) & ~(_align_size - 1)) diff --git a/libcomposefs/lcfs-writer-erofs.c b/libcomposefs/lcfs-writer-erofs.c index 52375b37..d24984c5 100644 --- a/libcomposefs/lcfs-writer-erofs.c +++ b/libcomposefs/lcfs-writer-erofs.c @@ -1067,7 +1067,7 @@ static int write_erofs_shared_xattrs(struct lcfs_ctx_s *ctx) return 0; } -static int add_overlayfs_xattrs(struct lcfs_node_s *node) +static int add_overlayfs_xattrs(struct lcfs_ctx_s *ctx, struct lcfs_node_s *node) { int type = node->inode.st_mode & S_IFMT; int ret; @@ -1148,6 +1148,18 @@ static int add_overlayfs_xattrs(struct lcfs_node_s *node) "", 0); if (ret < 0) return ret; + + /* Mark dir containing whiteouts with new format as of version 1 */ + if (ctx->options->version >= 1) { + ret = lcfs_node_set_xattr( + parent, OVERLAY_XATTR_ESCAPED_OPAQUE, "x", 1); + if (ret < 0) + return ret; + ret = lcfs_node_set_xattr( + parent, OVERLAY_XATTR_USERXATTR_OPAQUE, "x", 1); + if (ret < 0) + return ret; + } } return 0; @@ -1206,12 +1218,13 @@ static int add_overlay_whiteouts(struct lcfs_node_s *root) return 0; } -static int rewrite_tree_node_for_erofs(struct lcfs_node_s *node, +static int rewrite_tree_node_for_erofs(struct lcfs_ctx_s *ctx, + struct lcfs_node_s *node, struct lcfs_node_s *parent) { int ret; - ret = add_overlayfs_xattrs(node); + ret = add_overlayfs_xattrs(ctx, node); if (ret < 0) return ret; @@ -1256,7 +1269,7 @@ static int rewrite_tree_node_for_erofs(struct lcfs_node_s *node, continue; } - ret = rewrite_tree_node_for_erofs(child, node); + ret = rewrite_tree_node_for_erofs(ctx, child, node); if (ret < 0) { return -1; } @@ -1277,11 +1290,11 @@ static int set_overlay_opaque(struct lcfs_node_s *node) return 0; } -static int rewrite_tree_for_erofs(struct lcfs_node_s *root) +static int rewrite_tree_for_erofs(struct lcfs_ctx_s *ctx, struct lcfs_node_s *root) { int res; - res = rewrite_tree_node_for_erofs(root, root); + res = rewrite_tree_node_for_erofs(ctx, root, root); if (res < 0) return res; @@ -1320,7 +1333,7 @@ int lcfs_write_erofs_to(struct lcfs_ctx_s *ctx) root = ctx->root; /* After we cloned it */ /* Rewrite cloned tree as needed for erofs */ - ret = rewrite_tree_for_erofs(root); + ret = rewrite_tree_for_erofs(ctx, root); if (ret < 0) return ret; diff --git a/libcomposefs/lcfs-writer.h b/libcomposefs/lcfs-writer.h index df7f261c..0e585710 100644 --- a/libcomposefs/lcfs-writer.h +++ b/libcomposefs/lcfs-writer.h @@ -48,7 +48,11 @@ enum lcfs_flags_t { LCFS_FLAGS_MASK = 0, }; -#define LCFS_VERSION_MAX 0 +#define LCFS_VERSION_MAX 1 +/* Version history: + * 0 - Initial version + * 1 - Mark xwhitouts using the new opaque=x format as needed by Linux 6.8 + */ 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); diff --git a/tests/Makefile.am b/tests/Makefile.am index f82c9b56..3d1b6222 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ TEST_ASSETS_SMALL = \ - config.dump.gz config-with-hard-link.dump.gz special.dump + config.dump.gz config-with-hard-link.dump.gz special.dump special_v1.dump TEST_ASSETS = ${TEST_ASSETS_SMALL} \ cs9-x86_64-developer.dump.gz cs9-x86_64-minimal.dump.gz \ diff --git a/tests/assets/special.dump.sha256 b/tests/assets/special.dump.sha256 index ff898dc7..9776deb6 100644 --- a/tests/assets/special.dump.sha256 +++ b/tests/assets/special.dump.sha256 @@ -1 +1 @@ -d344782ff962ab7d1c5533755c19c5bf3c3485f55bfaf228fc89524c7e2ba61e \ No newline at end of file +d344782ff962ab7d1c5533755c19c5bf3c3485f55bfaf228fc89524c7e2ba61e diff --git a/tests/assets/special.dump.version b/tests/assets/special.dump.version new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/tests/assets/special.dump.version @@ -0,0 +1 @@ +0 diff --git a/tests/assets/special_v1.dump b/tests/assets/special_v1.dump new file mode 120000 index 00000000..9c50b02f --- /dev/null +++ b/tests/assets/special_v1.dump @@ -0,0 +1 @@ +special.dump \ No newline at end of file diff --git a/tests/assets/special_v1.dump.sha256 b/tests/assets/special_v1.dump.sha256 new file mode 100644 index 00000000..eeb1dd9a --- /dev/null +++ b/tests/assets/special_v1.dump.sha256 @@ -0,0 +1 @@ +ee8028939ca3151a50bbbbd377c52c46ab183013610c8909e624840bf496975f diff --git a/tests/assets/special_v1.dump.version b/tests/assets/special_v1.dump.version new file mode 100644 index 00000000..d00491fd --- /dev/null +++ b/tests/assets/special_v1.dump.version @@ -0,0 +1 @@ +1 diff --git a/tests/dumpdir b/tests/dumpdir index 3e2d4bd7..a60a2970 100755 --- a/tests/dumpdir +++ b/tests/dumpdir @@ -37,6 +37,8 @@ def dumpfile(file, root): if stat.S_ISDIR(st_mode) and has_whiteout_child(file): xattrs["trusted.overlay.overlay.whiteouts"] = b'' xattrs["user.overlay.whiteouts"] = b'' + xattrs["trusted.overlay.overlay.opaque"] = b'x' + xattrs["user.overlay.opaque"] = b'x' nlink = s.st_nlink; if args.no_nlink or (args.no_root_nlink and file == root):