From d636beeb6f3a483b64bec5a5666e1c1ac07cf9b5 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 12 Sep 2023 14:18:58 +0200 Subject: [PATCH] lib: Add LCFS_BUILD_NO_INLINE option to build flags Unless you pass this to lcfs_load_node_from_file() then it will automatically add the file content for small files to the lcfs_nodes, which means that the file data will be stored in the erofs image instead of in a separate file in the basedir. The limit for small files is 64 bytes, which is the size of a sha256 digest in hex form. That is typically the size of a redirect xattr that would be used otherwise, so the end result is that we always get a smaller image if we inline these. 64 is also the typical size of a erofs inode entry, so this will inline well in the inode table. Signed-off-by: Alexander Larsson --- libcomposefs/lcfs-internal.h | 6 ++++ libcomposefs/lcfs-writer.c | 66 +++++++++++++++++++++++++++++++----- libcomposefs/lcfs-writer.h | 1 + tools/mkcomposefs.c | 3 +- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/libcomposefs/lcfs-internal.h b/libcomposefs/lcfs-internal.h index 7c4dc838..3e4f32fe 100644 --- a/libcomposefs/lcfs-internal.h +++ b/libcomposefs/lcfs-internal.h @@ -23,6 +23,12 @@ #include "lcfs-fsverity.h" #include "hash.h" +/* When using LCFS_BUILD_INLINE_SMALL in lcfs_load_node_from_file() inline files below this size + * We pick 64 which is the size of a sha256 digest that would otherwise be used as a redirect + * xattr, so the inlined file is smaller. + */ +#define LCFS_BUILD_INLINE_FILE_SIZE_LIMIT 64 + #define ALIGN_TO(_offset, _align_size) \ (((_offset) + _align_size - 1) & ~(_align_size - 1)) diff --git a/libcomposefs/lcfs-writer.c b/libcomposefs/lcfs-writer.c index 4d097649..5504c6db 100644 --- a/libcomposefs/lcfs-writer.c +++ b/libcomposefs/lcfs-writer.c @@ -540,6 +540,30 @@ int lcfs_node_set_fsverity_from_fd(struct lcfs_node_s *node, int fd) return lcfs_node_set_fsverity_from_content(node, &_fd, fsverity_read_cb); } +static int read_content(int fd, size_t size, uint8_t *buf) +{ + int bytes_read; + + while (size > 0) { + do + bytes_read = read(fd, buf, size); + while (bytes_read < 0 && errno == EINTR); + + if (bytes_read == 0) + break; + + size -= bytes_read; + buf += bytes_read; + } + + if (size > 0) { + errno = ENODATA; + return -1; + } + + return 0; +} + struct lcfs_node_s *lcfs_load_node_from_file(int dirfd, const char *fname, int buildflags) { @@ -548,7 +572,8 @@ struct lcfs_node_s *lcfs_load_node_from_file(int dirfd, const char *fname, int r; if (buildflags & ~(LCFS_BUILD_SKIP_XATTRS | LCFS_BUILD_USE_EPOCH | - LCFS_BUILD_SKIP_DEVICES | LCFS_BUILD_COMPUTE_DIGEST)) { + LCFS_BUILD_SKIP_DEVICES | LCFS_BUILD_COMPUTE_DIGEST | + LCFS_BUILD_NO_INLINE)) { errno = EINVAL; return NULL; } @@ -568,17 +593,42 @@ struct lcfs_node_s *lcfs_load_node_from_file(int dirfd, const char *fname, ret->inode.st_size = sb.st_size; if ((sb.st_mode & S_IFMT) == S_IFREG) { - if (sb.st_size != 0 && (buildflags & LCFS_BUILD_COMPUTE_DIGEST) != 0) { - int fd = openat(dirfd, fname, O_RDONLY | O_CLOEXEC); + bool compute_digest = (buildflags & LCFS_BUILD_COMPUTE_DIGEST) != 0; + bool no_inline = (buildflags & LCFS_BUILD_NO_INLINE) != 0; + bool is_zerosized = sb.st_size == 0; + bool do_digest = !is_zerosized && compute_digest; + bool do_inline = !is_zerosized && !no_inline && + sb.st_size <= LCFS_BUILD_INLINE_FILE_SIZE_LIMIT; + + if (do_digest || do_inline) { + cleanup_fd int fd = + openat(dirfd, fname, O_RDONLY | O_CLOEXEC); if (fd < 0) { lcfs_node_unref(ret); return NULL; } - r = lcfs_node_set_fsverity_from_fd(ret, fd); - close(fd); - if (r < 0) { - lcfs_node_unref(ret); - return NULL; + if (do_digest) { + r = lcfs_node_set_fsverity_from_fd(ret, fd); + if (r < 0) { + lcfs_node_unref(ret); + return NULL; + } + /* In case we re-read below */ + lseek(fd, 0, SEEK_SET); + } + if (do_inline) { + uint8_t buf[LCFS_BUILD_INLINE_FILE_SIZE_LIMIT]; + + r = read_content(fd, sb.st_size, buf); + if (r < 0) { + lcfs_node_unref(ret); + return NULL; + } + r = lcfs_node_set_content(ret, buf, sb.st_size); + if (r < 0) { + lcfs_node_unref(ret); + return NULL; + } } } } diff --git a/libcomposefs/lcfs-writer.h b/libcomposefs/lcfs-writer.h index 1f7339c8..8fc60b9d 100644 --- a/libcomposefs/lcfs-writer.h +++ b/libcomposefs/lcfs-writer.h @@ -34,6 +34,7 @@ enum { LCFS_BUILD_USE_EPOCH = (1 << 1), LCFS_BUILD_SKIP_DEVICES = (1 << 2), LCFS_BUILD_COMPUTE_DIGEST = (1 << 3), + LCFS_BUILD_NO_INLINE = (1 << 4), }; enum lcfs_format_t { diff --git a/tools/mkcomposefs.c b/tools/mkcomposefs.c index 7fb7487a..1e88e7ba 100644 --- a/tools/mkcomposefs.c +++ b/tools/mkcomposefs.c @@ -323,7 +323,8 @@ static int fill_payload(struct lcfs_node_s *node, const char *path, size_t len, ret = lcfs_node_set_payload(node, target); if (ret < 0) return ret; - } else if ((lcfs_node_get_mode(node) & S_IFMT) == S_IFREG) { + } else if ((lcfs_node_get_mode(node) & S_IFMT) == S_IFREG && + lcfs_node_get_content(node) == NULL) { const uint8_t *digest = NULL; if (by_digest)