fsdev/file-op-9p.h | 5 +++++ fsdev/qemu-fsdev-opts.c | 12 ++++++++++++ fsdev/qemu-fsdev.c | 2 ++ hw/9pfs/9p-local.c | 15 +++++++++++++++ hw/9pfs/9p.c | 2 ++ system/vl.c | 9 +++++++++ 6 files changed, 45 insertions(+)
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
Works with any security_model
Signed-off-by: Andrey Erokhin <language.lawyer@gmail.com>
---
fsdev/file-op-9p.h | 5 +++++
fsdev/qemu-fsdev-opts.c | 12 ++++++++++++
fsdev/qemu-fsdev.c | 2 ++
hw/9pfs/9p-local.c | 15 +++++++++++++++
hw/9pfs/9p.c | 2 ++
system/vl.c | 9 +++++++++
6 files changed, 45 insertions(+)
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index b9dae8c84c..46fb88001e 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -15,6 +15,7 @@
#define FILE_OP_9P_H
#include <dirent.h>
+#include <sys/types.h>
#include <utime.h>
#include "qemu-fsdev-throttle.h"
#include "p9array.h"
@@ -94,6 +95,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 +110,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..cecf4aa50c 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;
@@ -1570,6 +1582,9 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
return -1;
}
+ fse->dflt_uid = qemu_opt_get_number(opts, "uid", -1);
+ fse->dflt_gid = qemu_opt_get_number(opts, "gid", -1);
+
if (fse->export_flags & V9FS_SM_MAPPED ||
fse->export_flags & V9FS_SM_MAPPED_FILE) {
fse->fmode =
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/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);
--
2.34.1
On Monday, 1 December 2025 19:00:53 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
>
> Works with any security_model
First I thought do we really want to open that rabbit hole and add permission
management to the CLI options? However I get why this might be useful for
mapped[-*] security models.
But for passthrough it is not of any use, is it? Just saying, because you
write it "Works with any security_model".
Also while it is very handy to have a short option name like "uid" and "gid",
for the sake of long term progression and clarity an option name like
"default-uid" would be more appropriate.
The patch is also missing the required documentation changes for these new
options BTW.
/Christian
> Signed-off-by: Andrey Erokhin <language.lawyer@gmail.com>
> ---
> fsdev/file-op-9p.h | 5 +++++
> fsdev/qemu-fsdev-opts.c | 12 ++++++++++++
> fsdev/qemu-fsdev.c | 2 ++
> hw/9pfs/9p-local.c | 15 +++++++++++++++
> hw/9pfs/9p.c | 2 ++
> system/vl.c | 9 +++++++++
> 6 files changed, 45 insertions(+)
>
> diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
> index b9dae8c84c..46fb88001e 100644
> --- a/fsdev/file-op-9p.h
> +++ b/fsdev/file-op-9p.h
> @@ -15,6 +15,7 @@
> #define FILE_OP_9P_H
>
> #include <dirent.h>
> +#include <sys/types.h>
> #include <utime.h>
> #include "qemu-fsdev-throttle.h"
> #include "p9array.h"
> @@ -94,6 +95,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 +110,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..cecf4aa50c 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;
> @@ -1570,6 +1582,9 @@ static int local_parse_opts(QemuOpts *opts,
> FsDriverEntry *fse, Error **errp) return -1;
> }
>
> + fse->dflt_uid = qemu_opt_get_number(opts, "uid", -1);
> + fse->dflt_gid = qemu_opt_get_number(opts, "gid", -1);
> +
> if (fse->export_flags & V9FS_SM_MAPPED ||
> fse->export_flags & V9FS_SM_MAPPED_FILE) {
> fse->fmode =
> 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/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);
On 03/12/2025 15:33, Christian Schoenebeck wrote: > On Monday, 1 December 2025 19:00:53 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 >> >> Works with any security_model > > First I thought do we really want to open that rabbit hole and add permission management to the CLI options? However I get why this might be useful for mapped[-*] security models. > But for passthrough it is not of any use, is it? Prolly none, just a side effect of how it's implemented. Can either make it an error when used with passthrough, or ignore them (use default -1 value) when copying options to 9p fs context (with or without a warning) > Also while it is very handy to have a short option name like "uid" and "gid", for the sake of long term progression and clarity an option name like "default-uid" would be more appropriate. Or rather default_uid, to match other options style? But uid/gid also kinda match fmode/dmode :\ > The patch is also missing the required documentation changes for these new options BTW. Haven’t added them yet, wasn’t sure there would be interest in this feature
On Sat, Dec 6, 2025, 10:12 AM Andrey Erokhin <language.lawyer@gmail.com> wrote: > On 03/12/2025 15:33, Christian Schoenebeck wrote: > > On Monday, 1 December 2025 19:00:53 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 > >> > >> Works with any security_model > > > > First I thought do we really want to open that rabbit hole and add > permission management to the CLI options? However I get why this might be > useful for mapped[-*] security models. > > But for passthrough it is not of any use, is it? > > Prolly none, just a side effect of how it's implemented. > Can either make it an error when used with passthrough, or ignore them > (use default -1 value) when copying options to 9p fs context (with or > without a warning) > > > Also while it is very handy to have a short option name like "uid" and > "gid", for the sake of long term progression and clarity an option name > like "default-uid" would be more appropriate. > > Or rather default_uid, to match other options style? But uid/gid also > kinda match fmode/dmode :\ > FreeBSD has a mode where you can build the image where the files in the filesystem are owned by the user with random permission bits, but the actual owners / modes are in an mtree formatted file. The nopriv imagers combine the two when making images. It would be nice to have p9 do a simular mapping for the guest so I can boot test these images more directly w/o the copyout to the "bootable image". The set the uid feature would help, true, but leaves me wanting more. Warner > The patch is also missing the required documentation changes for these > new options BTW. > > Haven’t added them yet, wasn’t sure there would be interest in this feature > >
On Sunday, 7 December 2025 12:34:24 CET Warner Losh wrote: > On Sat, Dec 6, 2025, 10:12 AM Andrey Erokhin <language.lawyer@gmail.com> > wrote: > > On 03/12/2025 15:33, Christian Schoenebeck wrote: > > > On Monday, 1 December 2025 19:00:53 CET Andrey Erokhin wrote: [...] > > > But for passthrough it is not of any use, is it? > > > > Prolly none, just a side effect of how it's implemented. > > Can either make it an error when used with passthrough, or ignore them > > (use default -1 value) when copying options to 9p fs context (with or > > without a warning) > > > > > Also while it is very handy to have a short option name like "uid" and > > > > "gid", for the sake of long term progression and clarity an option name > > like "default-uid" would be more appropriate. > > > > Or rather default_uid, to match other options style? But uid/gid also > > kinda match fmode/dmode :\ Right, that would render it strange having default_uid/default_gid vs. fmod/ gmode when all of them actually mean default values. OK, as fmode/dmode are already there, then let's stick to your initial suggestion of just using uid/gid. But similar to fmode/dmode it should be made clear on documentation level that uid/gid are only useful for mapped security models. > FreeBSD has a mode where you can build the image where the files in the > filesystem are owned by the user with random permission bits, but the > actual owners / modes are in an mtree formatted file. The nopriv imagers > combine the two when making images. It would be nice to have p9 do a > simular mapping for the guest so I can boot test these images more directly > w/o the copyout to the "bootable image". The set the uid feature would > help, true, but leaves me wanting more. And a host level (not yet existing) tool like qemu-9p-chown, qemu-9p-chmod would be less appropriate for your use case? /Christian
On Tue, Dec 9, 2025 at 3:21 AM Christian Schoenebeck <qemu_oss@crudebyte.com>
wrote:
> On Sunday, 7 December 2025 12:34:24 CET Warner Losh wrote:
> > On Sat, Dec 6, 2025, 10:12 AM Andrey Erokhin <language.lawyer@gmail.com>
>
> > wrote:
> > > On 03/12/2025 15:33, Christian Schoenebeck wrote:
> > > > On Monday, 1 December 2025 19:00:53 CET Andrey Erokhin wrote:
> [...]
> > > > But for passthrough it is not of any use, is it?
> > >
> > > Prolly none, just a side effect of how it's implemented.
> > > Can either make it an error when used with passthrough, or ignore them
> > > (use default -1 value) when copying options to 9p fs context (with or
> > > without a warning)
> > >
> > > > Also while it is very handy to have a short option name like "uid"
> and
> > >
> > > "gid", for the sake of long term progression and clarity an option name
> > > like "default-uid" would be more appropriate.
> > >
> > > Or rather default_uid, to match other options style? But uid/gid also
> > > kinda match fmode/dmode :\
>
> Right, that would render it strange having default_uid/default_gid vs.
> fmod/
> gmode when all of them actually mean default values.
>
> OK, as fmode/dmode are already there, then let's stick to your initial
> suggestion of just using uid/gid.
>
> But similar to fmode/dmode it should be made clear on documentation level
> that
> uid/gid are only useful for mapped security models.
>
> > FreeBSD has a mode where you can build the image where the files in the
> > filesystem are owned by the user with random permission bits, but the
> > actual owners / modes are in an mtree formatted file. The nopriv imagers
> > combine the two when making images. It would be nice to have p9 do a
> > simular mapping for the guest so I can boot test these images more
> directly
> > w/o the copyout to the "bootable image". The set the uid feature would
> > help, true, but leaves me wanting more.
>
> And a host level (not yet existing) tool like qemu-9p-chown, qemu-9p-chmod
> would be less appropriate for your use case?
>
I can't answer directly, since I can't look them up :)
But... I want to own all the files on the host, but I want them to conform
to a spec on
view p9 gives to the guest:
/etc/rc.d type=dir uname=root gname=wheel mode=755
./etc/rc.d/accounting type=file uname=root gname=wheel mode=555
./usr/bin type=dir uname=root gname=wheel mode=755
./usr type=dir uname=root gname=wheel mode=755
./usr/bin/last type=file uname=root gname=wheel mode=555
is a small excerpt of the file we happen to use (though I'm agnostic as to
the actual
format). But these files are long:
wc _.armv7.14.3.metalog
5316 26759 399552 _.armv7.14.3.metalog
which might pose problems...
Warner
© 2016 - 2025 Red Hat, Inc.