Skip to content

Commit

Permalink
lib: Add support for inlining small data files
Browse files Browse the repository at this point in the history
For small files, it makes no sense to use a redirect to the basedir.
The size of the redirect xattr value itself is typically 65 bytes, and
on top of that comes the xattr name overhead. Things are even worse if
we also want to store the fs-verity digest. The overhead for a very
small file is significant.

So, we add the ability to store the file content to save in a
lcfs_node. And when serializing the erofs layer, if it is set we
skip the redirect xattrs, and just directly store the data in the
erofs file (instead of making it a sparse file).

Signed-off-by: Alexander Larsson <[email protected]>
  • Loading branch information
alexlarsson committed Sep 13, 2023
1 parent 03ac5fc commit 7412067
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 15 deletions.
2 changes: 2 additions & 0 deletions libcomposefs/lcfs-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,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
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
39 changes: 39 additions & 0 deletions libcomposefs/lcfs-writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,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 +724,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 +855,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 +906,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
4 changes: 4 additions & 0 deletions libcomposefs/lcfs-writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,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

0 comments on commit 7412067

Please sign in to comment.