* [PATCH 1/4] uidgid: add map_id_range_up()
2025-01-29 23:19 [PATCH 0/4] statmount: allow to retrieve idmappings Christian Brauner
@ 2025-01-29 23:19 ` Christian Brauner
2025-01-29 23:19 ` [PATCH 2/4] statmount: allow to retrieve idmappings Christian Brauner
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Christian Brauner @ 2025-01-29 23:19 UTC (permalink / raw)
To: linux-fsdevel
Cc: Josef Bacik, Jeff Layton, Lennart Poettering, Daan De Meyer,
Seth Forshee, Miklos Szeredi, Christian Brauner
Add map_id_range_up() to verify that the full kernel id range can be
mapped up in a given idmapping. This will be used in follow-up patches.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
include/linux/uidgid.h | 6 ++++++
kernel/user_namespace.c | 26 +++++++++++++++++---------
2 files changed, 23 insertions(+), 9 deletions(-)
diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h
index f85ec5613721..2dc767e08f54 100644
--- a/include/linux/uidgid.h
+++ b/include/linux/uidgid.h
@@ -132,6 +132,7 @@ static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid)
u32 map_id_down(struct uid_gid_map *map, u32 id);
u32 map_id_up(struct uid_gid_map *map, u32 id);
+u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count);
#else
@@ -186,6 +187,11 @@ static inline u32 map_id_down(struct uid_gid_map *map, u32 id)
return id;
}
+static inline u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count)
+{
+ return id;
+}
+
static inline u32 map_id_up(struct uid_gid_map *map, u32 id)
{
return id;
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index aa0b2e47f2f2..682f40d5632d 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -238,7 +238,7 @@ EXPORT_SYMBOL(__put_user_ns);
struct idmap_key {
bool map_up; /* true -> id from kid; false -> kid from id */
u32 id; /* id to find */
- u32 count; /* == 0 unless used with map_id_range_down() */
+ u32 count;
};
/*
@@ -343,16 +343,19 @@ u32 map_id_down(struct uid_gid_map *map, u32 id)
* UID_GID_MAP_MAX_BASE_EXTENTS.
*/
static struct uid_gid_extent *
-map_id_up_base(unsigned extents, struct uid_gid_map *map, u32 id)
+map_id_range_up_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
{
unsigned idx;
- u32 first, last;
+ u32 first, last, id2;
+
+ id2 = id + count - 1;
/* Find the matching extent */
for (idx = 0; idx < extents; idx++) {
first = map->extent[idx].lower_first;
last = first + map->extent[idx].count - 1;
- if (id >= first && id <= last)
+ if (id >= first && id <= last &&
+ (id2 >= first && id2 <= last))
return &map->extent[idx];
}
return NULL;
@@ -363,28 +366,28 @@ map_id_up_base(unsigned extents, struct uid_gid_map *map, u32 id)
* Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS.
*/
static struct uid_gid_extent *
-map_id_up_max(unsigned extents, struct uid_gid_map *map, u32 id)
+map_id_range_up_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count)
{
struct idmap_key key;
key.map_up = true;
- key.count = 1;
+ key.count = count;
key.id = id;
return bsearch(&key, map->reverse, extents,
sizeof(struct uid_gid_extent), cmp_map_id);
}
-u32 map_id_up(struct uid_gid_map *map, u32 id)
+u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count)
{
struct uid_gid_extent *extent;
unsigned extents = map->nr_extents;
smp_rmb();
if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
- extent = map_id_up_base(extents, map, id);
+ extent = map_id_range_up_base(extents, map, id, count);
else
- extent = map_id_up_max(extents, map, id);
+ extent = map_id_range_up_max(extents, map, id, count);
/* Map the id or note failure */
if (extent)
@@ -395,6 +398,11 @@ u32 map_id_up(struct uid_gid_map *map, u32 id)
return id;
}
+u32 map_id_up(struct uid_gid_map *map, u32 id)
+{
+ return map_id_range_up(map, id, 1);
+}
+
/**
* make_kuid - Map a user-namespace uid pair into a kuid.
* @ns: User namespace that the uid is in
--
2.47.2
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH 2/4] statmount: allow to retrieve idmappings
2025-01-29 23:19 [PATCH 0/4] statmount: allow to retrieve idmappings Christian Brauner
2025-01-29 23:19 ` [PATCH 1/4] uidgid: add map_id_range_up() Christian Brauner
@ 2025-01-29 23:19 ` Christian Brauner
2025-01-30 12:37 ` Jeff Layton
2025-01-29 23:19 ` [PATCH 3/4] samples/vfs: check whether flag was raised Christian Brauner
` (2 subsequent siblings)
4 siblings, 1 reply; 10+ messages in thread
From: Christian Brauner @ 2025-01-29 23:19 UTC (permalink / raw)
To: linux-fsdevel
Cc: Josef Bacik, Jeff Layton, Lennart Poettering, Daan De Meyer,
Seth Forshee, Miklos Szeredi, Christian Brauner
This adds the STATMOUNT_MNT_UIDMAP and STATMOUNT_MNT_GIDMAP options.
It allows the retrieval of idmappings via statmount().
Currently it isn't possible to figure out what idmappings are applied to
an idmapped mount. This information is often crucial. Before statmount()
the only realistic options for an interface like this would have been to
add it to /proc/<pid>/fdinfo/<nr> or to expose it in
/proc/<pid>/mountinfo. Both solution would have been pretty ugly and
would've shown information that is of strong interest to some
application but not all. statmount() is perfect for this.
The idmappings applied to an idmapped mount are shown relative to the
caller's user namespace. This is the most useful solution that doesn't
risk leaking information or confuse the caller.
For example, an idmapped mount might have been created with the
following idmappings:
mount --bind -o X-mount.idmap="0:10000:1000 2000:2000:1 3000:3000:1" /srv /opt
Listing the idmappings through statmount() in the same context shows:
mnt_id: 2147485088
mnt_parent_id: 2147484816
fs_type: btrfs
mnt_root: /srv
mnt_point: /opt
mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
mnt_uidmap[0]: 0 10000 1000
mnt_uidmap[1]: 2000 2000 1
mnt_uidmap[2]: 3000 3000 1
mnt_gidmap[0]: 0 10000 1000
mnt_gidmap[1]: 2000 2000 1
mnt_gidmap[2]: 3000 3000 1
But the idmappings might not always be resolvablein the caller's user
namespace. For example:
unshare --user --map-root
In this case statmount() will indicate the failure to resolve the idmappings
in the caller's user namespace by listing 4294967295 aka (uid_t) -1 as
the target of the mapping while still showing the source and range of
the mapping:
mnt_id: 2147485087
mnt_parent_id: 2147484016
fs_type: btrfs
mnt_root: /srv
mnt_point: /opt
mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
mnt_uidmap[0]: 0 4294967295 1000
mnt_uidmap[1]: 2000 4294967295 1
mnt_uidmap[2]: 3000 4294967295 1
mnt_gidmap[0]: 0 4294967295 1000
mnt_gidmap[1]: 2000 4294967295 1
mnt_gidmap[2]: 3000 4294967295 1
Note that statmount() requires that the whole range must be resolvable
in the caller's user namespace. If a subrange fails to map it will still
list the map as not resolvable. This is a practical compromise to avoid
having to find which subranges are resovable and wich aren't.
Idmappings are listed as a string array with each mapping separated by
zero bytes. This allows to retrieve the idmappings and immediately use
them for writing to e.g., /proc/<pid>/{g,u}id_map and it also allow for
simple iteration like:
if (stmnt->mask & STATMOUNT_MNT_UIDMAP) {
const char *idmap = stmnt->str + stmnt->mnt_uidmap;
for (size_t idx = 0; idx < stmnt->mnt_uidmap_nr; idx++) {
printf("mnt_uidmap[%lu]: %s\n", idx, idmap);
idmap += strlen(idmap) + 1;
}
}
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
fs/internal.h | 1 +
fs/mnt_idmapping.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
fs/namespace.c | 43 +++++++++++++++++++++++++++++++++++++++-
include/uapi/linux/mount.h | 8 +++++++-
4 files changed, 99 insertions(+), 2 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index e7f02ae1e098..db6094d5cb0b 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -338,3 +338,4 @@ static inline bool path_mounted(const struct path *path)
return path->mnt->mnt_root == path->dentry;
}
void file_f_owner_release(struct file *file);
+int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map);
diff --git a/fs/mnt_idmapping.c b/fs/mnt_idmapping.c
index 7b1df8cc2821..4aca8e3ba97e 100644
--- a/fs/mnt_idmapping.c
+++ b/fs/mnt_idmapping.c
@@ -6,6 +6,7 @@
#include <linux/mnt_idmapping.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
+#include <linux/seq_file.h>
#include "internal.h"
@@ -334,3 +335,51 @@ void mnt_idmap_put(struct mnt_idmap *idmap)
free_mnt_idmap(idmap);
}
EXPORT_SYMBOL_GPL(mnt_idmap_put);
+
+int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map)
+{
+ struct uid_gid_map *map, *map_up;
+
+ if (idmap == &nop_mnt_idmap || idmap == &invalid_mnt_idmap)
+ return 0;
+
+ /*
+ * Idmappings are shown relative to the caller's idmapping.
+ * This is both the most intuitive and most useful solution.
+ */
+ if (uid_map) {
+ map = &idmap->uid_map;
+ map_up = ¤t_user_ns()->uid_map;
+ } else {
+ map = &idmap->gid_map;
+ map_up = ¤t_user_ns()->gid_map;
+ }
+
+ for (u32 idx = 0; idx < map->nr_extents; idx++) {
+ uid_t lower;
+ struct uid_gid_extent *extent;
+
+ if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
+ extent = &map->extent[idx];
+ else
+ extent = &map->forward[idx];
+
+ /*
+ * Verify that the whole range of the mapping can be
+ * resolved in the caller's idmapping. If it cannot be
+ * resolved 1/4294967295 will be shown as the target of
+ * the mapping. The source and range are shown as a hint
+ * to the caller.
+ */
+ lower = map_id_range_up(map_up, extent->lower_first, extent->count);
+ if (lower == (uid_t) -1)
+ seq_printf(seq, "%u %u %u", extent->first, -1, extent->count);
+ else
+ seq_printf(seq, "%u %u %u", extent->first, lower, extent->count);
+ seq->count++; /* mappings are separated by \0 */
+ if (seq_has_overflowed(seq))
+ return -EAGAIN;
+ }
+
+ return map->nr_extents;
+}
diff --git a/fs/namespace.c b/fs/namespace.c
index 4013fbac354a..535e4829061f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4915,6 +4915,7 @@ struct kstatmount {
struct statmount __user *buf;
size_t bufsize;
struct vfsmount *mnt;
+ struct mnt_idmap *idmap;
u64 mask;
struct path root;
struct statmount sm;
@@ -5185,6 +5186,30 @@ static int statmount_opt_sec_array(struct kstatmount *s, struct seq_file *seq)
return 0;
}
+static inline int statmount_mnt_uidmap(struct kstatmount *s, struct seq_file *seq)
+{
+ int ret;
+
+ ret = statmount_mnt_idmap(s->idmap, seq, true);
+ if (ret < 0)
+ return ret;
+
+ s->sm.mnt_uidmap_num = ret;
+ return 0;
+}
+
+static inline int statmount_mnt_gidmap(struct kstatmount *s, struct seq_file *seq)
+{
+ int ret;
+
+ ret = statmount_mnt_idmap(s->idmap, seq, false);
+ if (ret < 0)
+ return ret;
+
+ s->sm.mnt_gidmap_num = ret;
+ return 0;
+}
+
static int statmount_string(struct kstatmount *s, u64 flag)
{
int ret = 0;
@@ -5226,6 +5251,14 @@ static int statmount_string(struct kstatmount *s, u64 flag)
sm->sb_source = start;
ret = statmount_sb_source(s, seq);
break;
+ case STATMOUNT_MNT_UIDMAP:
+ sm->mnt_uidmap = start;
+ ret = statmount_mnt_uidmap(s, seq);
+ break;
+ case STATMOUNT_MNT_GIDMAP:
+ sm->mnt_gidmap = start;
+ ret = statmount_mnt_gidmap(s, seq);
+ break;
default:
WARN_ON_ONCE(true);
return -EINVAL;
@@ -5350,6 +5383,7 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
return err;
s->root = root;
+ s->idmap = mnt_idmap(s->mnt);
if (s->mask & STATMOUNT_SB_BASIC)
statmount_sb_basic(s);
@@ -5383,6 +5417,12 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
if (!err && s->mask & STATMOUNT_SB_SOURCE)
err = statmount_string(s, STATMOUNT_SB_SOURCE);
+ if (!err && s->mask & STATMOUNT_MNT_UIDMAP)
+ err = statmount_string(s, STATMOUNT_MNT_UIDMAP);
+
+ if (!err && s->mask & STATMOUNT_MNT_GIDMAP)
+ err = statmount_string(s, STATMOUNT_MNT_GIDMAP);
+
if (!err && s->mask & STATMOUNT_MNT_NS_ID)
statmount_mnt_ns_id(s, ns);
@@ -5406,7 +5446,8 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
#define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | \
STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE | \
- STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY)
+ STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY | \
+ STATMOUNT_MNT_UIDMAP | STATMOUNT_MNT_GIDMAP)
static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
struct statmount __user *buf, size_t bufsize,
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index c07008816aca..0be6ac4c1624 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -179,7 +179,11 @@ struct statmount {
__u32 opt_array; /* [str] Array of nul terminated fs options */
__u32 opt_sec_num; /* Number of security options */
__u32 opt_sec_array; /* [str] Array of nul terminated security options */
- __u64 __spare2[46];
+ __u32 mnt_uidmap_num; /* Number of uid mappings */
+ __u32 mnt_uidmap; /* [str] Array of uid mappings (as seen from callers namespace) */
+ __u32 mnt_gidmap_num; /* Number of gid mappings */
+ __u32 mnt_gidmap; /* [str] Array of gid mappings (as seen from callers namespace) */
+ __u64 __spare2[44];
char str[]; /* Variable size part containing strings */
};
@@ -217,6 +221,8 @@ struct mnt_id_req {
#define STATMOUNT_SB_SOURCE 0x00000200U /* Want/got sb_source */
#define STATMOUNT_OPT_ARRAY 0x00000400U /* Want/got opt_... */
#define STATMOUNT_OPT_SEC_ARRAY 0x00000800U /* Want/got opt_sec... */
+#define STATMOUNT_MNT_UIDMAP 0x00001000U /* Want/got uidmap... */
+#define STATMOUNT_MNT_GIDMAP 0x00002000U /* Want/got gidmap... */
/*
* Special @mnt_id values that can be passed to listmount
--
2.47.2
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 2/4] statmount: allow to retrieve idmappings
2025-01-29 23:19 ` [PATCH 2/4] statmount: allow to retrieve idmappings Christian Brauner
@ 2025-01-30 12:37 ` Jeff Layton
0 siblings, 0 replies; 10+ messages in thread
From: Jeff Layton @ 2025-01-30 12:37 UTC (permalink / raw)
To: Christian Brauner, linux-fsdevel
Cc: Josef Bacik, Lennart Poettering, Daan De Meyer, Seth Forshee,
Miklos Szeredi
On Thu, 2025-01-30 at 00:19 +0100, Christian Brauner wrote:
> This adds the STATMOUNT_MNT_UIDMAP and STATMOUNT_MNT_GIDMAP options.
> It allows the retrieval of idmappings via statmount().
>
> Currently it isn't possible to figure out what idmappings are applied to
> an idmapped mount. This information is often crucial. Before statmount()
> the only realistic options for an interface like this would have been to
> add it to /proc/<pid>/fdinfo/<nr> or to expose it in
> /proc/<pid>/mountinfo. Both solution would have been pretty ugly and
> would've shown information that is of strong interest to some
> application but not all. statmount() is perfect for this.
>
> The idmappings applied to an idmapped mount are shown relative to the
> caller's user namespace. This is the most useful solution that doesn't
> risk leaking information or confuse the caller.
>
> For example, an idmapped mount might have been created with the
> following idmappings:
>
> mount --bind -o X-mount.idmap="0:10000:1000 2000:2000:1 3000:3000:1" /srv /opt
>
> Listing the idmappings through statmount() in the same context shows:
>
> mnt_id: 2147485088
> mnt_parent_id: 2147484816
> fs_type: btrfs
> mnt_root: /srv
> mnt_point: /opt
> mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> mnt_uidmap[0]: 0 10000 1000
> mnt_uidmap[1]: 2000 2000 1
> mnt_uidmap[2]: 3000 3000 1
> mnt_gidmap[0]: 0 10000 1000
> mnt_gidmap[1]: 2000 2000 1
> mnt_gidmap[2]: 3000 3000 1
>
> But the idmappings might not always be resolvablein the caller's user
> namespace. For example:
>
> unshare --user --map-root
>
> In this case statmount() will indicate the failure to resolve the idmappings
> in the caller's user namespace by listing 4294967295 aka (uid_t) -1 as
> the target of the mapping while still showing the source and range of
> the mapping:
>
> mnt_id: 2147485087
> mnt_parent_id: 2147484016
> fs_type: btrfs
> mnt_root: /srv
> mnt_point: /opt
> mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> mnt_uidmap[0]: 0 4294967295 1000
> mnt_uidmap[1]: 2000 4294967295 1
> mnt_uidmap[2]: 3000 4294967295 1
> mnt_gidmap[0]: 0 4294967295 1000
> mnt_gidmap[1]: 2000 4294967295 1
> mnt_gidmap[2]: 3000 4294967295 1
>
> Note that statmount() requires that the whole range must be resolvable
> in the caller's user namespace. If a subrange fails to map it will still
> list the map as not resolvable. This is a practical compromise to avoid
> having to find which subranges are resovable and wich aren't.
>
> Idmappings are listed as a string array with each mapping separated by
> zero bytes. This allows to retrieve the idmappings and immediately use
> them for writing to e.g., /proc/<pid>/{g,u}id_map and it also allow for
> simple iteration like:
>
> if (stmnt->mask & STATMOUNT_MNT_UIDMAP) {
> const char *idmap = stmnt->str + stmnt->mnt_uidmap;
>
> for (size_t idx = 0; idx < stmnt->mnt_uidmap_nr; idx++) {
> printf("mnt_uidmap[%lu]: %s\n", idx, idmap);
> idmap += strlen(idmap) + 1;
> }
> }
>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> fs/internal.h | 1 +
> fs/mnt_idmapping.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++
> fs/namespace.c | 43 +++++++++++++++++++++++++++++++++++++++-
> include/uapi/linux/mount.h | 8 +++++++-
> 4 files changed, 99 insertions(+), 2 deletions(-)
>
> diff --git a/fs/internal.h b/fs/internal.h
> index e7f02ae1e098..db6094d5cb0b 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -338,3 +338,4 @@ static inline bool path_mounted(const struct path *path)
> return path->mnt->mnt_root == path->dentry;
> }
> void file_f_owner_release(struct file *file);
> +int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map);
> diff --git a/fs/mnt_idmapping.c b/fs/mnt_idmapping.c
> index 7b1df8cc2821..4aca8e3ba97e 100644
> --- a/fs/mnt_idmapping.c
> +++ b/fs/mnt_idmapping.c
> @@ -6,6 +6,7 @@
> #include <linux/mnt_idmapping.h>
> #include <linux/slab.h>
> #include <linux/user_namespace.h>
> +#include <linux/seq_file.h>
>
> #include "internal.h"
>
> @@ -334,3 +335,51 @@ void mnt_idmap_put(struct mnt_idmap *idmap)
> free_mnt_idmap(idmap);
> }
> EXPORT_SYMBOL_GPL(mnt_idmap_put);
> +
> +int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map)
> +{
> + struct uid_gid_map *map, *map_up;
> +
> + if (idmap == &nop_mnt_idmap || idmap == &invalid_mnt_idmap)
> + return 0;
> +
> + /*
> + * Idmappings are shown relative to the caller's idmapping.
> + * This is both the most intuitive and most useful solution.
> + */
> + if (uid_map) {
> + map = &idmap->uid_map;
> + map_up = ¤t_user_ns()->uid_map;
> + } else {
> + map = &idmap->gid_map;
> + map_up = ¤t_user_ns()->gid_map;
> + }
> +
> + for (u32 idx = 0; idx < map->nr_extents; idx++) {
> + uid_t lower;
> + struct uid_gid_extent *extent;
> +
> + if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
> + extent = &map->extent[idx];
> + else
> + extent = &map->forward[idx];
> +
> + /*
> + * Verify that the whole range of the mapping can be
> + * resolved in the caller's idmapping. If it cannot be
> + * resolved 1/4294967295 will be shown as the target of
nit: I think you mean '-1/4294967295'.
> + * the mapping. The source and range are shown as a hint
> + * to the caller.
> + */
> + lower = map_id_range_up(map_up, extent->lower_first, extent->count);
> + if (lower == (uid_t) -1)
> + seq_printf(seq, "%u %u %u", extent->first, -1, extent->count);
> + else
> + seq_printf(seq, "%u %u %u", extent->first, lower, extent->count);
Again, I think a different syntax for an unresolveable range would be
better. Another idea -- if you separate the fields by ':', you could
just leave out the middle field when it can't be resolved -- e.g.
"1000::1000".
> + seq->count++; /* mappings are separated by \0 */
> + if (seq_has_overflowed(seq))
> + return -EAGAIN;
> + }
> +
> + return map->nr_extents;
> +}
> diff --git a/fs/namespace.c b/fs/namespace.c
> index 4013fbac354a..535e4829061f 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -4915,6 +4915,7 @@ struct kstatmount {
> struct statmount __user *buf;
> size_t bufsize;
> struct vfsmount *mnt;
> + struct mnt_idmap *idmap;
> u64 mask;
> struct path root;
> struct statmount sm;
> @@ -5185,6 +5186,30 @@ static int statmount_opt_sec_array(struct kstatmount *s, struct seq_file *seq)
> return 0;
> }
>
> +static inline int statmount_mnt_uidmap(struct kstatmount *s, struct seq_file *seq)
> +{
> + int ret;
> +
> + ret = statmount_mnt_idmap(s->idmap, seq, true);
> + if (ret < 0)
> + return ret;
> +
> + s->sm.mnt_uidmap_num = ret;
> + return 0;
> +}
> +
> +static inline int statmount_mnt_gidmap(struct kstatmount *s, struct seq_file *seq)
> +{
> + int ret;
> +
> + ret = statmount_mnt_idmap(s->idmap, seq, false);
> + if (ret < 0)
> + return ret;
> +
> + s->sm.mnt_gidmap_num = ret;
> + return 0;
> +}
> +
> static int statmount_string(struct kstatmount *s, u64 flag)
> {
> int ret = 0;
> @@ -5226,6 +5251,14 @@ static int statmount_string(struct kstatmount *s, u64 flag)
> sm->sb_source = start;
> ret = statmount_sb_source(s, seq);
> break;
> + case STATMOUNT_MNT_UIDMAP:
> + sm->mnt_uidmap = start;
> + ret = statmount_mnt_uidmap(s, seq);
> + break;
> + case STATMOUNT_MNT_GIDMAP:
> + sm->mnt_gidmap = start;
> + ret = statmount_mnt_gidmap(s, seq);
> + break;
> default:
> WARN_ON_ONCE(true);
> return -EINVAL;
> @@ -5350,6 +5383,7 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
> return err;
>
> s->root = root;
> + s->idmap = mnt_idmap(s->mnt);
> if (s->mask & STATMOUNT_SB_BASIC)
> statmount_sb_basic(s);
>
> @@ -5383,6 +5417,12 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
> if (!err && s->mask & STATMOUNT_SB_SOURCE)
> err = statmount_string(s, STATMOUNT_SB_SOURCE);
>
> + if (!err && s->mask & STATMOUNT_MNT_UIDMAP)
> + err = statmount_string(s, STATMOUNT_MNT_UIDMAP);
> +
> + if (!err && s->mask & STATMOUNT_MNT_GIDMAP)
> + err = statmount_string(s, STATMOUNT_MNT_GIDMAP);
> +
> if (!err && s->mask & STATMOUNT_MNT_NS_ID)
> statmount_mnt_ns_id(s, ns);
>
> @@ -5406,7 +5446,8 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
> #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
> STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | \
> STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE | \
> - STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY)
> + STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY | \
> + STATMOUNT_MNT_UIDMAP | STATMOUNT_MNT_GIDMAP)
>
> static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
> struct statmount __user *buf, size_t bufsize,
> diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
> index c07008816aca..0be6ac4c1624 100644
> --- a/include/uapi/linux/mount.h
> +++ b/include/uapi/linux/mount.h
> @@ -179,7 +179,11 @@ struct statmount {
> __u32 opt_array; /* [str] Array of nul terminated fs options */
> __u32 opt_sec_num; /* Number of security options */
> __u32 opt_sec_array; /* [str] Array of nul terminated security options */
> - __u64 __spare2[46];
> + __u32 mnt_uidmap_num; /* Number of uid mappings */
> + __u32 mnt_uidmap; /* [str] Array of uid mappings (as seen from callers namespace) */
> + __u32 mnt_gidmap_num; /* Number of gid mappings */
> + __u32 mnt_gidmap; /* [str] Array of gid mappings (as seen from callers namespace) */
> + __u64 __spare2[44];
> char str[]; /* Variable size part containing strings */
> };
>
> @@ -217,6 +221,8 @@ struct mnt_id_req {
> #define STATMOUNT_SB_SOURCE 0x00000200U /* Want/got sb_source */
> #define STATMOUNT_OPT_ARRAY 0x00000400U /* Want/got opt_... */
> #define STATMOUNT_OPT_SEC_ARRAY 0x00000800U /* Want/got opt_sec... */
> +#define STATMOUNT_MNT_UIDMAP 0x00001000U /* Want/got uidmap... */
> +#define STATMOUNT_MNT_GIDMAP 0x00002000U /* Want/got gidmap... */
>
> /*
> * Special @mnt_id values that can be passed to listmount
>
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 3/4] samples/vfs: check whether flag was raised
2025-01-29 23:19 [PATCH 0/4] statmount: allow to retrieve idmappings Christian Brauner
2025-01-29 23:19 ` [PATCH 1/4] uidgid: add map_id_range_up() Christian Brauner
2025-01-29 23:19 ` [PATCH 2/4] statmount: allow to retrieve idmappings Christian Brauner
@ 2025-01-29 23:19 ` Christian Brauner
2025-01-30 8:45 ` Miklos Szeredi
2025-01-29 23:19 ` [PATCH 4/4] samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP Christian Brauner
2025-01-30 12:22 ` [PATCH 0/4] statmount: allow to retrieve idmappings Jeff Layton
4 siblings, 1 reply; 10+ messages in thread
From: Christian Brauner @ 2025-01-29 23:19 UTC (permalink / raw)
To: linux-fsdevel
Cc: Josef Bacik, Jeff Layton, Lennart Poettering, Daan De Meyer,
Seth Forshee, Miklos Szeredi, Christian Brauner
For string options the kernel will raise the corresponding flag only if
the string isn't empty. So check for the flag.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
samples/vfs/test-list-all-mounts.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/samples/vfs/test-list-all-mounts.c b/samples/vfs/test-list-all-mounts.c
index 1a02ea4593e3..ce272ded8a79 100644
--- a/samples/vfs/test-list-all-mounts.c
+++ b/samples/vfs/test-list-all-mounts.c
@@ -138,10 +138,11 @@ int main(int argc, char *argv[])
printf("mnt_id:\t\t%" PRIu64 "\nmnt_parent_id:\t%" PRIu64 "\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n\n",
(uint64_t)stmnt->mnt_id,
(uint64_t)stmnt->mnt_parent_id,
- stmnt->str + stmnt->fs_type,
- stmnt->str + stmnt->mnt_root,
- stmnt->str + stmnt->mnt_point,
- stmnt->str + stmnt->mnt_opts);
+ (stmnt->mask & STATMOUNT_FS_TYPE) ? stmnt->str + stmnt->fs_type : "",
+ (stmnt->mask & STATMOUNT_MNT_ROOT) ? stmnt->str + stmnt->mnt_root : "",
+ (stmnt->mask & STATMOUNT_MNT_POINT) ? stmnt->str + stmnt->mnt_point : "",
+ (stmnt->mask & STATMOUNT_MNT_OPTS) ? stmnt->str + stmnt->mnt_opts : "");
+
free(stmnt);
}
}
--
2.47.2
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 3/4] samples/vfs: check whether flag was raised
2025-01-29 23:19 ` [PATCH 3/4] samples/vfs: check whether flag was raised Christian Brauner
@ 2025-01-30 8:45 ` Miklos Szeredi
2025-01-30 15:17 ` Christian Brauner
0 siblings, 1 reply; 10+ messages in thread
From: Miklos Szeredi @ 2025-01-30 8:45 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Josef Bacik, Jeff Layton, Lennart Poettering,
Daan De Meyer, Seth Forshee
On Thu, 30 Jan 2025 at 00:20, Christian Brauner <brauner@kernel.org> wrote:
>
> For string options the kernel will raise the corresponding flag only if
> the string isn't empty. So check for the flag.
Hmm, seems like this is going to be a common mistake.
How about just putting a nul char at sm->str, which will solve this
once and for all (any unset str_ offset will point to this empty
string)?
Then we can say that instead of checking the mask for a string it's
okay to rely on it being empty by default.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 3/4] samples/vfs: check whether flag was raised
2025-01-30 8:45 ` Miklos Szeredi
@ 2025-01-30 15:17 ` Christian Brauner
0 siblings, 0 replies; 10+ messages in thread
From: Christian Brauner @ 2025-01-30 15:17 UTC (permalink / raw)
To: Miklos Szeredi
Cc: linux-fsdevel, Josef Bacik, Jeff Layton, Lennart Poettering,
Daan De Meyer, Seth Forshee
On Thu, Jan 30, 2025 at 09:45:50AM +0100, Miklos Szeredi wrote:
> On Thu, 30 Jan 2025 at 00:20, Christian Brauner <brauner@kernel.org> wrote:
> >
> > For string options the kernel will raise the corresponding flag only if
> > the string isn't empty. So check for the flag.
>
> Hmm, seems like this is going to be a common mistake.
Probably.
>
> How about just putting a nul char at sm->str, which will solve this
> once and for all (any unset str_ offset will point to this empty
> string)?
Agreed.
>
> Then we can say that instead of checking the mask for a string it's
> okay to rely on it being empty by default.
Right, but I do prefer checking whether the flag is raised or not. But
we can just support both.
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 4/4] samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP
2025-01-29 23:19 [PATCH 0/4] statmount: allow to retrieve idmappings Christian Brauner
` (2 preceding siblings ...)
2025-01-29 23:19 ` [PATCH 3/4] samples/vfs: check whether flag was raised Christian Brauner
@ 2025-01-29 23:19 ` Christian Brauner
2025-01-30 12:22 ` [PATCH 0/4] statmount: allow to retrieve idmappings Jeff Layton
4 siblings, 0 replies; 10+ messages in thread
From: Christian Brauner @ 2025-01-29 23:19 UTC (permalink / raw)
To: linux-fsdevel
Cc: Josef Bacik, Jeff Layton, Lennart Poettering, Daan De Meyer,
Seth Forshee, Miklos Szeredi, Christian Brauner
Illustrate how to use STATMOUNT_MNT_{G,U}IDMAP.
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
samples/vfs/samples-vfs.h | 14 +++++++++++++-
samples/vfs/test-list-all-mounts.c | 26 ++++++++++++++++++++++++--
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/samples/vfs/samples-vfs.h b/samples/vfs/samples-vfs.h
index 103e1e7c4cec..a7ff4e79e8e6 100644
--- a/samples/vfs/samples-vfs.h
+++ b/samples/vfs/samples-vfs.h
@@ -42,7 +42,11 @@ struct statmount {
__u32 opt_array; /* [str] Array of nul terminated fs options */
__u32 opt_sec_num; /* Number of security options */
__u32 opt_sec_array; /* [str] Array of nul terminated security options */
- __u64 __spare2[46];
+ __u32 mnt_uidmap_num; /* Number of uid mappings */
+ __u32 mnt_uidmap; /* [str] Array of uid mappings */
+ __u32 mnt_gidmap_num; /* Number of gid mappings */
+ __u32 mnt_gidmap; /* [str] Array of gid mappings */
+ __u64 __spare2[44];
char str[]; /* Variable size part containing strings */
};
@@ -158,6 +162,14 @@ struct mnt_ns_info {
#define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */
#endif
+#ifndef STATMOUNT_MNT_UIDMAP
+#define STATMOUNT_MNT_UIDMAP 0x00001000U /* Want/got uidmap... */
+#endif
+
+#ifndef STATMOUNT_MNT_GIDMAP
+#define STATMOUNT_MNT_GIDMAP 0x00002000U /* Want/got gidmap... */
+#endif
+
#ifndef MOUNT_ATTR_RDONLY
#define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */
#endif
diff --git a/samples/vfs/test-list-all-mounts.c b/samples/vfs/test-list-all-mounts.c
index ce272ded8a79..bb3b83d8f1d7 100644
--- a/samples/vfs/test-list-all-mounts.c
+++ b/samples/vfs/test-list-all-mounts.c
@@ -128,14 +128,16 @@ int main(int argc, char *argv[])
STATMOUNT_MNT_POINT |
STATMOUNT_MNT_NS_ID |
STATMOUNT_MNT_OPTS |
- STATMOUNT_FS_TYPE, 0);
+ STATMOUNT_FS_TYPE |
+ STATMOUNT_MNT_UIDMAP |
+ STATMOUNT_MNT_GIDMAP, 0);
if (!stmnt) {
printf("Failed to statmount(%" PRIu64 ") in mount namespace(%" PRIu64 ")\n",
(uint64_t)last_mnt_id, (uint64_t)info.mnt_ns_id);
continue;
}
- printf("mnt_id:\t\t%" PRIu64 "\nmnt_parent_id:\t%" PRIu64 "\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n\n",
+ printf("mnt_id:\t\t%" PRIu64 "\nmnt_parent_id:\t%" PRIu64 "\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n",
(uint64_t)stmnt->mnt_id,
(uint64_t)stmnt->mnt_parent_id,
(stmnt->mask & STATMOUNT_FS_TYPE) ? stmnt->str + stmnt->fs_type : "",
@@ -143,6 +145,26 @@ int main(int argc, char *argv[])
(stmnt->mask & STATMOUNT_MNT_POINT) ? stmnt->str + stmnt->mnt_point : "",
(stmnt->mask & STATMOUNT_MNT_OPTS) ? stmnt->str + stmnt->mnt_opts : "");
+ if (stmnt->mask & STATMOUNT_MNT_UIDMAP) {
+ const char *idmap = stmnt->str + stmnt->mnt_uidmap;
+
+ for (size_t idx = 0; idx < stmnt->mnt_uidmap_num; idx++) {
+ printf("mnt_uidmap[%lu]:\t%s\n", idx, idmap);
+ idmap += strlen(idmap) + 1;
+ }
+ }
+
+ if (stmnt->mask & STATMOUNT_MNT_GIDMAP) {
+ const char *idmap = stmnt->str + stmnt->mnt_gidmap;
+
+ for (size_t idx = 0; idx < stmnt->mnt_gidmap_num; idx++) {
+ printf("mnt_gidmap[%lu]:\t%s\n", idx, idmap);
+ idmap += strlen(idmap) + 1;
+ }
+ }
+
+ printf("\n");
+
free(stmnt);
}
}
--
2.47.2
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH 0/4] statmount: allow to retrieve idmappings
2025-01-29 23:19 [PATCH 0/4] statmount: allow to retrieve idmappings Christian Brauner
` (3 preceding siblings ...)
2025-01-29 23:19 ` [PATCH 4/4] samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP Christian Brauner
@ 2025-01-30 12:22 ` Jeff Layton
2025-01-30 15:16 ` Christian Brauner
4 siblings, 1 reply; 10+ messages in thread
From: Jeff Layton @ 2025-01-30 12:22 UTC (permalink / raw)
To: Christian Brauner, linux-fsdevel
Cc: Josef Bacik, Lennart Poettering, Daan De Meyer, Seth Forshee,
Miklos Szeredi
On Thu, 2025-01-30 at 00:19 +0100, Christian Brauner wrote:
> This adds the STATMOUNT_MNT_UIDMAP and STATMOUNT_MNT_GIDMAP options.
> It allows the retrieval of idmappings via statmount().
>
> Currently it isn't possible to figure out what idmappings are applied to
> an idmapped mount. This information is often crucial. Before statmount()
> the only realistic options for an interface like this would have been to
> add it to /proc/<pid>/fdinfo/<nr> or to expose it in
> /proc/<pid>/mountinfo. Both solution would have been pretty ugly and
> would've shown information that is of strong interest to some
> application but not all. statmount() is perfect for this.
>
> The idmappings applied to an idmapped mount are shown relative to the
> caller's user namespace. This is the most useful solution that doesn't
> risk leaking information or confuse the caller.
>
> For example, an idmapped mount might have been created with the
> following idmappings:
>
> mount --bind -o X-mount.idmap="0:10000:1000 2000:2000:1 3000:3000:1" /srv /opt
>
> Listing the idmappings through statmount() in the same context shows:
>
> mnt_id: 2147485088
> mnt_parent_id: 2147484816
> fs_type: btrfs
> mnt_root: /srv
> mnt_point: /opt
> mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> mnt_uidmap[0]: 0 10000 1000
> mnt_uidmap[1]: 2000 2000 1
> mnt_uidmap[2]: 3000 3000 1
> mnt_gidmap[0]: 0 10000 1000
> mnt_gidmap[1]: 2000 2000 1
> mnt_gidmap[2]: 3000 3000 1
>
nit: any reason not to separate the fields with ':' like the mount
option syntax?
> But the idmappings might not always be resolvablein the caller's user
> namespace. For example:
>
> unshare --user --map-root
>
> In this case statmount() will indicate the failure to resolve the idmappings
> in the caller's user namespace by listing 4294967295 aka (uid_t) -1 as
> the target of the mapping while still showing the source and range of
> the mapping:
>
> mnt_id: 2147485087
> mnt_parent_id: 2147484016
> fs_type: btrfs
> mnt_root: /srv
> mnt_point: /opt
> mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> mnt_uidmap[0]: 0 4294967295 1000
> mnt_uidmap[1]: 2000 4294967295 1
> mnt_uidmap[2]: 3000 4294967295 1
> mnt_gidmap[0]: 0 4294967295 1000
> mnt_gidmap[1]: 2000 4294967295 1
> mnt_gidmap[2]: 3000 4294967295 1
>
From a UI standpoint, this behavior is pretty ugly. What if we
(hypothetically) move to 64-bit uids one day? Maybe it'd be better to
note an inability to resolve with more distinct output? Like a '?'
instead of a -1 cast to unsigned?
If I can't resolve the range, maybe it'd be better to just not return
the info at all? Are the first and third fields of any value without
the second?
> Note that statmount() requires that the whole range must be resolvable
> in the caller's user namespace. If a subrange fails to map it will still
> list the map as not resolvable. This is a practical compromise to avoid
> having to find which subranges are resovable and wich aren't.
>
> Idmappings are listed as a string array with each mapping separated by
> zero bytes. This allows to retrieve the idmappings and immediately use
> them for writing to e.g., /proc/<pid>/{g,u}id_map and it also allow for
> simple iteration like:
>
> if (stmnt->mask & STATMOUNT_MNT_UIDMAP) {
> const char *idmap = stmnt->str + stmnt->mnt_uidmap;
>
> for (size_t idx = 0; idx < stmnt->mnt_uidmap_nr; idx++) {
> printf("mnt_uidmap[%lu]: %s\n", idx, idmap);
> idmap += strlen(idmap) + 1;
> }
> }
>
> Signed-off-by: Christian Brauner <brauner@kernel.org>
> ---
> Christian Brauner (4):
> uidgid: add map_id_range_up()
> statmount: allow to retrieve idmappings
> samples/vfs: check whether flag was raised
> samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP
>
> fs/internal.h | 1 +
> fs/mnt_idmapping.c | 49 ++++++++++++++++++++++++++++++++++++++
> fs/namespace.c | 43 ++++++++++++++++++++++++++++++++-
> include/linux/uidgid.h | 6 +++++
> include/uapi/linux/mount.h | 8 ++++++-
> kernel/user_namespace.c | 26 +++++++++++++-------
> samples/vfs/samples-vfs.h | 14 ++++++++++-
> samples/vfs/test-list-all-mounts.c | 35 ++++++++++++++++++++++-----
> 8 files changed, 164 insertions(+), 18 deletions(-)
> ---
> base-commit: 6d61a53dd6f55405ebcaea6ee38d1ab5a8856c2c
> change-id: 20250129-work-mnt_idmap-statmount-e57f258fef8e
>
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH 0/4] statmount: allow to retrieve idmappings
2025-01-30 12:22 ` [PATCH 0/4] statmount: allow to retrieve idmappings Jeff Layton
@ 2025-01-30 15:16 ` Christian Brauner
0 siblings, 0 replies; 10+ messages in thread
From: Christian Brauner @ 2025-01-30 15:16 UTC (permalink / raw)
To: Jeff Layton
Cc: linux-fsdevel, Josef Bacik, Lennart Poettering, Daan De Meyer,
Seth Forshee, Miklos Szeredi
On Thu, Jan 30, 2025 at 07:22:42AM -0500, Jeff Layton wrote:
> On Thu, 2025-01-30 at 00:19 +0100, Christian Brauner wrote:
> > This adds the STATMOUNT_MNT_UIDMAP and STATMOUNT_MNT_GIDMAP options.
> > It allows the retrieval of idmappings via statmount().
> >
> > Currently it isn't possible to figure out what idmappings are applied to
> > an idmapped mount. This information is often crucial. Before statmount()
> > the only realistic options for an interface like this would have been to
> > add it to /proc/<pid>/fdinfo/<nr> or to expose it in
> > /proc/<pid>/mountinfo. Both solution would have been pretty ugly and
> > would've shown information that is of strong interest to some
> > application but not all. statmount() is perfect for this.
> >
> > The idmappings applied to an idmapped mount are shown relative to the
> > caller's user namespace. This is the most useful solution that doesn't
> > risk leaking information or confuse the caller.
> >
> > For example, an idmapped mount might have been created with the
> > following idmappings:
> >
> > mount --bind -o X-mount.idmap="0:10000:1000 2000:2000:1 3000:3000:1" /srv /opt
> >
> > Listing the idmappings through statmount() in the same context shows:
> >
> > mnt_id: 2147485088
> > mnt_parent_id: 2147484816
> > fs_type: btrfs
> > mnt_root: /srv
> > mnt_point: /opt
> > mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> > mnt_uidmap[0]: 0 10000 1000
> > mnt_uidmap[1]: 2000 2000 1
> > mnt_uidmap[2]: 3000 3000 1
> > mnt_gidmap[0]: 0 10000 1000
> > mnt_gidmap[1]: 2000 2000 1
> > mnt_gidmap[2]: 3000 3000 1
> >
>
> nit: any reason not to separate the fields with ':' like the mount
> option syntax?
I followed the format of how idmappings are written and shown in
/proc/<PID>/{g,u}id_map.
>
> > But the idmappings might not always be resolvablein the caller's user
> > namespace. For example:
> >
> > unshare --user --map-root
> >
> > In this case statmount() will indicate the failure to resolve the idmappings
> > in the caller's user namespace by listing 4294967295 aka (uid_t) -1 as
> > the target of the mapping while still showing the source and range of
> > the mapping:
> >
> > mnt_id: 2147485087
> > mnt_parent_id: 2147484016
> > fs_type: btrfs
> > mnt_root: /srv
> > mnt_point: /opt
> > mnt_opts: ssd,discard=async,space_cache=v2,subvolid=5,subvol=/
> > mnt_uidmap[0]: 0 4294967295 1000
> > mnt_uidmap[1]: 2000 4294967295 1
> > mnt_uidmap[2]: 3000 4294967295 1
> > mnt_gidmap[0]: 0 4294967295 1000
> > mnt_gidmap[1]: 2000 4294967295 1
> > mnt_gidmap[2]: 3000 4294967295 1
> >
>
> From a UI standpoint, this behavior is pretty ugly. What if we
> (hypothetically) move to 64-bit uids one day? Maybe it'd be better to
> note an inability to resolve with more distinct output? Like a '?'
> instead of a -1 cast to unsigned?
>
> If I can't resolve the range, maybe it'd be better to just not return
> the info at all? Are the first and third fields of any value without
> the second?
That's an option but then users can only distinguish between no
idmapping and an empty idmapping by checking sm->mask for
STATMOUNT_MNT_{G,U}IDMAP. Which is probably fine. I'm just pointing it
out.
Leaving them out is probably a good idea rather than adding some special
syntax.
> > Note that statmount() requires that the whole range must be resolvable
> > in the caller's user namespace. If a subrange fails to map it will still
> > list the map as not resolvable. This is a practical compromise to avoid
> > having to find which subranges are resovable and wich aren't.
> >
> > Idmappings are listed as a string array with each mapping separated by
> > zero bytes. This allows to retrieve the idmappings and immediately use
> > them for writing to e.g., /proc/<pid>/{g,u}id_map and it also allow for
> > simple iteration like:
> >
> > if (stmnt->mask & STATMOUNT_MNT_UIDMAP) {
> > const char *idmap = stmnt->str + stmnt->mnt_uidmap;
> >
> > for (size_t idx = 0; idx < stmnt->mnt_uidmap_nr; idx++) {
> > printf("mnt_uidmap[%lu]: %s\n", idx, idmap);
> > idmap += strlen(idmap) + 1;
> > }
> > }
> >
> > Signed-off-by: Christian Brauner <brauner@kernel.org>
> > ---
> > Christian Brauner (4):
> > uidgid: add map_id_range_up()
> > statmount: allow to retrieve idmappings
> > samples/vfs: check whether flag was raised
> > samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP
> >
> > fs/internal.h | 1 +
> > fs/mnt_idmapping.c | 49 ++++++++++++++++++++++++++++++++++++++
> > fs/namespace.c | 43 ++++++++++++++++++++++++++++++++-
> > include/linux/uidgid.h | 6 +++++
> > include/uapi/linux/mount.h | 8 ++++++-
> > kernel/user_namespace.c | 26 +++++++++++++-------
> > samples/vfs/samples-vfs.h | 14 ++++++++++-
> > samples/vfs/test-list-all-mounts.c | 35 ++++++++++++++++++++++-----
> > 8 files changed, 164 insertions(+), 18 deletions(-)
> > ---
> > base-commit: 6d61a53dd6f55405ebcaea6ee38d1ab5a8856c2c
> > change-id: 20250129-work-mnt_idmap-statmount-e57f258fef8e
> >
>
> --
> Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 10+ messages in thread