On Tuesday, 10 February 2026 10:45:09 CET Andrey Erokhin wrote:
> On 30/01/2026 19:30, Christian Schoenebeck wrote:
> > On Wednesday, 28 January 2026 20:13:45 CET Andrey Erokhin wrote:
> >>> I was trying to boot from a directory tree owned by an ordinary user,
> >>> and some daemons weren't happy about non-root ownership of some files
> >>> 
> >>> Example use:
> >>> -virtfs
> >>> local,path=rootfs,mount_tag=root,security_model=mapped,uid=0,gid=0
> >> 
> >> I personally switched from fuse-overlayfs to user namespaces+kernel
> >> overlay
> >> fs (for writeable overlay for rootfs) long time ago, so I do not need
> >> uid=0,gid=0, I'm being mapped to 0:0 in the user namespace. I wanted to
> >> publish this change to support users which can't use user namespaces, but
> >> yesterday I realized I could just run QEMU (with fuse-overlayfs) under
> >> fakeroot 🤦‍♂️
> > 
> > Nevertheless you already came more than half way to finish this. All it
> > would take is adding some lines to the command line docs.
> Do you mean smth. like this?

Yes, but you know the drill: top post as v2, please.

> (BTW, is it OK that there is no fmode/dmode processing in system/vl.c?)

You mean error handling of these options. Well, earlier error handling on one 
hand might be more desirable, but OTOH it might also be more complicated to be 
handled in vl.c than either in 9p.c or 9p-local.c I guess.

/Christian

> 
> ---
>  fsdev/file-op-9p.h      |  4 ++++
>  fsdev/qemu-fsdev-opts.c | 12 ++++++++++++
>  fsdev/qemu-fsdev.c      |  2 ++
>  hw/9pfs/9p-local.c      | 25 +++++++++++++++++++++++++
>  hw/9pfs/9p.c            |  2 ++
>  qemu-options.hx         | 24 ++++++++++++++++++++----
>  system/vl.c             |  9 +++++++++
>  7 files changed, 74 insertions(+), 4 deletions(-)
> 
> diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
> index b9dae8c84c..10f3a7270c 100644
> --- a/fsdev/file-op-9p.h
> +++ b/fsdev/file-op-9p.h
> @@ -94,6 +94,8 @@ typedef struct FsDriverEntry {
>      FsThrottle fst;
>      mode_t fmode;
>      mode_t dmode;
> +    uid_t dflt_uid;
> +    gid_t dflt_gid;
>  } FsDriverEntry;
> 
>  struct FsContext {
> @@ -107,6 +109,8 @@ struct FsContext {
>      void *private;
>      mode_t fmode;
>      mode_t dmode;
> +    uid_t dflt_uid;
> +    gid_t dflt_gid;
>  };
> 
>  struct V9fsPath {
> diff --git a/fsdev/qemu-fsdev-opts.c b/fsdev/qemu-fsdev-opts.c
> index 07a18c6e48..c99abb3de6 100644
> --- a/fsdev/qemu-fsdev-opts.c
> +++ b/fsdev/qemu-fsdev-opts.c
> @@ -46,6 +46,12 @@ static QemuOptsList qemu_fsdev_opts = {
>          }, {
>              .name = "dmode",
>              .type = QEMU_OPT_NUMBER,
> +        }, {
> +            .name = "uid",
> +            .type = QEMU_OPT_NUMBER,
> +        }, {
> +            .name = "gid",
> +            .type = QEMU_OPT_NUMBER,
>          },
> 
>          THROTTLE_OPTS,
> @@ -92,6 +98,12 @@ static QemuOptsList qemu_virtfs_opts = {
>          }, {
>              .name = "dmode",
>              .type = QEMU_OPT_NUMBER,
> +        }, {
> +            .name = "uid",
> +            .type = QEMU_OPT_NUMBER,
> +        }, {
> +            .name = "gid",
> +            .type = QEMU_OPT_NUMBER,
>          },
> 
>          { /*End of list */ }
> diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
> index 57877dad0a..faa84dc033 100644
> --- a/fsdev/qemu-fsdev.c
> +++ b/fsdev/qemu-fsdev.c
> @@ -58,6 +58,8 @@ static FsDriverTable FsDrivers[] = {
>              "writeout",
>              "fmode",
>              "dmode",
> +            "uid",
> +            "gid",
>              "multidevs",
>              "throttling.bps-total",
>              "throttling.bps-read",
> diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
> index 5ce97b76a6..f20b1c5d1a 100644
> --- a/hw/9pfs/9p-local.c
> +++ b/hw/9pfs/9p-local.c
> @@ -198,6 +198,12 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath
> *fs_path, struct stat *stbuf) if (err) {
>          goto err_out;
>      }
> +    if (fs_ctx->dflt_uid != -1) {
> +        stbuf->st_uid = fs_ctx->dflt_uid;
> +    }
> +    if (fs_ctx->dflt_gid != -1) {
> +        stbuf->st_gid = fs_ctx->dflt_gid;
> +    }
>      if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
>          /* Actual credentials are part of extended attrs */
>          uid_t tmp_uid;
> @@ -788,6 +794,12 @@ static int local_fstat(FsContext *fs_ctx, int fid_type,
> if (err) {
>          return err;
>      }
> +    if (fs_ctx->dflt_uid != -1) {
> +        stbuf->st_uid = fs_ctx->dflt_uid;
> +    }
> +    if (fs_ctx->dflt_gid != -1) {
> +        stbuf->st_gid = fs_ctx->dflt_gid;
> +    }
>      if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
>          /* Actual credentials are part of extended attrs */
>          uid_t tmp_uid;
> @@ -1587,6 +1599,19 @@ static int local_parse_opts(QemuOpts *opts,
> FsDriverEntry *fse, Error **errp) }
>      }
> 
> +    if (fse->export_flags & V9FS_SM_PASSTHROUGH) {
> +        if (qemu_opt_find(opts, "uid")) {
> +            error_setg(errp, "uid is invalid in the passthrough security
> mode"); +            return -1;
> +        }
> +        if (qemu_opt_find(opts, "gid")) {
> +            error_setg(errp, "gid is invalid in the passthrough security
> mode"); +            return -1;
> +        }
> +    }
> +    fse->dflt_uid = qemu_opt_get_number(opts, "uid", -1);
> +    fse->dflt_gid = qemu_opt_get_number(opts, "gid", -1);
> +
>      fse->path = g_strdup(path);
> 
>      return 0;
> diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
> index acfa7db4e1..492379d361 100644
> --- a/hw/9pfs/9p.c
> +++ b/hw/9pfs/9p.c
> @@ -4317,6 +4317,8 @@ int v9fs_device_realize_common(V9fsState *s, const
> V9fsTransport *t,
> 
>      s->ctx.fmode = fse->fmode;
>      s->ctx.dmode = fse->dmode;
> +    s->ctx.dflt_uid = fse->dflt_uid;
> +    s->ctx.dflt_gid = fse->dflt_gid;
> 
>      s->fids = g_hash_table_new(NULL, NULL);
>      qemu_co_rwlock_init(&s->rename_lock);
> diff --git a/qemu-options.hx b/qemu-options.hx
> index ab23f14d21..84f108d9ad 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1806,7 +1806,7 @@ ERST
> 
>  DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
>      "-fsdev
> local,id=id,path=path,security_model=mapped-xattr|mapped-file|passthrough|n
> one\n" -    "
> [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode]\n" +    "
> [,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,uid=uid][,
> gid=gid]\n" "
> [[,throttling.bps-total=b]|[[,throttling.bps-read=r][,throttling.bps-write=
> w]]]\n" "
> [[,throttling.iops-total=i]|[[,throttling.iops-read=r][,throttling.iops-wri
> te=w]]]\n" "
> [[,throttling.bps-total-max=bm]|[[,throttling.bps-read-max=rm][,throttling.
> bps-write-max=wm]]]\n" @@ -1816,7 +1816,7 @@ DEF("fsdev", HAS_ARG,
> QEMU_OPTION_fsdev,
>      QEMU_ARCH_ALL)
> 
>  SRST
> -``-fsdev local,id=id,path=path,security_model=security_model
> [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode]
> [,throttling.option=value[,throttling.option=value[,...]]]`` +``-fsdev
> local,id=id,path=path,security_model=security_model
> [,writeout=writeout][,readonly=on][,fmode=fmode][,dmode=dmode][,uid=uid][,g
> id=gid] [,throttling.option=value[,throttling.option=value[,...]]]`` \
>  ``-fsdev synth,id=id[,readonly=on]``
>      Define a new file system device. Valid options are:
> @@ -1870,6 +1870,14 @@ SRST
>          host. Works only with security models "mapped-xattr" and
>          "mapped-file".
> 
> +    ``uid=uid``
> +        Specifies the default uid for files and directories. Works with
> +        security models "mapped-xattr", "mapped-file" and "none".
> +
> +    ``gid=gid``
> +        Specifies the default gid for files and directories. Works with
> +        security models "mapped-xattr", "mapped-file" and "none".
> +
>      ``throttling.bps-total=b,throttling.bps-read=r,throttling.bps-write=w``
> Specify bandwidth throttling limits in bytes per second, either for all
> request types or for reads or writes only.
> @@ -1911,12 +1919,12 @@ ERST
> 
>  DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
>      "-virtfs
> local,path=path,mount_tag=tag,security_model=mapped-xattr|mapped-file|passt
> hrough|none\n" -    "       
> [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,mu
> ltidevs=remap|forbid|warn]\n" +    "       
> [,id=id][,writeout=immediate][,readonly=on][,fmode=fmode][,dmode=dmode][,ui
> d=uid][,gid=gid][,multidevs=remap|forbid|warn]\n" "-virtfs
> synth,mount_tag=tag[,id=id][,readonly=on]\n",
>      QEMU_ARCH_ALL)
> 
>  SRST
> -``-virtfs local,path=path,mount_tag=mount_tag
> ,security_model=security_model[,writeout=writeout][,readonly=on]
> [,fmode=fmode][,dmode=dmode][,multidevs=multidevs]`` +``-virtfs
> local,path=path,mount_tag=mount_tag
> ,security_model=security_model[,writeout=writeout][,readonly=on]
> [,fmode=fmode][,dmode=dmode][,uid=uid][,gid=gid][,multidevs=multidevs]`` \
>  ``-virtfs synth,mount_tag=mount_tag``
>      Define a new virtual filesystem device and expose it to the guest using
> @@ -1980,6 +1988,14 @@ SRST
>          host. Works only with security models "mapped-xattr" and
>          "mapped-file".
> 
> +    ``uid=uid``
> +        Specifies the default uid for files and directories. Works with
> +        security models "mapped-xattr", "mapped-file" and "none".
> +
> +    ``gid=gid``
> +        Specifies the default gid for files and directories. Works with
> +        security models "mapped-xattr", "mapped-file" and "none".
> +
>      ``mount_tag=mount_tag``
>          Specifies the tag name to be used by the guest to mount this
>          export point.
> diff --git a/system/vl.c b/system/vl.c
> index 3b7057e6c6..d363b046a6 100644
> --- a/system/vl.c
> +++ b/system/vl.c
> @@ -3253,6 +3253,7 @@ void qemu_init(int argc, char **argv)
>                  QemuOpts *fsdev;
>                  QemuOpts *device;
>                  const char *writeout, *sock_fd, *socket, *path,
> *security_model, +                           *uid, *gid,
>                             *multidevs;
> 
>                  olist = qemu_find_opts("virtfs");
> @@ -3301,6 +3302,14 @@ void qemu_init(int argc, char **argv)
>                      qemu_opt_set(fsdev, "security_model", security_model,
>                                   &error_abort);
>                  }
> +                uid = qemu_opt_get(opts, "uid");
> +                if (uid) {
> +                    qemu_opt_set(fsdev, "uid", uid, &error_abort);
> +                }
> +                gid = qemu_opt_get(opts, "gid");
> +                if (gid) {
> +                    qemu_opt_set(fsdev, "gid", gid, &error_abort);
> +                }
>                  socket = qemu_opt_get(opts, "socket");
>                  if (socket) {
>                      qemu_opt_set(fsdev, "socket", socket, &error_abort);
> --





Reply via email to