Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline small files #187

Merged
merged 4 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions libcomposefs/lcfs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down Expand Up @@ -98,6 +104,8 @@ struct lcfs_node_s {
char *name;
char *payload; /* backing file or symlink target */

uint8_t *content;

struct lcfs_xattr_s *xattrs;
size_t n_xattrs;

Expand Down
1 change: 1 addition & 0 deletions libcomposefs/lcfs-utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <unistd.h>

#define max(a, b) ((a > b) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))

static inline void _lcfs_reset_errno_(int *saved_errno)
{
Expand Down
86 changes: 71 additions & 15 deletions libcomposefs/lcfs-writer-erofs.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "lcfs-writer.h"
#include "lcfs-fsverity.h"
#include "lcfs-erofs.h"
#include "lcfs-utils.h"
#include "hash.h"

#include <errno.h>
Expand Down Expand Up @@ -517,12 +518,21 @@ static void compute_erofs_inode_size(struct lcfs_node_s *node)
node->erofs_n_blocks = 0;
node->erofs_tailsize = strlen(node->payload);
} else if (type == S_IFREG && file_size > 0) {
uint32_t chunkbits = compute_erofs_chunk_bitsize(node);
uint64_t chunksize = 1ULL << chunkbits;
uint32_t chunk_count = DIV_ROUND_UP(file_size, chunksize);
if (node->content != NULL) {
node->erofs_n_blocks = file_size / EROFS_BLKSIZ;
node->erofs_tailsize = file_size % EROFS_BLKSIZ;
if (node->erofs_tailsize > EROFS_BLKSIZ / 2) {
node->erofs_n_blocks++;
node->erofs_tailsize = 0;
}
} else {
uint32_t chunkbits = compute_erofs_chunk_bitsize(node);
uint64_t chunksize = 1ULL << chunkbits;
uint32_t chunk_count = DIV_ROUND_UP(file_size, chunksize);

node->erofs_n_blocks = 0;
node->erofs_tailsize = chunk_count * sizeof(uint32_t);
node->erofs_n_blocks = 0;
node->erofs_tailsize = chunk_count * sizeof(uint32_t);
}
} else {
node->erofs_n_blocks = 0;
node->erofs_tailsize = 0;
Expand Down Expand Up @@ -823,7 +833,7 @@ static int write_erofs_inode_data(struct lcfs_ctx_s *ctx, struct lcfs_node_s *no
} else if (type == S_IFREG) {
size = node->inode.st_size;

if (size > 0) {
if (size > 0 && node->content == NULL) {
uint32_t chunkbits = compute_erofs_chunk_bitsize(node);
uint64_t chunksize = 1ULL << chunkbits;

Expand Down Expand Up @@ -889,6 +899,12 @@ static int write_erofs_inode_data(struct lcfs_ctx_s *ctx, struct lcfs_node_s *no
} else if (type == S_IFCHR || type == S_IFBLK) {
i.i_u.rdev = lcfs_u32_to_file(node->inode.st_rdev);
} else if (type == S_IFREG) {
if (node->erofs_n_blocks > 0) {
i.i_u.raw_blkaddr = lcfs_u32_to_file(
ctx_erofs->current_end / EROFS_BLKSIZ);
ctx_erofs->current_end +=
EROFS_BLKSIZ * node->erofs_n_blocks;
}
if (datalayout == EROFS_INODE_CHUNK_BASED) {
i.i_u.c.format = lcfs_u16_to_file(chunk_format);
}
Expand Down Expand Up @@ -944,11 +960,24 @@ static int write_erofs_inode_data(struct lcfs_ctx_s *ctx, struct lcfs_node_s *no
if (ret < 0)
return ret;
} else if (type == S_IFREG) {
for (size_t i = 0; i < chunk_count; i++) {
uint32_t empty_chunk = 0xFFFFFFFF;
ret = lcfs_write(ctx, &empty_chunk, sizeof(empty_chunk));
if (ret < 0)
return ret;
if (node->content != NULL) {
if (node->erofs_tailsize) {
uint64_t file_size = node->inode.st_size;
ret = lcfs_write(ctx,
node->content + file_size -
node->erofs_tailsize,
node->erofs_tailsize);
if (ret < 0)
return ret;
}
} else {
for (size_t i = 0; i < chunk_count; i++) {
uint32_t empty_chunk = 0xFFFFFFFF;
ret = lcfs_write(ctx, &empty_chunk,
sizeof(empty_chunk));
if (ret < 0)
return ret;
}
}
}

Expand Down Expand Up @@ -976,7 +1005,31 @@ static int write_erofs_inodes(struct lcfs_ctx_s *ctx)
return 0;
}

static int write_erofs_dirent_blocks(struct lcfs_ctx_s *ctx)
/* Writes the non-tailpacked file data, if any */
static int write_erofs_file_content(struct lcfs_ctx_s *ctx, struct lcfs_node_s *node)
{
int type = node->inode.st_mode & S_IFMT;
off_t size = node->inode.st_size;

if (type != S_IFREG || node->erofs_n_blocks == 0)
return 0;

assert(node->content != NULL);

for (size_t i = 0; i < node->erofs_n_blocks; i++) {
off_t offset = i * EROFS_BLKSIZ;
off_t len = min(size - offset, EROFS_BLKSIZ);
int ret;

ret = lcfs_write(ctx, node->content + offset, len);
if (ret < 0)
return ret;
}

return lcfs_write_align(ctx, EROFS_BLKSIZ);
}

static int write_erofs_data_blocks(struct lcfs_ctx_s *ctx)
{
struct lcfs_node_s *node;
int ret;
Expand All @@ -985,6 +1038,9 @@ static int write_erofs_dirent_blocks(struct lcfs_ctx_s *ctx)
ret = write_erofs_dentries(ctx, node, true, false);
if (ret < 0)
return ret;
ret = write_erofs_file_content(ctx, node);
if (ret < 0)
return ret;
}

return 0;
Expand Down Expand Up @@ -1042,7 +1098,7 @@ static int add_overlayfs_xattrs(struct lcfs_node_s *node)
}
}

if (type == S_IFREG && node->inode.st_size > 0) {
if (type == S_IFREG && node->inode.st_size > 0 && node->content == NULL) {
uint8_t xattr_data[4 + LCFS_DIGEST_SIZE];
size_t xattr_len = 0;

Expand All @@ -1060,7 +1116,7 @@ static int add_overlayfs_xattrs(struct lcfs_node_s *node)
if (ret < 0)
return ret;

if (strlen(node->payload) > 0) {
if (node->payload && strlen(node->payload) > 0) {
char *path = maybe_join_path("/", node->payload);
if (path == NULL) {
errno = ENOMEM;
Expand Down Expand Up @@ -1351,7 +1407,7 @@ int lcfs_write_erofs_to(struct lcfs_ctx_s *ctx)

assert(data_block_start == (uint64_t)ctx->bytes_written);

ret = write_erofs_dirent_blocks(ctx);
ret = write_erofs_data_blocks(ctx);
if (ret < 0)
return ret;

Expand Down
105 changes: 97 additions & 8 deletions libcomposefs/lcfs-writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
alexlarsson marked this conversation as resolved.
Show resolved Hide resolved
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)
{
Expand All @@ -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;
}
Expand All @@ -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;
}
}
}
}
Expand Down Expand Up @@ -627,6 +677,31 @@ void lcfs_node_set_fsverity_digest(struct lcfs_node_s *node,
memcpy(node->digest, digest, LCFS_DIGEST_SIZE);
}

int lcfs_node_set_content(struct lcfs_node_s *node, const uint8_t *data,
size_t data_size)
{
uint8_t *dup = NULL;

if (data && data_size != 0) {
dup = malloc(data_size);
if (dup == NULL) {
errno = ENOMEM;
return -1;
}
memcpy(dup, data, data_size);
}
free(node->content);
node->content = dup;
node->inode.st_size = data_size;

return 0;
}

const uint8_t *lcfs_node_get_content(struct lcfs_node_s *node)
{
return node->content;
}

const char *lcfs_node_get_name(struct lcfs_node_s *node)
{
return node->name;
Expand Down Expand Up @@ -699,8 +774,14 @@ uint64_t lcfs_node_get_size(struct lcfs_node_s *node)
return node->inode.st_size;
}

/* Clears content if size changes */
void lcfs_node_set_size(struct lcfs_node_s *node, uint64_t size)
{
if (size == node->inode.st_size)
return;

free(node->content);
node->content = NULL;
node->inode.st_size = size;
}

Expand Down Expand Up @@ -824,6 +905,7 @@ void lcfs_node_unref(struct lcfs_node_s *node)

free(node->name);
free(node->payload);
free(node->content);

for (i = 0; i < node->n_xattrs; i++) {
free(node->xattrs[i].key);
Expand Down Expand Up @@ -874,6 +956,13 @@ struct lcfs_node_s *lcfs_node_clone(struct lcfs_node_s *node)
goto fail;
}

if (node->content) {
new->content = malloc(node->inode.st_size);
if (new->content == NULL)
goto fail;
memcpy(new->content, node->content, node->inode.st_size);
}

if (node->n_xattrs > 0) {
new->xattrs = malloc(sizeof(struct lcfs_xattr_s) * node->n_xattrs);
if (new->xattrs == NULL)
Expand Down
5 changes: 5 additions & 0 deletions libcomposefs/lcfs-writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -78,6 +79,10 @@ LCFS_EXTERN const char *lcfs_node_get_xattr_name(struct lcfs_node_s *node,

LCFS_EXTERN int lcfs_node_set_payload(struct lcfs_node_s *node, const char *payload);

LCFS_EXTERN int lcfs_node_set_content(struct lcfs_node_s *node,
const uint8_t *data, size_t data_size);
LCFS_EXTERN const uint8_t *lcfs_node_get_content(struct lcfs_node_s *node);

LCFS_EXTERN struct lcfs_node_s *lcfs_node_lookup_child(struct lcfs_node_s *node,
const char *name);
LCFS_EXTERN struct lcfs_node_s *lcfs_node_get_parent(struct lcfs_node_s *node);
Expand Down
Loading
Loading