From: Chengyu Zhu <hudson...@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 <hudson...@tencent.com>
---
 lib/liberofs_oci.h |   3 +
 lib/remotes/oci.c  |  66 +++++++++++++++++++++
 mount/main.c       | 143 ++++++++++++++++++++++++++++++++++++---------
 3 files changed, 184 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..937eed7 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,71 @@ 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;
+
+       ret = asprintf(&buf, "%s:%s", username ?: "", password ?: "");
+       if (ret == -1)
+               return ERR_PTR(-ENOMEM);
+       outlen = 4 * DIV_ROUND_UP(ret, 3);
+       out = malloc(outlen + 1);
+       if (!out) {
+               ret = -ENOMEM;
+       } else {
+               ret = erofs_base64_encode((unsigned char *)buf, ret, out);
+               if (ret < 0)
+                       free(out);
+               out[ret] = '\0';
+       }
+       free(buf);
+       return ret < 0 ? ERR_PTR(ret) : 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


Reply via email to