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]>
[ Gao Xiang: drop recovery support for now. ]
Signed-off-by: Gao Xiang <[email protected]>
---
 mount/main.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 172 insertions(+), 10 deletions(-)

diff --git a/mount/main.c b/mount/main.c
index 140591006e5d..f3f94d04c4cf 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>
@@ -59,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 {
@@ -118,7 +126,7 @@ 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"
@@ -466,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);
@@ -1402,6 +1416,26 @@ out_fork:
        return num;
 }
 
+static int erofsmount_ublk_handler(void *ctx, struct erofs_ublk_request *rq)
+{
+       struct erofs_vfile *vf = ctx;
+       ssize_t ret;
+
+       if (rq->op != EROFS_UBLK_OP_READ) {
+               rq->result = -EROFS;
+               return -EOPNOTSUPP;
+       }
+
+       ret = erofs_io_pread(vf, rq->buf, rq->nr_sectors << 9,
+                            rq->start_sector << 9);
+       if (ret < 0) {
+               rq->result = -EIO;
+               return (int)ret;
+       }
+       rq->result = ret;
+       return 0;
+}
+
 static int erofsmount_reattach(const char *target)
 {
        struct erofsmount_nbd_ctx ctx = { .vd = &ctx._vd };
@@ -2037,6 +2071,109 @@ out:
 }
 #endif
 
+static int erofsmount_ublk(struct erofsmount_source *source,
+                          const char *mountpoint, const char *fstype,
+                          int flags, const char *options)
+{
+       char *dev_path, ready;
+       int dev_id, err;
+       int pipefd[2];
+       pid_t pid;
+
+       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;
+               struct stat st;
+
+               close(pipefd[0]);
+               err = erofsmount_open_source(&ctx, source);
+               if (err)
+                       exit(EXIT_FAILURE);
+
+               info = (struct erofs_ublk_dev_info) {
+                       .nr_hw_queues = EROFS_UBLK_DEF_NR_HW_QUEUES,
+                       .queue_depth = EROFS_UBLK_DEF_QUEUE_DEPTH,
+                       .max_io_buf_bytes = EROFS_UBLK_DEF_MAX_IO_BUF_BYTES,
+                       .dev_id = -1,
+                       .blkbits = EROFS_UBLK_DEF_BLK_BITS,
+                       .flags = 0,
+                       .dev_size = source->type == EROFSMOUNT_SOURCE_LOCAL &&
+                               erofs_io_fstat(ctx.vd, &st) == 0 ?
+                                       st.st_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);
+               }
+
+               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));
+               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;
+       }
+
+       if (read(pipefd[0], &ready, 1) != 1) {
+               waitpid(pid, NULL, 0);
+               close(pipefd[0]);
+               return -EIO;
+       }
+       close(pipefd[0]);
+
+       if (asprintf(&dev_path, "/dev/ublkb%d", dev_id) < 0)
+               err = -ENOMEM;
+       else
+               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;
@@ -2110,12 +2247,30 @@ int erofsmount_umount(char *target)
                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;
+       if (isblk && !mountpoint && S_ISBLK(st.st_mode)) {
+               if (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;
+               } else if ((nbdnum = ublk_dev_id_from_path(target)) >= 0) {
+                       err = erofs_ublk_del_dev_by_id(nbdnum);
+                       goto err_out;
+               }
+       }
+
+       /* XXX: ublk doesn't have autoclose feature */
+       nbdnum = ublk_dev_id_from_path(device);
+       if (nbdnum >= 0) {
+               if (mountpoint) {
+                       err = umount(mountpoint);
+                       if (err) {
+                               err = -errno;
+                               goto err_out;
+                       }
+               }
+               err = erofs_ublk_del_dev_by_id(nbdnum);
+               goto err_out;
        }
 
        /* Avoid TOCTOU issue with NBD_CFLAG_DISCONNECT_ON_CLOSE */
@@ -2227,13 +2382,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