[PATCH v7] erofs-utils: mount: add OCI recovery support for NBD reattach
ChengyuZhu6
hudson at cyzhu.com
Wed Sep 10 16:13:02 AEST 2025
From: Chengyu Zhu <hudsonzhu at tencent.com>
This commit implements recovery support for OCI-based NBD mounts,
allowing the system to properly reattach NBD devices after
NBD disconnection.
Signed-off-by: Chengyu Zhu <hudsonzhu at tencent.com>
---
lib/liberofs_oci.h | 3 +
lib/remotes/oci.c | 71 ++++++++++++++++++++++
mount/main.c | 143 ++++++++++++++++++++++++++++++++++++---------
3 files changed, 189 insertions(+), 28 deletions(-)
diff --git a/lib/liberofs_oci.h b/lib/liberofs_oci.h
index 01a83aa..aa41141 100644
--- a/lib/liberofs_oci.h
+++ b/lib/liberofs_oci.h
@@ -71,6 +71,9 @@ int ocierofs_build_trees(struct erofs_importer *importer,
const struct ocierofs_config *cfg);
int ocierofs_io_open(struct erofs_vfile *vf, const struct ocierofs_config *cfg);
+char *ocierofs_encode_userpass(const char *username, const char *password);
+int ocierofs_decode_userpass(const char *b64, char **out_user, char **out_pass);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/remotes/oci.c b/lib/remotes/oci.c
index de18daa..a00c04a 100644
--- a/lib/remotes/oci.c
+++ b/lib/remotes/oci.c
@@ -24,6 +24,7 @@
#include "erofs/io.h"
#include "erofs/print.h"
#include "erofs/tar.h"
+#include "liberofs_base64.h"
#include "liberofs_oci.h"
#include "liberofs_private.h"
@@ -1471,6 +1472,76 @@ int ocierofs_io_open(struct erofs_vfile *vfile, const struct ocierofs_config *cf
*(struct ocierofs_iostream **)vfile->payload = oci_iostream;
return 0;
}
+
+char *ocierofs_encode_userpass(const char *username, const char *password)
+{
+ char *buf;
+ char *out;
+ int ret;
+ size_t outlen;
+ size_t inlen;
+
+ if (asprintf(&buf, "%s:%s", username ?: "", password ?: "") == -1)
+ return ERR_PTR(-ENOMEM);
+
+ inlen = strlen(buf);
+ outlen = 4 * DIV_ROUND_UP(inlen, 3);
+ out = malloc(outlen + 1);
+ if (!out) {
+ free(buf);
+ return ERR_PTR(-ENOMEM);
+ }
+ ret = erofs_base64_encode((unsigned char *)buf, inlen, out);
+ if (ret < 0) {
+ free(buf);
+ free(out);
+ return ERR_PTR(ret);
+ }
+ out[ret] = '\0';
+ free(buf);
+ return out;
+}
+
+int ocierofs_decode_userpass(const char *b64, char **out_user, char **out_pass)
+{
+ size_t len;
+ unsigned char *out;
+ int ret;
+ char *colon;
+
+ if (!b64 || !out_user || !out_pass)
+ return -EINVAL;
+ *out_user = NULL;
+ *out_pass = NULL;
+
+ len = strlen(b64);
+ out = malloc(len * 3 / 4 + 1);
+ if (!out)
+ return -ENOMEM;
+ ret = erofs_base64_decode(b64, len, out);
+ if (ret < 0) {
+ free(out);
+ return ret;
+ }
+ out[ret] = '\0';
+ colon = (char *)memchr(out, ':', ret);
+ if (!colon) {
+ free(out);
+ return -EINVAL;
+ }
+ *colon = '\0';
+ *out_user = strdup((char *)out);
+ *out_pass = strdup(colon + 1);
+ free(out);
+ if (!*out_user || !*out_pass) {
+ free(*out_user);
+ free(*out_pass);
+ *out_user = *out_pass = NULL;
+ return -ENOMEM;
+ }
+ return 0;
+}
+
#else
int ocierofs_io_open(struct erofs_vfile *vfile, const struct ocierofs_config *cfg)
{
diff --git a/mount/main.c b/mount/main.c
index c52ac3b..a759689 100644
--- a/mount/main.c
+++ b/mount/main.c
@@ -401,10 +401,45 @@ out_closefd:
return err;
}
-static char *erofsmount_write_recovery_info(const char *source)
+static int erofsmount_write_recovery_oci(FILE *f, struct erofs_nbd_source *source)
+{
+ char *b64cred = NULL;
+ int ret;
+
+ if (source->ocicfg.username || source->ocicfg.password) {
+ b64cred = ocierofs_encode_userpass(
+ source->ocicfg.username,
+ source->ocicfg.password);
+ if (IS_ERR(b64cred))
+ return PTR_ERR(b64cred);
+ }
+ ret = fprintf(f, "OCI_LAYER %s %s %d %s\n",
+ source->ocicfg.image_ref ?: "",
+ source->ocicfg.platform ?: "",
+ source->ocicfg.layer_index,
+ b64cred ?: "");
+ free(b64cred);
+ return ret < 0 ? -ENOMEM : 0;
+}
+
+static int erofsmount_write_recovery_local(FILE *f, struct erofs_nbd_source *source)
{
- char recp[] = "/var/run/erofs/mountnbd_XXXXXX";
char *realp;
+ int err;
+
+ realp = realpath(source->device_path, NULL);
+ if (!realp)
+ return -errno;
+
+ /* TYPE<LOCAL> <SOURCE PATH>\n(more..) */
+ err = fprintf(f, "LOCAL %s\n", realp) < 0;
+ free(realp);
+ return err ? -ENOMEM : 0;
+}
+
+static char *erofsmount_write_recovery_info(struct erofs_nbd_source *source)
+{
+ char recp[] = "/var/run/erofs/mountnbd_XXXXXX";
int fd, err;
FILE *f;
@@ -424,20 +459,67 @@ static char *erofsmount_write_recovery_info(const char *source)
return ERR_PTR(-errno);
}
- realp = realpath(source, NULL);
- if (!realp) {
- fclose(f);
- return ERR_PTR(-errno);
+ if (source->type == EROFSNBD_SOURCE_OCI) {
+ err = erofsmount_write_recovery_oci(f, source);
+ if (err) {
+ fclose(f);
+ return ERR_PTR(err);
+ }
+ } else {
+ err = erofsmount_write_recovery_local(f, source);
+ if (err) {
+ fclose(f);
+ return ERR_PTR(err);
+ }
}
- /* TYPE<LOCAL> <SOURCE PATH>\n(more..) */
- err = fprintf(f, "LOCAL %s\n", realp) < 0;
+
fclose(f);
- free(realp);
- if (err)
- return ERR_PTR(-ENOMEM);
return strdup(recp) ?: ERR_PTR(-ENOMEM);
}
+/**
+ * Parses input string in format: "image_ref platform layer [b64cred]"
+ */
+static int erofsmount_parse_recovery_ocilayer(struct ocierofs_config *oci_cfg,
+ char *source)
+{
+ char *tokens[4] = {0};
+ int token_count = 0;
+ char *p = source;
+ int err;
+ char *endptr;
+ unsigned long v;
+
+ while (token_count < 4 && (p = strchr(p, ' ')) != NULL) {
+ *p++ = '\0';
+ while (*p == ' ')
+ p++;
+ if (*p == '\0')
+ break;
+ tokens[token_count++] = p;
+ }
+
+ if (token_count < 2)
+ return -EINVAL;
+
+ oci_cfg->image_ref = source;
+ oci_cfg->platform = tokens[0];
+
+ v = strtoul(tokens[1], &endptr, 10);
+ if (endptr == tokens[1] || *endptr != '\0')
+ return -EINVAL;
+ oci_cfg->layer_index = (int)v;
+
+ if (token_count > 2) {
+ err = ocierofs_decode_userpass(tokens[2], &oci_cfg->username,
+ &oci_cfg->password);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
static int erofsmount_nbd_fix_backend_linkage(int num, char **recp)
{
char *newrecp;
@@ -491,15 +573,10 @@ static int erofsmount_startnbd_nl(pid_t *pid, struct erofs_nbd_source *source)
exit(EXIT_FAILURE);
ctx.vd.fd = err;
}
-
- if (source->type == EROFSNBD_SOURCE_LOCAL) {
- recp = erofsmount_write_recovery_info(source->device_path);
- if (IS_ERR(recp)) {
- erofs_io_close(&ctx.vd);
- exit(EXIT_FAILURE);
- }
- } else {
- recp = NULL;
+ recp = erofsmount_write_recovery_info(source);
+ if (IS_ERR(recp)) {
+ erofs_io_close(&ctx.vd);
+ exit(EXIT_FAILURE);
}
num = -1;
@@ -595,19 +672,29 @@ static int erofsmount_reattach(const char *target)
*(source++) = '\0';
}
- if (strcmp(line, "LOCAL")) {
+ if (!strcmp(line, "LOCAL")) {
+ err = open(source, O_RDONLY);
+ if (err < 0) {
+ err = -errno;
+ goto err_line;
+ }
+ ctx.vd.fd = err;
+ } else if (!strcmp(line, "OCI_LAYER")) {
+ struct ocierofs_config oci_cfg = {};
+
+ err = erofsmount_parse_recovery_ocilayer(&oci_cfg, source);
+ if (err)
+ goto err_line;
+
+ err = ocierofs_io_open(&ctx.vd, &oci_cfg);
+ if (err < 0)
+ goto err_line;
+ } else {
err = -EOPNOTSUPP;
erofs_err("unsupported source type %s recorded in recovery file", line);
goto err_line;
}
- err = open(source, O_RDONLY);
- if (err < 0) {
- err = -errno;
- goto err_line;
- }
- ctx.vd.fd = err;
-
err = erofs_nbd_nl_reconnect(nbdnum, identifier);
if (err >= 0) {
ctx.sk.fd = err;
--
2.51.0
More information about the Linux-erofs
mailing list