@@ -1652,6 +1652,22 @@ SYSCALL_DEFINE1(oldumount, char __user *, name)
#endif
+/* Returns true if the VFS should shift inode's UID */
+static bool vfs_mount_shift_i_uid(struct mnt_namespace *ns,
+ const struct inode *inode)
+{
+ return (ns->flags & CLONE_MNTNS_SHIFT_UIDGID) &&
+ (inode->i_sb->s_iflags & SB_I_VFS_SHIFT_UIDS);
+}
+
+/* Returns true if the VFS should shift inode's GID */
+static bool vfs_mount_shift_i_gid(struct mnt_namespace *ns,
+ const struct inode *inode)
+{
+ return (ns->flags & CLONE_MNTNS_SHIFT_UIDGID) &&
+ (inode->i_sb->s_iflags & SB_I_VFS_SHIFT_GIDS);
+}
+
static bool is_mnt_ns_file(struct dentry *dentry)
{
/* Is this a proxy for a mount namespace? */
@@ -1664,6 +1680,52 @@ struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
return container_of(ns, struct mnt_namespace, ns);
}
+/*
+ * Returns the virtual UID view of the inode's uid
+ * If UID shifts are enabled on the mount namespace and the filesystem,
+ * the VFS will return the shifted UID according to the rules of the
+ * user namespace of the containing mount namespace. If no shift is
+ * performed, inode->i_uid is returned.
+ */
+kuid_t vfs_shift_i_uid_to_virtual(const struct inode *inode)
+{
+ kuid_t i_uid = inode->i_uid;
+ struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+
+ /* Nothing to do */
+ if (!vfs_mount_shift_i_uid(ns, inode))
+ return i_uid;
+
+ /* If there is no mapping construct one in the current mountns */
+ if (!kuid_has_mapping(ns->user_ns, i_uid))
+ return make_kuid(ns->user_ns, i_uid.val);
+
+ return i_uid;
+}
+
+/*
+ * Returns the virtual GID view of the inode's gid
+ * If GID shifts are enabled on the mount namespace and the filesystem,
+ * the VFS will return the shifted GID according to the rules of the
+ * user namespace of the containing mount namespace. If no shift is
+ * performed, inode->i_gid is returned.
+ */
+kgid_t vfs_shift_i_gid_to_virtual(const struct inode *inode)
+{
+ kgid_t i_gid = inode->i_gid;
+ struct mnt_namespace *ns = current->nsproxy->mnt_ns;
+
+ /* Nothing to do */
+ if (!vfs_mount_shift_i_gid(ns, inode))
+ return i_gid;
+
+ /* If there is no mapping construct one in the current mountns */
+ if (!kgid_has_mapping(ns->user_ns, i_gid))
+ return make_kgid(ns->user_ns, i_gid.val);
+
+ return i_gid;
+}
+
static bool mnt_ns_loop(struct dentry *dentry)
{
/* Could bind mounting the mount namespace inode cause a
@@ -24,8 +24,8 @@ void generic_fillattr(struct inode *inode, struct kstat *stat)
stat->ino = inode->i_ino;
stat->mode = inode->i_mode;
stat->nlink = inode->i_nlink;
- stat->uid = inode->i_uid;
- stat->gid = inode->i_gid;
+ stat->uid = vfs_shift_i_uid_to_virtual(inode);
+ stat->gid = vfs_shift_i_gid_to_virtual(inode);
stat->rdev = inode->i_rdev;
stat->size = i_size_read(inode);
stat->atime = inode->i_atime;
@@ -1298,6 +1298,8 @@ struct mm_struct;
/* sb->s_iflags */
#define SB_I_CGROUPWB 0x00000001 /* cgroup-aware writeback enabled */
#define SB_I_NOEXEC 0x00000002 /* Ignore executables on this fs */
+#define SB_I_VFS_SHIFT_UIDS 0X00000004 /* FS allows VFS to do UID shifts */
+#define SB_I_VFS_SHIFT_GIDS 0X00000008 /* FS allows VFS to do GID shifts */
/* Possible states of 'frozen' field */
enum {
@@ -1563,6 +1565,15 @@ extern int vfs_whiteout(struct inode *, struct dentry *);
*/
extern void inode_init_owner(struct inode *inode, const struct inode *dir,
umode_t mode);
+
+/*
+ * VFS helpers to shift inodes's uid/gid and passed values to either virtual
+ * or on-disk view. The shift is done according to rules of the user namespace
+ * of the containing mount namespace.
+ */
+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);
+
/*
* VFS FS_IOC_FIEMAP helper definitions.
*/