[PATCH v6] erofs-utils: mount: add OCI recovery support for NBD reattach

Gao Xiang hsiangkao at linux.alibaba.com
Wed Sep 10 15:10:36 AEST 2025



On 2025/9/10 11:44, ChengyuZhu6 wrote:
> 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  |  77 +++++++++++++++++++++++
>   mount/main.c       | 153 ++++++++++++++++++++++++++++++++++++---------
>   3 files changed, 205 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..7f16741 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,82 @@ 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)
> +{
> +	size_t ulen = username ? strlen(username) : 0;
> +	size_t plen = password ? strlen(password) : 0;
> +	size_t inlen = ulen + 1 + plen;
> +	size_t outlen;
> +	unsigned char *buf;
> +	char *out;
> +	int ret;
> +
> +	buf = malloc(inlen + 1);
> +	if (!buf)
> +		return ERR_PTR(-ENOMEM);
> +	memcpy(buf, username ? username : "", ulen);
> +	buf[ulen] = ':';
> +	memcpy(buf + ulen + 1, password ? password : "", plen);
> +	buf[inlen] = '\0';

Just:
	asprintf(buf, "%s:%s", username ?: "",
	         password ?: "");

> +
> +	outlen = 4 * ((inlen + 2) / 3);

It seems like
`outlen = 4 * DIV_ROUND_UP(inlen, 3);`

> +	out = malloc(outlen + 1);
> +	if (!out) {
> +		free(buf);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +	ret = erofs_base64_encode(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..f6d0e1e 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 %s %d\n",
> +		       source->ocicfg.image_ref ?: "",
> +		       source->ocicfg.platform ?: "",
> +		       b64cred ?: "",
> +		       source->ocicfg.layer_index);
> +	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,77 @@ 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] [b64cred_or_layer] [layer]"
> + * Supports 4 scenarios:
> + * 1. "image_ref platform" - basic format with platform
> + * 2. "image_ref platform layer" - with layer index only
> + * 3. "image_ref platform b64cred" - with base64 credentials
> + * 4. "image_ref platform b64cred layer" - with both credentials and layer

what's the extract behavior of case 1,3, I think we should just bail out instead?

so that I think the order should be
"<image_ref> <platform> <layer num> [b64cred]" instead.

Thanks,
Gao Xiang


More information about the Linux-erofs mailing list