[RFC v2 PATCH 5/8] VFS:userns: add helpers to shift UIDs and GIDs into on-disk view

From: Djalal Harouni
Date: Wed May 04 2016 - 10:31:34 EST


Add helpers to allow the VFS to shift UIDs and GIDs into on-disk view
according to the user namespace of the containing mount namespace.
All decisions are taken by VFS. This is a preparation patch for the next
one where we convert kuid and kgid to be written into disk.

To allow the shift of UID and GID, filesystems when mounted must set
"vfs_shift_uids" and "vfs_shift_gids" options, otherwise no shift is
performed at all.

vfs_shift_kuid_to_disk() and vfs_shift_kgid_to_disk() take two
arugments, the inode that we are trying to update on-disk and the
corresponding kuid and kgid that should be used to update
inode->{i_uid|i_gid} values. To convert to on-disk value we perform:

1) First check if UID/GID shift is enabled on the inode which means
the filesystem and current mount namespace.

2) If the passed kuid has a mapping in the user namespace of the
containing mount namespace, then get the correspondig uid_t value
otherwise we fallback to init_user_ns

3) Finally construct the kuid from the pair init_user_ns and uid_t
values, this will always reflects what should be put into
inode->{i_uid|i_gid} on the disk.

If a mount supports VFS UID/GID shifts shows up in a mount namespace
that allows UID/GID shifts, these helpers will handle the shift to
on-disk view and perform the translation accordingly.

Signed-off-by: Dongsu Park <dongsu@xxxxxxxxxxxx>
Signed-off-by: Djalal Harouni <tixxdz@xxxxxxxxxx>
---
fs/namespace.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fs.h | 3 ++
2 files changed, 96 insertions(+)

diff --git a/fs/namespace.c b/fs/namespace.c
index a8820fb..7df896b 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1726,6 +1726,99 @@ kgid_t vfs_shift_i_gid_to_virtual(const struct inode *inode)
return i_gid;
}

+/*
+ * Returns the on-disk UID view of the passed kuid that is supposed to be
+ * used to update the inode's uid.
+ * If UID shifts are enabled on the mount namespace and the filesystem,
+ * the VFS will return the on-disk view of the passed kuid. If no shift is
+ * performed, kuid is returned without any change.
+ */
+kuid_t vfs_shift_kuid_to_disk(const struct inode *inode, kuid_t kuid)
+{
+ uid_t uid;
+ struct mnt_namespace *ns;
+
+ if (!current->mm)
+ return kuid;
+
+ ns = current->nsproxy->mnt_ns;
+
+ /* Nothing to do */
+ if (!vfs_mount_shift_i_uid(ns, inode))
+ return kuid;
+
+ /*
+ * If kuid has a mapping in the mountns get its uid_t otherwise get it
+ * from init_user_ns
+ */
+ if (kuid_has_mapping(ns->user_ns, kuid))
+ uid = from_kuid(ns->user_ns, kuid);
+ else
+ uid = from_kuid(&init_user_ns, kuid);
+
+ if (uid == (uid_t) -1)
+ return kuid;
+
+ /* Get the final kuid that will be used for on-disk writes */
+ return make_kuid(&init_user_ns, uid);
+}
+
+/*
+ * Returns the on-disk GID view of the passed kgid that is supposed to be
+ * used to update the inode's gid.
+ * If GID shifts are enabled on the mount namespace and the filesystem,
+ * the VFS will return the on-disk view of the passed kgid. If no shift is
+ * performed, kgid is returned without any change.
+ */
+kgid_t vfs_shift_kgid_to_disk(const struct inode *inode, kgid_t kgid)
+{
+ gid_t gid;
+ struct mnt_namespace *ns;
+
+ if (!current->mm)
+ return kgid;
+
+ ns = current->nsproxy->mnt_ns;
+
+ /* Nothing to do ? */
+ if (!vfs_mount_shift_i_gid(ns, inode))
+ return kgid;
+
+ /*
+ * If kgid has a mapping in the mountns get its gid_t otherwise get it
+ * from init_user_ns
+ */
+ if (kgid_has_mapping(ns->user_ns, kgid))
+ gid = from_kgid(ns->user_ns, kgid);
+ else
+ gid = from_kgid(&init_user_ns, kgid);
+
+ if (gid == (gid_t) -1)
+ return kgid;
+
+ /* Get the final kgid that will be used for on-disk writes */
+ return make_kgid(&init_user_ns, gid);
+}
+
+/*
+ * Converts the passed kgid from on-disk view into the virtual one and returns it.
+ * It takes two arguments, the inode where the related kgid is supposed to be
+ * used, and the kgid.
+ */
+kgid_t vfs_kgid_disk_to_virtual(const struct inode *inode, kgid_t kgid)
+{
+ struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+
+ /* shift kgid if necessary */
+ if (!vfs_mount_shift_i_gid(ns, inode))
+ return kgid;
+
+ if (!kgid_has_mapping(ns->user_ns, kgid))
+ return make_kgid(ns->user_ns, kgid.val);
+
+ return kgid;
+}
+
static bool mnt_ns_loop(struct dentry *dentry)
{
/* Could bind mounting the mount namespace inode cause a
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a9efc5a..e957474 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1573,6 +1573,9 @@ extern void inode_init_owner(struct inode *inode, const struct inode *dir,
*/
extern kuid_t vfs_shift_i_uid_to_virtual(const struct inode *inode);
extern kgid_t vfs_shift_i_gid_to_virtual(const struct inode *inode);
+extern kgid_t vfs_kgid_disk_to_virtual(const struct inode *inode, kgid_t kgid);
+extern kuid_t vfs_shift_kuid_to_disk(const struct inode *inode, kuid_t kuid);
+extern kgid_t vfs_shift_kgid_to_disk(const struct inode *inode, kgid_t kgid);

/*
* VFS FS_IOC_FIEMAP helper definitions.
--
2.5.5