Hey Bhavik! On 2026-03-17T17:48:33+0530, Bhavik Sachdev wrote: > In case of idmapped mounts and statmount(), three cases can occur: > > 1. The mount is not an idmapped mount. In this case, smbuf->mask will > *not* have STATMOUNT_MNT_{UID,GID}MAP set. > 2. The mount is an idmapped mount but *all* its mappings are *not* > resolvable in the user namespace of the caller. > In this case, smbuf->mask will have STATMOUNT_MNT_{UID,GID}MAP set > but smbuf->mnt_{uid,gid}map_num will be 0. > 3. The mount is an idmapped mount and *all* its mappings are resolvable > in the user namespace of the caller. > In this case, smbuf->mask will have STATMOUNT_MNT_{UID,GID}MAP set > and mbuf->mnt_{uid,gid}map_num will be greater than 0. > > The current documentation fails to differentiate between case 1 and 2 > and incorrectly states that STATMOUNT_MNT_{UID,GID}MAP will be set for > non-idmapped mounts. > > We can verify that the above is the case by looking at [1] and is made > explicitly clear by the comment in the implementation [2]. The case for > STATMOUNT_MNT_{UID,GID}MAP not being raised for a non-idmapped mount can > be verified by running this program [3]. > > [1]: > > [2]: > > [3]: Thanks for the example program! I'll paste it here, to avoid others having to go to github to read it. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include static int __statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask, struct statmount *stmnt, size_t bufsize, unsigned int flags) { struct mnt_id_req req = { .size = MNT_ID_REQ_SIZE_VER1, .mnt_id = mnt_id, .param = mask, .mnt_ns_id = mnt_ns_id, }; return syscall(__NR_statmount, &req, stmnt, bufsize, flags); } static struct statmount *sys_statmount(__u64 mnt_id, __u64 mnt_ns_id, __u64 mask, unsigned int flags) { size_t bufsize = 1 << 15; struct statmount *stmnt = NULL, *tmp = NULL; int ret; for (;;) { tmp = realloc(stmnt, bufsize); if (!tmp) goto out; stmnt = tmp; ret = __statmount(mnt_id, mnt_ns_id, mask, stmnt, bufsize, flags); if (!ret) return stmnt; if (errno != EOVERFLOW) goto out; bufsize <<= 1; if (bufsize >= UINT_MAX / 2) goto out; } out: free(stmnt); return NULL; } int main(void) { struct statmount *sm; struct statx stx; int fd = open("/tmp", O_PATH); if (fd < 0) { perror("open /tmp"); return 1; } memset(&stx, 0, sizeof(stx)); if (statx(fd, "", AT_EMPTY_PATH, STATX_MNT_ID_UNIQUE, &stx) < 0) { perror("statx"); close(fd); return 1; } if (!(stx.stx_mask & STATX_MNT_ID_UNIQUE)) { fprintf(stderr, "STATX_MNT_ID_UNIQUE not returned by kernel\n"); close(fd); return 1; } sm = sys_statmount(stx.stx_mnt_id, 0, STATMOUNT_MNT_BASIC | STATMOUNT_MNT_UIDMAP, 0); if (!sm) { close(fd); return 1; } if (sm->mask & STATMOUNT_MNT_UIDMAP) printf("this should not have happened.\n"); else printf("%llu is not an idmapped mount\n", sm->mnt_id); close(fd); return 0; } > > Signed-off-by: Bhavik Sachdev > --- > Hey Alex! > > My understanding of how statmount() differentiated between idmapped > mounts and non-idmapped mounts was incorrect. Thanks for the correction! > This patch fixes the > incorrect documentation introduced as a result. Thanks! Would you mind documenting all the commits that we're fixing? Please use Fixes tags. See and . > > Thanks, > Bhavik > > man/man2/statmount.2 | 6 ++++-- > 1 file changed, 4 insertions(+), 2 deletions(-) > > diff --git a/man/man2/statmount.2 b/man/man2/statmount.2 > index 42ca902d9..40a07181b 100644 > --- a/man/man2/statmount.2 > +++ b/man/man2/statmount.2 > @@ -356,7 +356,8 @@ .SS The returned information > If > .I smbuf.mask > has STATMOUNT_UIDMAP set and this field is 0, > -the mount is not an idmapped mount. > +then uid mappings applied on the mount cannot be resolved in the user namespace > +of the caller. Please use semantic newlines. See man-pages(7): $ MANWIDTH=72 man man-pages | awk '/Use semantic newlines/,/^$/' Use semantic newlines In the source of a manual page, new sentences should be started on new lines, long sentences should be split into lines at clause breaks (commas, semicolons, colons, and so on), and long clauses should be split at phrase boundaries. This convention, sometimes known as "semantic newlines", makes it easier to see the effect of patches, which often operate at the level of individual sentences, clauses, or phrases. In this case, I'd break the line between 'mount\ncannot'. Have a lovely night! Alex > .TP > .IR smbuf.mnt_uidmap " (since Linux 6.15)" > The offset to the location in the > @@ -372,7 +373,8 @@ .SS The returned information > If > .I smbuf.mask > has STATMOUNT_GIDMAP set and this field is 0, > -the mount is not an idmapped mount. > +then gid mappings applied on the mount cannot be resolved in the user namespace > +of the caller. > .TP > .IR smbuf.mnt_gidmap " (since Linux 6.15)" > The offset to the location in the > -- > 2.53.0 > > --