[RFC PATCH 4/5] erofs-utils: lib: implement proto manifest subset
adigitX
adityakammati.workspace at gmail.com
Sun Mar 22 02:28:31 AEDT 2026
---
lib/manifest.c | 388 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 362 insertions(+), 26 deletions(-)
diff --git a/lib/manifest.c b/lib/manifest.c
index ed37f94..a376a98 100644
--- a/lib/manifest.c
+++ b/lib/manifest.c
@@ -8,6 +8,10 @@
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <config.h>
+#if defined(HAVE_SYS_SYSMACROS_H)
+#include <sys/sysmacros.h>
+#endif
#include "erofs/diskbuf.h"
#include "erofs/hashmap.h"
#include "erofs/inode.h"
@@ -16,6 +20,7 @@
#include "liberofs_rebuild.h"
#define EROFS_MANIFEST_COMPOSEFS_NR_FIELDS 11
+#define EROFS_MANIFEST_PROTO_HEADER "%erofs.proto.v1"
struct erofs_manifest_path {
struct hashmap_entry ent;
@@ -236,7 +241,12 @@ static int erofs_manifest_next_field(char **cursor, char **field)
char *start, *end;
start = *cursor;
- if (!start || !*start)
+ if (!start)
+ return -EINVAL;
+
+ while (*start == ' ')
+ ++start;
+ if (!*start)
return -EINVAL;
end = strchr(start, ' ');
@@ -255,6 +265,18 @@ static int erofs_manifest_next_field(char **cursor, char **field)
return 0;
}
+static int erofs_manifest_stat_regular(const char *path, erofs_off_t *size)
+{
+ struct stat st;
+
+ if (stat(path, &st))
+ return -errno;
+ if (!S_ISREG(st.st_mode))
+ return -EINVAL;
+ *size = st.st_size;
+ return 0;
+}
+
static int erofs_manifest_apply_inode(struct erofs_manifest_ctx *ctx,
struct erofs_inode *inode,
const char *path,
@@ -399,7 +421,8 @@ static int erofs_manifest_prepare_dentry(struct erofs_manifest_ctx *ctx,
if (!S_ISDIR(mode))
return -EINVAL;
*out_inode = ctx->root;
- *out_dentry = NULL;
+ if (out_dentry)
+ *out_dentry = NULL;
*out_root = true;
return 0;
}
@@ -435,7 +458,8 @@ static int erofs_manifest_prepare_dentry(struct erofs_manifest_ctx *ctx,
return err;
*out_inode = inode;
- *out_dentry = d;
+ if (out_dentry)
+ *out_dentry = d;
return 0;
}
@@ -666,33 +690,357 @@ out:
}
static int erofs_manifest_load_composefs(struct erofs_manifest_ctx *ctx,
- const char *source)
+ FILE *fp, char **line, size_t *cap,
+ unsigned int lineno, bool has_line)
+{
+ int err;
+
+ if (has_line && (*line)[0]) {
+ err = erofs_manifest_apply_composefs_line(ctx, *line, lineno);
+ if (err)
+ return err;
+ }
+
+ while (getline(line, cap, fp) >= 0) {
+ ++lineno;
+ erofs_manifest_trim_newline(*line);
+ if (!(*line)[0])
+ continue;
+
+ err = erofs_manifest_apply_composefs_line(ctx, *line, lineno);
+ if (err)
+ return err;
+ }
+ return ferror(fp) ? -EIO : 0;
+}
+
+static int erofs_manifest_apply_proto_hardlink(struct erofs_manifest_ctx *ctx,
+ const char *path,
+ const char *target_path,
+ umode_t mode)
+{
+ struct erofs_dentry *dentry = NULL, *target = NULL;
+ struct erofs_inode *inode, *target_inode;
+ bool is_root;
+ int err;
+
+ err = erofs_manifest_prepare_dentry(ctx, path, mode, &dentry, &inode,
+ &is_root);
+ if (err)
+ return err;
+ if (is_root)
+ return -EINVAL;
+
+ err = erofs_manifest_lookup_dentry(ctx, target_path, &target);
+ if (err)
+ return err;
+ if (!target || target->type == EROFS_FT_UNKNOWN)
+ return -ENOENT;
+
+ target_inode = target->inode;
+ if (S_ISDIR(target_inode->i_mode))
+ return -EISDIR;
+
+ erofs_iput(inode);
+ dentry->inode = erofs_igrab(target_inode);
+ dentry->type = erofs_mode_to_ftype(target_inode->i_mode);
+ ++target_inode->i_nlink;
+ return 0;
+}
+
+static int erofs_manifest_apply_proto_line(struct erofs_manifest_ctx *ctx,
+ char *line, unsigned int lineno)
+{
+ char *cursor = line, *field, *path = NULL, *payload = NULL;
+ struct erofs_inode *inode;
+ umode_t mode;
+ erofs_off_t size = 0;
+ u64 mode_raw, uid, gid, major_id, minor_id;
+ u64 mtime = ctx->root->i_mtime;
+ u32 mtime_nsec = ctx->root->i_mtime_nsec;
+ dev_t rdev = 0;
+ char kind;
+ bool is_root;
+ int err;
+
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err) {
+ erofs_err("manifest:%u: missing proto path", lineno);
+ return err;
+ }
+ err = erofs_manifest_unescape(field, false, &path);
+ if (err) {
+ erofs_err("manifest:%u: invalid proto path", lineno);
+ goto out;
+ }
+ if (path[0] != '/') {
+ err = -EINVAL;
+ erofs_err("manifest:%u: proto path must be absolute: %s",
+ lineno, path);
+ goto out;
+ }
+
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || strlen(field) != 1) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto type", lineno);
+ goto out;
+ }
+ kind = field[0];
+
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_parse_uint(field, 8, &mode_raw)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto mode", lineno);
+ goto out;
+ }
+
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_parse_uint(field, 10, &uid)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto uid", lineno);
+ goto out;
+ }
+
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_parse_uint(field, 10, &gid)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto gid", lineno);
+ goto out;
+ }
+
+ switch (kind) {
+ case 'd':
+ mode = S_IFDIR | (mode_raw & 07777);
+ break;
+ case 'f':
+ mode = S_IFREG | (mode_raw & 07777);
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_unescape(field, false, &payload)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto file payload", lineno);
+ goto out;
+ }
+ err = erofs_manifest_stat_regular(payload, &size);
+ if (err)
+ goto out;
+ break;
+ case 'l':
+ mode = S_IFLNK | (mode_raw & 07777);
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_unescape(field, false, &payload)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto symlink target",
+ lineno);
+ goto out;
+ }
+ size = strlen(payload);
+ break;
+ case 'L':
+ mode = S_IFREG | (mode_raw & 07777);
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_unescape(field, false, &payload)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto hardlink target",
+ lineno);
+ goto out;
+ }
+ if (payload[0] != '/') {
+ err = -EINVAL;
+ erofs_err("manifest:%u: hardlink target must be absolute",
+ lineno);
+ goto out;
+ }
+ break;
+ case 'p':
+ mode = S_IFIFO | (mode_raw & 07777);
+ break;
+ case 'c':
+ case 'b':
+ mode = (kind == 'c' ? S_IFCHR : S_IFBLK) | (mode_raw & 07777);
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_parse_uint(field, 10, &major_id)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto device major", lineno);
+ goto out;
+ }
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err || erofs_manifest_parse_uint(field, 10, &minor_id)) {
+ err = -EINVAL;
+ erofs_err("manifest:%u: invalid proto device minor", lineno);
+ goto out;
+ }
+ rdev = makedev(major_id, minor_id);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ erofs_err("manifest:%u: unsupported proto type %c", lineno, kind);
+ goto out;
+ }
+
+ while (cursor && *cursor) {
+ err = erofs_manifest_next_field(&cursor, &field);
+ if (err)
+ goto out;
+ if (!strncmp(field, "mtime:", 6)) {
+ err = erofs_manifest_parse_mtime(field + 6, &mtime,
+ &mtime_nsec);
+ if (err) {
+ erofs_err("manifest:%u: invalid proto mtime",
+ lineno);
+ goto out;
+ }
+ continue;
+ }
+
+ err = -EINVAL;
+ erofs_err("manifest:%u: unsupported proto attribute %s",
+ lineno, field);
+ goto out;
+ }
+
+ if (kind == 'L') {
+ err = erofs_manifest_apply_proto_hardlink(ctx, path, payload,
+ mode);
+ goto out;
+ }
+
+ err = erofs_manifest_prepare_dentry(ctx, path, mode, NULL, &inode,
+ &is_root);
+ if (err)
+ goto out;
+
+ switch (mode & S_IFMT) {
+ case S_IFDIR:
+ case S_IFREG:
+ case S_IFLNK:
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = erofs_manifest_apply_inode(ctx, inode, path, mode, uid, gid,
+ mtime, mtime_nsec, rdev, size);
+ if (err)
+ goto out;
+
+ if (S_ISREG(mode) && payload) {
+ err = erofs_manifest_stage_file(inode, payload);
+ } else if (S_ISLNK(mode)) {
+ inode->i_link = strdup(payload);
+ if (!inode->i_link)
+ err = -ENOMEM;
+ }
+out:
+ free(path);
+ free(payload);
+ return err;
+}
+
+static int erofs_manifest_load_proto(struct erofs_manifest_ctx *ctx, FILE *fp,
+ char **line, size_t *cap,
+ unsigned int lineno, bool has_line)
+{
+ bool seen_header = false;
+ int err;
+
+ if (has_line && (*line)[0]) {
+ if (strcmp(*line, EROFS_MANIFEST_PROTO_HEADER)) {
+ erofs_err("manifest:%u: expected %s", lineno,
+ EROFS_MANIFEST_PROTO_HEADER);
+ return -EINVAL;
+ }
+ seen_header = true;
+ }
+
+ while (getline(line, cap, fp) >= 0) {
+ ++lineno;
+ erofs_manifest_trim_newline(*line);
+ if (!(*line)[0])
+ continue;
+ if (!seen_header) {
+ if (strcmp(*line, EROFS_MANIFEST_PROTO_HEADER)) {
+ erofs_err("manifest:%u: expected %s", lineno,
+ EROFS_MANIFEST_PROTO_HEADER);
+ return -EINVAL;
+ }
+ seen_header = true;
+ continue;
+ }
+
+ if ((err = erofs_manifest_apply_proto_line(ctx, *line, lineno)))
+ return err;
+ }
+
+ if (!seen_header) {
+ erofs_err("manifest: missing %s header",
+ EROFS_MANIFEST_PROTO_HEADER);
+ return -EINVAL;
+ }
+
+ return ferror(fp) ? -EIO : 0;
+}
+
+static int erofs_manifest_load_source(struct erofs_manifest_ctx *ctx,
+ enum erofs_manifest_format format,
+ const char *source)
{
FILE *fp;
char *line = NULL;
size_t cap = 0;
unsigned int lineno = 0;
+ bool has_line = false;
int err;
err = erofs_manifest_open_source(source, &fp);
if (err)
return err;
- while (getline(&line, &cap, fp) >= 0) {
- ++lineno;
- erofs_manifest_trim_newline(line);
- if (!line[0])
- continue;
+ if (format == EROFS_MANIFEST_FORMAT_AUTO) {
+ while (getline(&line, &cap, fp) >= 0) {
+ ++lineno;
+ erofs_manifest_trim_newline(line);
+ if (!line[0])
+ continue;
+
+ format = !strcmp(line, EROFS_MANIFEST_PROTO_HEADER) ?
+ EROFS_MANIFEST_FORMAT_PROTO :
+ EROFS_MANIFEST_FORMAT_COMPOSEFS;
+ has_line = true;
+ break;
+ }
- err = erofs_manifest_apply_composefs_line(ctx, line, lineno);
- if (err)
+ if (ferror(fp)) {
+ err = -EIO;
+ goto out;
+ }
+ if (format == EROFS_MANIFEST_FORMAT_AUTO) {
+ err = 0;
goto out;
+ }
+ }
+
+ switch (format) {
+ case EROFS_MANIFEST_FORMAT_COMPOSEFS:
+ err = erofs_manifest_load_composefs(ctx, fp, &line, &cap,
+ lineno, has_line);
+ break;
+ case EROFS_MANIFEST_FORMAT_PROTO:
+ err = erofs_manifest_load_proto(ctx, fp, &line, &cap,
+ lineno, has_line);
+ break;
+ default:
+ err = -EINVAL;
+ break;
}
- err = ferror(fp) ? -EIO : 0;
out:
- free(line);
erofs_manifest_close_source(source, fp);
+ free(line);
return err;
}
@@ -707,19 +1055,7 @@ int erofs_manifest_load(struct erofs_importer *im,
return -EINVAL;
erofs_manifest_ctx_init(&ctx, im);
- switch (format) {
- case EROFS_MANIFEST_FORMAT_AUTO:
- case EROFS_MANIFEST_FORMAT_COMPOSEFS:
- err = erofs_manifest_load_composefs(&ctx, source);
- break;
- case EROFS_MANIFEST_FORMAT_PROTO:
- erofs_err("proto manifest input support is not implemented yet");
- err = -EOPNOTSUPP;
- break;
- default:
- err = -EINVAL;
- break;
- }
+ err = erofs_manifest_load_source(&ctx, format, source);
erofs_manifest_ctx_exit(&ctx);
return err;
}
--
2.51.0
More information about the Linux-erofs
mailing list