Thanks, Gao Xiang.

Reviewed-by: [email protected] <mailto:[email protected]>

Thanks,
Chengyu

> 2026年5月19日 12:31,Gao Xiang <[email protected]> 写道:
> 
> From: Chengyu Zhu <[email protected]>
> 
> Wire up the ublk userspace block device backend into mount.erofs,
> providing an alternative to nbd for block device exposure.
> 
> Signed-off-by: Chengyu Zhu <[email protected]>
> Signed-off-by: Gao Xiang <[email protected]>
> ---
> mount/main.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 244 insertions(+), 22 deletions(-)
> 
> diff --git a/mount/main.c b/mount/main.c
> index 90fbdc68f88d..7713ba41058c 100644
> --- a/mount/main.c
> +++ b/mount/main.c
> @@ -23,6 +23,7 @@
> #include "../lib/liberofs_fanotify.h"
> #endif
> #include "../lib/liberofs_s3.h"
> +#include "../lib/liberofs_ublk.h"
> 
> #ifdef HAVE_LINUX_LOOP_H
> #include <linux/loop.h>
> @@ -51,6 +52,7 @@ struct loop_info {
> 
> #define EROFSMOUNT_RUNDIR             "/var/run/erofs"
> #define EROFSMOUNT_NBD_REC_FMT                EROFSMOUNT_RUNDIR 
> "/mountnbd_nbd%d"
> +#define EROFSMOUNT_UBLK_REC_FMT      EROFSMOUNT_RUNDIR "/mountublk_ublkb%d"
> 
> #ifdef EROFS_FANOTIFY_ENABLED
> #define EROFSMOUNT_FANOTIFY_HELP      ", fanotify"
> @@ -58,12 +60,19 @@ struct loop_info {
> #define EROFSMOUNT_FANOTIFY_HELP      ""
> #endif
> 
> +#ifdef HAVE_LIBURING
> +#define EROFSMOUNT_UBLK_HELP         ", ublk"
> +#else
> +#define EROFSMOUNT_UBLK_HELP         ""
> +#endif
> +
> enum erofs_backend_drv {
>       EROFSAUTO,
>       EROFSLOCAL,
>       EROFSFUSE,
>       EROFSNBD,
>       EROFSFANOTIFY,
> +     EROFSUBLK,
> };
> 
> enum erofsmount_mode {
> @@ -117,10 +126,10 @@ static void usage(int argc, char **argv)
>               " -d <0-9>                   set output verbosity; 0=quiet, 
> 9=verbose (default=%i)\n"
>               " -o options                 comma-separated list of mount 
> options\n"
>               " -t type[.subtype]          filesystem type (and optional 
> subtype)\n"
> -             "                            subtypes: fuse, local, nbd" 
> EROFSMOUNT_FANOTIFY_HELP "\n"
> +             "                            subtypes: fuse, local, nbd" 
> EROFSMOUNT_FANOTIFY_HELP EROFSMOUNT_UBLK_HELP "\n"
>               " -u                         unmount the filesystem\n"
>               "    --disconnect            abort an existing NBD device 
> forcibly\n"
> -             "    --reattach              reattach to an existing NBD 
> device\n"
> +             "    --reattach              reattach to an existing NBD or 
> ublk device\n"
> #ifdef OCIEROFS_ENABLED
>               "\n"
>               "OCI-specific options (EXPERIMENTAL, with -o):\n"
> @@ -465,6 +474,12 @@ static int erofsmount_parse_options(int argc, char 
> **argv)
> #else
>                                       erofs_err("fanotify backend support is 
> not built-in");
>                                       return -EINVAL;
> +#endif
> +                             } else if (!strcmp(dot + 1, "ublk")) {
> +#ifdef HAVE_LIBURING
> +                                     mountcfg.backend = EROFSUBLK;
> +#else
> +                                     erofs_err("ublk backend support is not 
> built-in");
> #endif
>                               } else {
>                                       erofs_err("invalid filesystem subtype 
> `%s`", dot + 1);
> @@ -1399,11 +1414,29 @@ out_fork:
>       return num;
> }
> 
> +static int erofsmount_ublk_handler(void *ctx, struct erofs_ublk_request *req)
> +{
> +     struct erofs_vfile *vf = ctx;
> +     ssize_t ret;
> +
> +     if (req->op != EROFS_UBLK_OP_READ)
> +             return -EOPNOTSUPP;
> +
> +     ret = erofs_io_pread(vf, req->buf, req->nr_sectors << 9,
> +                          req->start_sector << 9);
> +     if (ret < 0)
> +             return (int)ret;
> +
> +     req->result = ret;
> +     return 0;
> +}
> +
> static int erofsmount_reattach(const char *target)
> {
>       struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
> +     int ublk_dev_id, nbdnum, err;
> +     char ublk_recp[64];
>       char *identifier;
> -     int nbdnum, err;
>       struct stat st;
>       FILE *f;
> 
> @@ -1411,7 +1444,48 @@ static int erofsmount_reattach(const char *target)
>       if (err < 0)
>               return -errno;
> 
> -     if (!S_ISBLK(st.st_mode) || major(st.st_rdev) != EROFS_NBD_MAJOR)
> +     if (!S_ISBLK(st.st_mode))
> +             return -ENOTBLK;
> +
> +     if (sscanf(target, "/dev/ublkb%d", &ublk_dev_id) == 1) {
> +             if (!erofs_ublk_is_recoverable(ublk_dev_id)) {
> +                     erofs_err("ublk device %d is not recoverable",
> +                               ublk_dev_id);
> +                     return -ENODEV;
> +             }
> +             snprintf(ublk_recp, sizeof(ublk_recp),
> +                      EROFSMOUNT_UBLK_REC_FMT, ublk_dev_id);
> +             f = fopen(ublk_recp, "r");
> +             if (!f) {
> +                     erofs_err("cannot open recovery file %s: %s",
> +                               ublk_recp, strerror(errno));
> +                     return -errno;
> +             }
> +             err = erofsmount_recovery_open_source(&ctx, f);
> +             if (err)
> +                     return err;
> +             if (fork() == 0) {
> +                     if (erofs_ublk_init() < 0)
> +                             exit(EXIT_FAILURE);
> +                     err = erofs_ublk_recover_dev(ublk_dev_id,
> +                                                  erofsmount_ublk_handler,
> +                                                  &ctx.vd);
> +                     if (err) {
> +                             erofs_err("erofs_ublk_recover_dev: %s",
> +                                       strerror(-err));
> +                             exit(EXIT_FAILURE);
> +                     }
> +                     erofs_ublk_start(ublk_dev_id, -1);
> +                     unlink(ublk_recp);
> +                     erofs_ublk_destroy(ublk_dev_id);
> +                     erofs_io_close(ctx.vd);
> +                     exit(EXIT_SUCCESS);
> +             }
> +             erofs_io_close(ctx.vd);
> +             return 0;
> +     }
> +
> +     if (major(st.st_rdev) != EROFS_NBD_MAJOR)
>               return -ENOTBLK;
> 
>       nbdnum = erofs_nbd_get_index_from_minor(minor(st.st_rdev));
> @@ -2034,6 +2108,130 @@ out:
> }
> #endif
> 
> +static int erofsmount_ublk(struct erofsmount_source *source,
> +                        const char *mountpoint, const char *fstype,
> +                        int flags, const char *options)
> +{
> +     int pipefd[2];
> +     char dev_path[64];
> +     pid_t pid;
> +     int dev_id, err;
> +     char ready;
> +
> +     err = erofs_ublk_init();
> +     if (err) {
> +             erofs_err("ublk not supported");
> +             return err;
> +     }
> +
> +     if (pipe(pipefd) < 0)
> +             return -errno;
> +
> +     pid = fork();
> +     if (pid < 0) {
> +             close(pipefd[0]);
> +             close(pipefd[1]);
> +             return -errno;
> +     }
> +
> +     if (pid == 0) {
> +             struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
> +             struct erofs_ublk_dev_info info = {};
> +             char ublk_recp[64], *recp;
> +             struct stat st;
> +
> +             close(pipefd[0]);
> +
> +             err = erofsmount_open_source(&ctx, source);
> +             if (err)
> +                     exit(EXIT_FAILURE);
> +
> +             info.nr_hw_queues = 1;
> +             info.queue_depth = 64;
> +             info.max_io_buf_bytes = 65536;
> +             info.dev_id = -1;
> +             info.blkbits = 12;
> +             info.flags = EROFS_UBLK_F_USER_RECOVERY;
> +
> +             if (source->type == EROFSMOUNT_SOURCE_LOCAL &&
> +                 erofs_io_fstat(ctx.vd, &st) == 0)
> +                     info.dev_size = st.st_size;
> +             else
> +                     info.dev_size = INT64_MAX;
> +
> +             dev_id = erofs_ublk_create_dev(&info,
> +                             erofsmount_ublk_handler, ctx.vd);
> +             if (dev_id < 0) {
> +                     erofs_err("erofs_ublk_create_dev failed: %s",
> +                               strerror(-dev_id));
> +                     exit(EXIT_FAILURE);
> +             }
> +
> +             snprintf(ublk_recp, sizeof(ublk_recp),
> +                      EROFSMOUNT_UBLK_REC_FMT, dev_id);
> +             recp = erofsmount_write_recovery_info(source);
> +             if (IS_ERR(recp)) {
> +                     erofs_err("write_recovery_info: %s",
> +                               strerror(-(int)PTR_ERR(recp)));
> +             } else {
> +                     if (rename(recp, ublk_recp))
> +                             erofs_err("rename recovery: %s",
> +                                       strerror(errno));
> +                     free(recp);
> +             }
> +
> +             if (write(pipefd[1], &dev_id,
> +                       sizeof(dev_id)) != sizeof(dev_id))
> +                     exit(EXIT_FAILURE);
> +
> +             err = erofs_ublk_start(dev_id, pipefd[1]);
> +             if (err)
> +                     erofs_err("erofs_ublk_start: %s",
> +                               strerror(-err));
> +
> +             unlink(ublk_recp);
> +             erofs_ublk_destroy(dev_id);
> +             erofs_io_close(ctx.vd);
> +             exit(EXIT_SUCCESS);
> +     }
> +
> +     close(pipefd[1]);
> +     if (read(pipefd[0], &dev_id, sizeof(dev_id)) !=
> +         sizeof(dev_id)) {
> +             waitpid(pid, NULL, 0);
> +             close(pipefd[0]);
> +             return -EIO;
> +     }
> +
> +     snprintf(dev_path, sizeof(dev_path),
> +              "/dev/ublkb%d", dev_id);
> +
> +     if (read(pipefd[0], &ready, 1) != 1) {
> +             waitpid(pid, NULL, 0);
> +             close(pipefd[0]);
> +             return -EIO;
> +     }
> +     close(pipefd[0]);
> +
> +     err = mount(dev_path, mountpoint, fstype, flags, options);
> +     if (err < 0) {
> +             err = -errno;
> +             kill(pid, SIGTERM);
> +             waitpid(pid, NULL, 0);
> +             return err;
> +     }
> +     return 0;
> +}
> +
> +static int ublk_dev_id_from_path(const char *path)
> +{
> +     int dev_id;
> +
> +     if (sscanf(path, "/dev/ublkb%d", &dev_id) == 1)
> +             return dev_id;
> +     return -1;
> +}
> +
> int erofsmount_umount(char *target)
> {
>       char *device = NULL, *mountpoint = NULL;
> @@ -2071,7 +2269,7 @@ int erofsmount_umount(char *target)
> 
>       for (s = NULL; (getline(&s, &n, mounts)) > 0;) {
>               bool hit = false;
> -             char *f1, *f2, *end;
> +             char *f1, *f2 = NULL, *end;
> 
>               f1 = s;
>               end = strchr(f1, ' ');
> @@ -2088,31 +2286,48 @@ int erofsmount_umount(char *target)
>                               hit = true;
>               }
>               if (hit) {
> -                     if (isblk) {
> -                             err = -EBUSY;
> -                             free(s);
> -                             fclose(mounts);
> -                             goto err_out;
> -                     }
>                       free(device);
>                       device = strdup(f1);
> -                     if (!mountpoint)
> -                             mountpoint = strdup(f2);
> +                     free(mountpoint);
> +                     mountpoint = f2 ? strdup(f2) : NULL;
>               }
>       }
>       free(s);
>       fclose(mounts);
> +
> +     if (isblk && !device) {
> +             if (S_ISBLK(st.st_mode) && major(st.st_rdev) == 
> EROFS_NBD_MAJOR) {
> +                     nbdnum = 
> erofs_nbd_get_index_from_minor(minor(st.st_rdev));
> +                     err = erofs_nbd_nl_disconnect(nbdnum);
> +                     if (err != -EOPNOTSUPP)
> +                             goto err_out;
> +             }
> +             err = ublk_dev_id_from_path(target);
> +             if (err >= 0) {
> +                     err = erofs_ublk_del_dev_by_id(err);
> +                     goto err_out;
> +             }
> +             err = -ENOENT;
> +             goto err_out;
> +     }
> +
>       if (!isblk && !device) {
>               err = -ENOENT;
>               goto err_out;
>       }
> 
> -     if (isblk && !mountpoint &&
> -         S_ISBLK(st.st_mode) && major(st.st_rdev) == EROFS_NBD_MAJOR) {
> -             nbdnum = erofs_nbd_get_index_from_minor(minor(st.st_rdev));
> -             err = erofs_nbd_nl_disconnect(nbdnum);
> -             if (err != -EOPNOTSUPP)
> -                     return err;
> +     err = ublk_dev_id_from_path(device);
> +     if (err >= 0) {
> +             if (mountpoint) {
> +                     int ret = umount(mountpoint);
> +
> +                     if (ret) {
> +                             err = -errno;
> +                             goto err_out;
> +                     }
> +             }
> +             err = erofs_ublk_del_dev_by_id(err);
> +             goto err_out;
>       }
> 
>       /* Avoid TOCTOU issue with NBD_CFLAG_DISCONNECT_ON_CLOSE */
> @@ -2224,13 +2439,20 @@ int main(int argc, char *argv[])
>               goto exit;
>       }
> 
> -     if (mountcfg.backend == EROFSNBD) {
> +     if (mountcfg.backend == EROFSNBD || mountcfg.backend == EROFSUBLK) {
>               if (mountsrc.type == EROFSMOUNT_SOURCE_OCI)
>                       mountsrc.ocicfg.image_ref = mountcfg.device;
>               else
>                       mountsrc.device_path = mountcfg.device;
> -             err = erofsmount_nbd(&mountsrc, mountcfg.target,
> -                                  mountcfg.fstype, mountcfg.flags, 
> mountcfg.options);
> +
> +             if (mountcfg.backend == EROFSNBD)
> +                     err = erofsmount_nbd(&mountsrc, mountcfg.target,
> +                                          mountcfg.fstype, mountcfg.flags,
> +                                          mountcfg.options);
> +             else
> +                     err = erofsmount_ublk(&mountsrc, mountcfg.target,
> +                                           mountcfg.fstype, mountcfg.flags,
> +                                           mountcfg.options);
>               goto exit;
>       }
> 
> -- 
> 2.43.5
> 

Reply via email to