diff --git a/libcomposefs/lcfs-internal.h b/libcomposefs/lcfs-internal.h index c5da1e14..7d3ebf79 100644 --- a/libcomposefs/lcfs-internal.h +++ b/libcomposefs/lcfs-internal.h @@ -148,6 +148,9 @@ char *maybe_join_path(const char *a, const char *b); struct lcfs_node_s *follow_links(struct lcfs_node_s *node); int node_get_dtype(struct lcfs_node_s *node); +int lcfs_node_rename_xattr(struct lcfs_node_s *node, size_t index, + const char *new_name); + /* lcfs-writer-erofs.c */ int lcfs_write_erofs_to(struct lcfs_ctx_s *ctx); diff --git a/libcomposefs/lcfs-writer-erofs.c b/libcomposefs/lcfs-writer-erofs.c index c4b7ae65..ff1a2322 100644 --- a/libcomposefs/lcfs-writer-erofs.c +++ b/libcomposefs/lcfs-writer-erofs.c @@ -19,6 +19,7 @@ #include "config.h" #include "lcfs-internal.h" +#include "lcfs-utils.h" #include "lcfs-writer.h" #include "lcfs-fsverity.h" #include "lcfs-erofs.h" @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -863,11 +865,44 @@ static int write_erofs_shared_xattrs(struct lcfs_ctx_s *ctx) return 0; } +static char *str_join(const char *a, const char *b) +{ + size_t a_len = strlen(a); + size_t b_len = strlen(b); + char *res = malloc(a_len + b_len + 1); + if (res) { + memcpy(res, a, a_len); + memcpy(res + a_len, b, b_len + 1); + } + return res; +} + static int add_overlayfs_xattrs(struct lcfs_node_s *node) { + int type = node->inode.st_mode & S_IFMT; int ret; - if ((node->inode.st_mode & S_IFMT) == S_IFREG && node->inode.st_size > 0) { + /* First escape all existing "trusted.overlay.*" xattrs */ + for (size_t i = 0; i < lcfs_node_get_n_xattr(node); i++) { + const char *name = lcfs_node_get_xattr_name(node, i); + + if (str_has_prefix(name, "trusted.overlay.")) { + cleanup_free char *renamed = + str_join("trusted.overlay.overlay.", + name + strlen("trusted.overlay.")); + if (renamed == NULL) { + errno = ENOMEM; + return -1; + } + /* We rename in-place, this is safe from + collisions because we also rename any + colliding xattr */ + if (lcfs_node_rename_xattr(node, i, renamed) < 0) + return -1; + } + } + + if (type == S_IFREG && node->inode.st_size > 0) { uint8_t xattr_data[4 + LCFS_DIGEST_SIZE]; size_t xattr_len = 0; @@ -899,6 +934,24 @@ static int add_overlayfs_xattrs(struct lcfs_node_s *node) } } + /* escape whiteouts */ + if (type == S_IFCHR && node->inode.st_rdev == makedev(0, 0)) { + struct lcfs_node_s *parent = lcfs_node_get_parent(node); + + lcfs_node_set_mode(node, + S_IFREG | (lcfs_node_get_mode(node) & ~S_IFMT)); + ret = lcfs_node_set_xattr(node, "trusted.overlay.overlay.whiteout", + "", 0); + if (ret < 0) + return ret; + + /* Mark parent dir containing whiteouts */ + ret = lcfs_node_set_xattr( + parent, "trusted.overlay.overlay.whiteouts", "", 0); + if (ret < 0) + return ret; + } + return 0; } diff --git a/libcomposefs/lcfs-writer.c b/libcomposefs/lcfs-writer.c index 5842a4b5..7e00b8e3 100644 --- a/libcomposefs/lcfs-writer.c +++ b/libcomposefs/lcfs-writer.c @@ -1201,3 +1201,27 @@ int lcfs_node_set_xattr(struct lcfs_node_s *node, const char *name, return 0; } + +/* This is an internal function. + * Be careful to not cause duplicates if new_name already exist */ +int lcfs_node_rename_xattr(struct lcfs_node_s *node, size_t index, const char *new_name) +{ + struct lcfs_xattr_s *xattr; + cleanup_free char *dup = NULL; + + dup = strdup(new_name); + if (dup == NULL) { + errno = ENOMEM; + return -1; + } + + if (index >= node->n_xattrs) { + errno = EINVAL; + return -1; + } + + xattr = &node->xattrs[index]; + free(xattr->key); + xattr->key = steal_pointer(&dup); + return 0; +}