[PATCH 2/2] erofs-utils: mount: add recovery support for S3 object mounts
Gao Xiang <[email protected]>
| Newsgroups | org.ozlabs.lists.linux-erofs |
|---|---|
| Message-ID | <[email protected]> |
Assisted-by: qoder:(unknown) Cc: Yuxuan Liu <[email protected]> Signed-off-by: Gao Xiang <[email protected]> --- lib/liberofs_s3.h | 7 ++- lib/remotes/s3.c | 72 ++++++++++++++++++++++++++++++ mount/main.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 2 deletions(-) diff --git a/lib/liberofs_s3.h b/lib/liberofs_s3.h index 3d2b2727b3b6..0c1f6c6d6a10 100644 --- a/lib/liberofs_s3.h +++ b/lib/liberofs_s3.h @@ -26,8 +26,8 @@ enum s3erofs_signature_version { struct erofs_s3 { void *easy_curl; - const char *endpoint; - const char *region; + char *endpoint; + char *region; char access_key[S3_ACCESS_KEY_LEN + 1]; char secret_key[S3_SECRET_KEY_LEN + 1]; @@ -43,6 +43,9 @@ struct erofs_vfile *s3erofs_io_open(struct erofs_s3 *s3, const char *bucket, const char *key); int s3erofs_parse_s3fs_passwd(const char *filepath, char *ak, char *sk); +char *s3erofs_encode_cred(const char *access_key, const char *secret_key); +int s3erofs_decode_cred(const char *b64, char **out_access_key, char **out_secret_key); + #ifdef __cplusplus } #endif diff --git a/lib/remotes/s3.c b/lib/remotes/s3.c index 35df935f8328..9cbf7ffd1035 100644 --- a/lib/remotes/s3.c +++ b/lib/remotes/s3.c @@ -1449,6 +1449,78 @@ err: return ret; } +char *s3erofs_encode_cred(const char *access_key, const char *secret_key) +{ + char *cred, *out; + size_t outlen; + int ret; + + ret = asprintf(&cred, "%s:%s", access_key ?: "", secret_key ?: ""); + if (ret < 0) + return ERR_PTR(-ENOMEM); + + outlen = 4 * DIV_ROUND_UP(ret, 3); + out = malloc(outlen + 1); + if (!out) { + free(cred); + return ERR_PTR(-ENOMEM); + } + ret = erofs_base64_encode((u8 *)cred, ret, out); + if (ret < 0) { + free(out); + free(cred); + return ERR_PTR(ret); + } + out[ret] = '\0'; + free(cred); + return out; +} + +int s3erofs_decode_cred(const char *b64, char **out_access_key, + char **out_secret_key) +{ + size_t len; + unsigned char *out; + int ret; + char *colon; + + if (!b64 || !out_access_key || !out_secret_key) + return -EINVAL; + + *out_access_key = NULL; + *out_secret_key = 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 = strchr((char *)out, ':'); + if (!colon) { + free(out); + return -EINVAL; + } + + *colon = '\0'; + *out_access_key = strdup((char *)out); + *out_secret_key = strdup(colon + 1); + free(out); + + if (!*out_access_key || !*out_secret_key) { + free(*out_access_key); + free(*out_secret_key); + return -ENOMEM; + } + return 0; +} + #ifdef TEST struct s3erofs_prepare_url_testcase { const char *name; diff --git a/mount/main.c b/mount/main.c index bd7beb1fbb13..25f94f4a29b5 100644 --- a/mount/main.c +++ b/mount/main.c @@ -956,6 +956,37 @@ static int erofsmount_write_recovery_local(FILE *f, struct erofsmount_source *so return err ? -ENOMEM : 0; } +#ifdef S3EROFS_ENABLED +static int erofsmount_write_recovery_s3(FILE *f, struct erofsmount_source *source) +{ + char *b64cred = NULL; + int ret; + + if (source->s3cfg.access_key[0] || source->s3cfg.secret_key[0]) { + b64cred = s3erofs_encode_cred(source->s3cfg.access_key, + source->s3cfg.secret_key); + if (IS_ERR(b64cred)) + return PTR_ERR(b64cred); + } + + /* S3_OBJECT <bucket/key> <endpoint> <urlstyle> <sig> <region> [b64cred] */ + ret = fprintf(f, "S3_OBJECT %s %s %d %d %s %s\n", + source->device_path, + source->s3cfg.endpoint, + source->s3cfg.url_style, + source->s3cfg.sig, + source->s3cfg.region ?: "(nil)", + b64cred ?: ""); + free(b64cred); + return ret < 0 ? -ENOMEM : 0; +} +#else +static int erofsmount_write_recovery_s3(FILE *f, struct erofsmount_source *source) +{ + return -EOPNOTSUPP; +} +#endif + static char *erofsmount_write_recovery_info(struct erofsmount_source *source) { char recp[] = "/var/run/erofs/mountnbd_XXXXXX"; @@ -980,6 +1011,8 @@ static char *erofsmount_write_recovery_info(struct erofsmount_source *source) if (source->type == EROFSMOUNT_SOURCE_OCI) err = erofsmount_write_recovery_oci(f, source); + else if (source->type == EROFSMOUNT_SOURCE_S3_OBJECT) + err = erofsmount_write_recovery_s3(f, source); else if (source->type == EROFSMOUNT_SOURCE_LOCAL) err = erofsmount_write_recovery_local(f, source); @@ -1106,6 +1139,76 @@ static int erofsmount_reattach_oci(struct erofs_vfile *vf, } #endif +#ifdef S3EROFS_ENABLED +static int erofsmount_reattach_s3(struct erofsmount_nbd_ctx *ctx, char *source) +{ + char *tokens[5] = {0}, *p = source; + char *bucket = NULL, *key = NULL; + struct erofs_s3 *s3cfg = &mountsrc.s3cfg; + int token_count = 0, err; + struct erofs_vfile *vf; + + while (token_count < 5 && (p = strchr(p, ' ')) != NULL) { + *p++ = '\0'; + while (*p == ' ') + p++; + if (*p == '\0') + break; + tokens[token_count++] = p; + } + + if (token_count < 4) + return -EINVAL; + + s3cfg->endpoint = strdup(tokens[0]); + s3cfg->url_style = atoi(tokens[1]); + s3cfg->sig = atoi(tokens[2]); + s3cfg->region = strdup(tokens[3]); + if (!s3cfg->endpoint || !s3cfg->region) + return -ENOMEM; + + err = erofsmount_parse_s3_source(s3cfg, source, &bucket, &key); + if (err) + return err; + + if (token_count > 4 && tokens[4][0]) { + char *tmp_access = NULL, *tmp_secret = NULL; + + err = s3erofs_decode_cred(tokens[4], &tmp_access, &tmp_secret); + if (err) + goto err_out; + if (tmp_access) { + strncpy(s3cfg->access_key, tmp_access, S3_ACCESS_KEY_LEN); + s3cfg->access_key[S3_ACCESS_KEY_LEN] = '\0'; + free(tmp_access); + } + if (tmp_secret) { + strncpy(s3cfg->secret_key, tmp_secret, S3_SECRET_KEY_LEN); + s3cfg->secret_key[S3_SECRET_KEY_LEN] = '\0'; + free(tmp_secret); + } + } + vf = s3erofs_io_open(s3cfg, bucket, key); + free(bucket); + free(key); + if (IS_ERR(vf)) + return PTR_ERR(vf); + ctx->vd = vf; + return 0; +err_out: + free(bucket); + free(key); + free(s3cfg->region); + free(s3cfg->endpoint); + return err; +} +#else +static int erofsmount_reattach_s3(struct erofsmount_nbd_ctx *ctx, char *source) +{ + return -EOPNOTSUPP; +} +#endif + static int erofsmount_reattach_gzran_oci(struct erofsmount_nbd_ctx *ctx, char *source) { @@ -1352,6 +1455,12 @@ static int erofsmount_reattach(const char *target) err = erofsmount_reattach_oci(ctx.vd, line, source); if (err) goto err_line; +#ifdef S3EROFS_ENABLED + } else if (!strcmp(line, "S3_OBJECT")) { + err = erofsmount_reattach_s3(&ctx, source); + if (err) + goto err_line; +#endif } else { err = -EOPNOTSUPP; erofs_err("unsupported source type %s recorded in recovery file", line); -- 2.43.5