A number of ownership checks made by the VFS make a number of assumptions: (1) that it is meaningful to compare inode->i_uid to a second ->i_uid or to current_fsuid(),
(2) that current_fsuid() represents the subject of the action, (3) that the number in ->i_uid belong to the system's ID space and (4) that the IDs can be represented by 32-bit integers. Network filesystems, however, may violate all four of these assumptions. Indeed, a network filesystem may not even have an actual concept of a UNIX integer UID (cifs without POSIX extensions, for example). Plug-in block filesystems (e.g. USB drives) may also violate this assumption. In particular, AFS implements its own ACL security model with its own per-cell user ID space with 64-bit IDs for some server variants. The subject is represented by a token in a key, not current_fsuid(). The AFS user IDs and the system user IDs for a cell may be numerically equivalent, but that's matter of administrative policy and should perhaps be noted in the cell definition or by mount option. A subsequent patch will address AFS. To help fix this, three functions are defined to perform UID comparison within the VFS: (1) vfs_inode_is_owned_by_me(). This defaults to comparing i_uid to current_fsuid(), with appropriate namespace mapping, assuming that the fsuid identifies the subject of the action. The filesystem may override it by implementing an inode op: int (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode); This should return 0 if owned, 1 if not or an error if there's some sort of lookup failure. It may use a means of identifying the subject of the action other than fsuid, for example by using an authentication token stored in a key. (2) vfs_inodes_have_same_owner(). This defaults to comparing the i_uids of two different inodes with appropriate namespace mapping. The filesystem may override it by implementing another inode op: int (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode1, struct inode *inode2); Again, this should return 0 if matching, 1 if not or an error if there's some sort of lookup failure. (3) vfs_inode_and_dir_have_same_owner(). This is similar to (2), but assumes that the second inode is the parent directory to the first and takes a nameidata struct instead of a second inode pointer. Fix a number of places within the VFS where such UID checks are made that should be deferring interpretation to the filesystem. (*) chown_ok() (*) chgrp_ok() Call vfs_inode_is_owned_by_me(). Possibly these need to defer all their checks to the network filesystem as the interpretation of the new UID/GID depends on the netfs too, but the ->setattr() method gets a chance to deal with that. (*) do_coredump() Call vfs_is_owned_by_me() to check that the file created is owned by the caller - but the check that's there might be sufficient. (*) inode_owner_or_capable() Call vfs_is_owned_by_me(). I'm not sure whether the namespace mapping makes sense in such a case, but it probably could be used. (*) vfs_setlease() Call vfs_is_owned_by_me(). Actually, it should query if leasing is permitted. Also, setting locks could perhaps do with a permission call to the filesystem driver as AFS, for example, has a lock permission bit in the ACL, but since the AFS server checks that when the RPC call is made, it's probably unnecessary. (*) acl_permission_check() (*) posix_acl_permission() Unchanged. These functions are only used by generic_permission() which is overridden if ->permission() is supplied, and when evaluating a POSIX ACL, it should arguably be checking the UID anyway. AFS, for example, implements its own ACLs and evaluates them in ->permission() and on the server. (*) may_follow_link() Call vfs_inode_and_dir_have_same_owner() and vfs_is_owned_by_me() on the the link and its parent dir. (*) may_create_in_sticky() Call vfs_is_owned_by_me() and also vfs_inode_and_dir_have_same_owner() both. [?] Should this return ok immediately if the open call we're in created the file being checked. (*) __check_sticky() Call vfs_is_owned_by_me() on both the dir and the inode, but for AFS vfs_is_owned_by_me() on a directory doesn't work, so call vfs_inodes_have_same_owner() instead to check the directory (as is done in may_create_in_sticky()). (*) may_dedupe_file() Call vfs_is_owned_by_me(). (*) IMA policy ops. Unchanged for now. I'm not sure what the best way to deal with this is - if, indeed, it needs any changes. Note that wrapping stuff up into vfs_inode_is_owned_by_me() isn't necessarily the most efficient as it means we may end up doing the uid idmapping an extra time - though this is only done in three places, all to do with world-writable sticky dir checks. Signed-off-by: David Howells <dhowe...@redhat.com> cc: Etienne Champetier <champetier.etie...@gmail.com> cc: Marc Dionne <marc.dio...@auristor.com> cc: Jeffrey Altman <jalt...@auristor.com> cc: Chet Ramey <chet.ra...@case.edu> cc: Cheyenne Wills <cwi...@sinenomine.net> cc: Alexander Viro <v...@zeniv.linux.org.uk> cc: Christian Brauner <brau...@kernel.org> cc: Steve French <sfre...@samba.org> cc: Mimi Zohar <zo...@linux.ibm.com> cc: linux-...@lists.infradead.org cc: openafs-de...@openafs.org cc: linux-c...@vger.kernel.org cc: linux-fsde...@vger.kernel.org cc: linux-integrity@vger.kernel.org Link: https://groups.google.com/g/gnu.bash.bug/c/6PPTfOgFdL4/m/2AQU-S1N76UJ Link: https://git.savannah.gnu.org/cgit/bash.git/tree/redir.c?h=bash-5.3-rc1#n733 --- Documentation/filesystems/vfs.rst | 23 ++++- fs/attr.c | 58 ++++++----- fs/coredump.c | 3 +- fs/inode.c | 11 +- fs/internal.h | 1 + fs/locks.c | 7 +- fs/namei.c | 161 ++++++++++++++++++++++++------ fs/remap_range.c | 20 ++-- include/linux/fs.h | 6 +- 9 files changed, 217 insertions(+), 73 deletions(-) diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst index fd32a9a17bfb..0e91e42d5e8a 100644 --- a/Documentation/filesystems/vfs.rst +++ b/Documentation/filesystems/vfs.rst @@ -517,7 +517,10 @@ As of kernel 2.6.22, the following members are defined: int (*fileattr_set)(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa); int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa); - struct offset_ctx *(*get_offset_ctx)(struct inode *inode); + struct offset_ctx *(*get_offset_ctx)(struct inode *inode); + int (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode); + int (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode, + struct dentry *dentry); }; Again, all methods are called without any locks being held, unless @@ -702,6 +705,24 @@ otherwise noted. filesystem must define this operation to use simple_offset_dir_operations. +``is_owned_by_me`` + called to determine if the file can be considered to be 'owned' by + the owner of the process or if the process has a token that grants + it ownership privileges. If unset, the default is to compare i_uid + to current_fsuid() - but this may give incorrect results for some + network or plug-in block filesystems. For example, AFS determines + ownership entirely according to an obtained token and i_uid may not + even be from the same ID space as current_uid(). + +``have_same_owner`` + called to determine if an inode has the same owner as its immediate + parent on the path walked. If unset, the default is to simply + compare the i_uid of both. For example, AFS compares the owner IDs + of both - but these are a 64-bit values on some variants that might + not fit into a kuid_t and cifs has GUIDs that cannot be compared to + kuid_t. + + The Address Space Object ======================== diff --git a/fs/attr.c b/fs/attr.c index 9caf63d20d03..03435fc0e0ec 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -16,6 +16,7 @@ #include <linux/fcntl.h> #include <linux/filelock.h> #include <linux/security.h> +#include "internal.h" /** * setattr_should_drop_sgid - determine whether the setgid bit needs to be @@ -91,19 +92,21 @@ EXPORT_SYMBOL(setattr_should_drop_suidgid); * permissions. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply pass @nop_mnt_idmap. */ -static bool chown_ok(struct mnt_idmap *idmap, - const struct inode *inode, vfsuid_t ia_vfsuid) +static int chown_ok(struct mnt_idmap *idmap, + struct inode *inode, vfsuid_t ia_vfsuid) { vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); - if (vfsuid_eq_kuid(vfsuid, current_fsuid()) && - vfsuid_eq(ia_vfsuid, vfsuid)) - return true; + int ret; + + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret <= 0) + return ret; if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN)) - return true; + return 0; if (!vfsuid_valid(vfsuid) && ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) - return true; - return false; + return 0; + return -EPERM; } /** @@ -118,23 +121,27 @@ static bool chown_ok(struct mnt_idmap *idmap, * permissions. On non-idmapped mounts or if permission checking is to be * performed on the raw inode simply pass @nop_mnt_idmap. */ -static bool chgrp_ok(struct mnt_idmap *idmap, - const struct inode *inode, vfsgid_t ia_vfsgid) +static int chgrp_ok(struct mnt_idmap *idmap, + struct inode *inode, vfsgid_t ia_vfsgid) { vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); - vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); - if (vfsuid_eq_kuid(vfsuid, current_fsuid())) { + int ret; + + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret < 0) + return ret; + if (ret == 0) { if (vfsgid_eq(ia_vfsgid, vfsgid)) - return true; + return 0; if (vfsgid_in_group_p(ia_vfsgid)) - return true; + return 0; } if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN)) - return true; + return 0; if (!vfsgid_valid(vfsgid) && ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) - return true; - return false; + return 0; + return -EPERM; } /** @@ -163,6 +170,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, { struct inode *inode = d_inode(dentry); unsigned int ia_valid = attr->ia_valid; + int ret; /* * First check size constraints. These can't be overriden using @@ -179,14 +187,18 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, goto kill_priv; /* Make sure a caller can chown. */ - if ((ia_valid & ATTR_UID) && - !chown_ok(idmap, inode, attr->ia_vfsuid)) - return -EPERM; + if (ia_valid & ATTR_UID) { + ret = chown_ok(idmap, inode, attr->ia_vfsuid); + if (ret < 0) + return ret; + } /* Make sure caller can chgrp. */ - if ((ia_valid & ATTR_GID) && - !chgrp_ok(idmap, inode, attr->ia_vfsgid)) - return -EPERM; + if (ia_valid & ATTR_GID) { + ret = chgrp_ok(idmap, inode, attr->ia_vfsgid); + if (ret < 0) + return ret; + } /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { diff --git a/fs/coredump.c b/fs/coredump.c index f217ebf2b3b6..f6d449907f92 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -774,8 +774,7 @@ void do_coredump(const kernel_siginfo_t *siginfo) * filesystem. */ idmap = file_mnt_idmap(cprm.file); - if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), - current_fsuid())) { + if (vfs_inode_is_owned_by_me(idmap, inode) != 0) { coredump_report_failure("Core dump to %s aborted: " "cannot preserve file owner", cn.corename); goto close_fail; diff --git a/fs/inode.c b/fs/inode.c index 99318b157a9a..8166c37ac3c6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2581,16 +2581,19 @@ EXPORT_SYMBOL(inode_init_owner); * On non-idmapped mounts or if permission checking is to be performed on the * raw inode simply pass @nop_mnt_idmap. */ -bool inode_owner_or_capable(struct mnt_idmap *idmap, - const struct inode *inode) +bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode) { vfsuid_t vfsuid; struct user_namespace *ns; + int ret; - vfsuid = i_uid_into_vfsuid(idmap, inode); - if (vfsuid_eq_kuid(vfsuid, current_fsuid())) + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret == 0) return true; + if (ret < 0) + return false; + vfsuid = i_uid_into_vfsuid(idmap, inode); ns = current_user_ns(); if (vfsuid_has_mapping(ns, vfsuid) && ns_capable(ns, CAP_FOWNER)) return true; diff --git a/fs/internal.h b/fs/internal.h index 393f6c5c24f6..9d8fb31c0404 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -52,6 +52,7 @@ extern int finish_clean_context(struct fs_context *fc); /* * namei.c */ +int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode); extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root); int do_rmdir(int dfd, struct filename *name); diff --git a/fs/locks.c b/fs/locks.c index 1619cddfa7a4..b7a2a3ab7529 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -68,6 +68,7 @@ #include <trace/events/filelock.h> #include <linux/uaccess.h> +#include "internal.h" static struct file_lock *file_lock(struct file_lock_core *flc) { @@ -2013,10 +2014,12 @@ int vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv) { struct inode *inode = file_inode(filp); - vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_idmap(filp), inode); int error; - if ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) && !capable(CAP_LEASE)) + error = vfs_inode_is_owned_by_me(file_mnt_idmap(filp), inode); + if (error < 0) + return error; + if (error != 0 && !capable(CAP_LEASE)) return -EACCES; if (!S_ISREG(inode->i_mode)) return -EINVAL; diff --git a/fs/namei.c b/fs/namei.c index c26a7ee42184..4f4fff2ee4e7 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -53,8 +53,8 @@ * The new code replaces the old recursive symlink resolution with * an iterative one (in case of non-nested symlink chains). It does * this with calls to <fs>_follow_link(). - * As a side effect, dir_namei(), _namei() and follow_link() are now - * replaced with a single function lookup_dentry() that can handle all + * As a side effect, dir_namei(), _namei() and follow_link() are now + * replaced with a single function lookup_dentry() that can handle all * the special cases of the former code. * * With the new dcache, the pathname is stored at each inode, at least as @@ -1149,6 +1149,72 @@ fs_initcall(init_fs_namei_sysctls); #endif /* CONFIG_SYSCTL */ +/* + * Determine if an inode is owned by the process (allowing for fsuid override), + * returning 0 if so, 1 if not and a negative error code if there was a problem + * making the determination. + */ +int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode) +{ + if (unlikely(inode->i_op->is_owned_by_me)) + return inode->i_op->is_owned_by_me(idmap, inode); + if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) + return 0; + return 1; /* Not same. */ +} + +/* + * Determine if an inode has the same owner as its parent, returning 0 if so, 1 + * if not and a negative error code if there was a problem making the + * determination. + */ +static int vfs_inode_and_dir_have_same_owner(struct mnt_idmap *idmap, struct inode *inode, + const struct nameidata *nd) +{ + if (unlikely(inode->i_op->have_same_owner)) { + struct dentry *parent; + struct inode *dir; + int ret; + + if (inode != nd->inode) { + dir = nd->inode; + ret = inode->i_op->have_same_owner(idmap, inode, dir); + } else if (nd->flags & LOOKUP_RCU) { + parent = READ_ONCE(nd->path.dentry); + dir = READ_ONCE(parent->d_inode); + if (!dir) + return -ECHILD; + ret = inode->i_op->have_same_owner(idmap, inode, dir); + } else { + parent = dget_parent(nd->path.dentry); + dir = parent->d_inode; + ret = inode->i_op->have_same_owner(idmap, inode, dir); + dput(parent); + } + return ret; + } + + if (vfsuid_valid(nd->dir_vfsuid) && + vfsuid_eq(i_uid_into_vfsuid(idmap, inode), nd->dir_vfsuid)) + return 0; + return 1; /* Not same. */ +} + +/* + * Determine if two inodes have the same owner, returning 0 if so, 1 if not and + * a negative error code if there was a problem making the determination. + */ +static int vfs_inodes_have_same_owner(struct mnt_idmap *idmap, struct inode *inode, + struct inode *dir) +{ + if (unlikely(inode->i_op->have_same_owner)) + return inode->i_op->have_same_owner(idmap, inode, dir); + if (vfsuid_eq(i_uid_into_vfsuid(idmap, inode), + i_uid_into_vfsuid(idmap, dir))) + return 0; + return 1; /* Not same. */ +} + /** * may_follow_link - Check symlink following for unsafe situations * @nd: nameidata pathwalk data @@ -1165,27 +1231,28 @@ fs_initcall(init_fs_namei_sysctls); * * Returns 0 if following the symlink is allowed, -ve on error. */ -static inline int may_follow_link(struct nameidata *nd, const struct inode *inode) +static inline int may_follow_link(struct nameidata *nd, struct inode *inode) { struct mnt_idmap *idmap; - vfsuid_t vfsuid; + int ret; if (!sysctl_protected_symlinks) return 0; - idmap = mnt_idmap(nd->path.mnt); - vfsuid = i_uid_into_vfsuid(idmap, inode); - /* Allowed if owner and follower match. */ - if (vfsuid_eq_kuid(vfsuid, current_fsuid())) - return 0; - /* Allowed if parent directory not sticky and world-writable. */ if ((nd->dir_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH)) return 0; + idmap = mnt_idmap(nd->path.mnt); + /* Allowed if owner and follower match. */ + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret <= 0) + return ret; + /* Allowed if parent directory and link owner match. */ - if (vfsuid_valid(nd->dir_vfsuid) && vfsuid_eq(nd->dir_vfsuid, vfsuid)) - return 0; + ret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd); + if (ret <= 0) + return ret; if (nd->flags & LOOKUP_RCU) return -ECHILD; @@ -1283,12 +1350,12 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link) * @inode: the inode of the file to open * * Block an O_CREAT open of a FIFO (or a regular file) when: - * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled - * - the file already exists - * - we are in a sticky directory - * - we don't own the file + * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled, + * - the file already exists, + * - we are in a sticky directory, + * - the directory is world writable, + * - we don't own the file and * - the owner of the directory doesn't own the file - * - the directory is world writable * If the sysctl_protected_fifos (or sysctl_protected_regular) is set to 2 * the directory doesn't have to be world writable: being group writable will * be enough. @@ -1299,13 +1366,45 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link) * On non-idmapped mounts or if permission checking is to be performed on the * raw inode simply pass @nop_mnt_idmap. * + * For a filesystem (e.g. a network filesystem) that has a separate ID space + * and has foreign IDs (maybe even non-integer IDs), i_uid cannot be compared + * to current_fsuid() and may not be directly comparable to another i_uid. + * Instead, the filesystem is asked to perform the comparisons. With network + * filesystems, there also exists the possibility of doing anonymous + * operations and having anonymously-owned objects. + * + * We have the following scenarios: + * + * USER DIR FILE FILE ALLOWED + * OWNER OWNER STATE + * ======= ======= ======= ======= ======= + * A A - New Yes + * A A A Exists Yes + * A A C Exists No + * A B - New Yes + * A B A Exists Yes, FO==U + * A B B Exists Yes, FO==DO + * A B C Exists No + * A anon[1] - New Yes + * A anon[1] A Exists Yes + * A anon[1] C Exists No + * anon A - New Yes + * anon A A Exists Yes, FO==DO + * anon anon[1] - New Yes + * anon anon[1] - Exists No + * anon A A Exists Yes, FO==DO + * anon A C Exists No + * anon A anon Exists No + * + * [1] Can anonymously-owned dirs be sticky? + * * Returns 0 if the open is allowed, -ve on error. */ static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd, - struct inode *const inode) + struct inode *inode) { umode_t dir_mode = nd->dir_mode; - vfsuid_t dir_vfsuid = nd->dir_vfsuid, i_vfsuid; + int ret; if (likely(!(dir_mode & S_ISVTX))) return 0; @@ -1316,13 +1415,13 @@ static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd, if (S_ISFIFO(inode->i_mode) && !sysctl_protected_fifos) return 0; - i_vfsuid = i_uid_into_vfsuid(idmap, inode); - - if (vfsuid_eq(i_vfsuid, dir_vfsuid)) - return 0; + ret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd); + if (ret <= 0) + return ret; - if (vfsuid_eq_kuid(i_vfsuid, current_fsuid())) - return 0; + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret <= 0) + return ret; if (likely(dir_mode & 0002)) { audit_log_path_denied(AUDIT_ANOM_CREAT, "sticky_create"); @@ -3143,12 +3242,14 @@ EXPORT_SYMBOL(user_path_at); int __check_sticky(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode) { - kuid_t fsuid = current_fsuid(); + int ret; - if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), fsuid)) - return 0; - if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, dir), fsuid)) - return 0; + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret <= 0) + return ret; + ret = vfs_inodes_have_same_owner(idmap, inode, dir); + if (ret <= 0) + return ret; return !capable_wrt_inode_uidgid(idmap, inode, CAP_FOWNER); } EXPORT_SYMBOL(__check_sticky); diff --git a/fs/remap_range.c b/fs/remap_range.c index 26afbbbfb10c..9eee93c27001 100644 --- a/fs/remap_range.c +++ b/fs/remap_range.c @@ -413,20 +413,22 @@ loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in, EXPORT_SYMBOL(vfs_clone_file_range); /* Check whether we are allowed to dedupe the destination file */ -static bool may_dedupe_file(struct file *file) +static int may_dedupe_file(struct file *file) { struct mnt_idmap *idmap = file_mnt_idmap(file); struct inode *inode = file_inode(file); + int ret; if (capable(CAP_SYS_ADMIN)) - return true; + return 0; if (file->f_mode & FMODE_WRITE) - return true; - if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) - return true; + return 0; + ret = vfs_inode_is_owned_by_me(idmap, inode); + if (ret <= 0) + return ret; if (!inode_permission(idmap, inode, MAY_WRITE)) - return true; - return false; + return 0; + return -EPERM; } loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos, @@ -459,8 +461,8 @@ loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos, if (ret) return ret; - ret = -EPERM; - if (!may_dedupe_file(dst_file)) + ret = may_dedupe_file(dst_file); + if (ret < 0) goto out_drop_write; ret = -EXDEV; diff --git a/include/linux/fs.h b/include/linux/fs.h index 040c0036320f..00e5a6fa2a64 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1983,8 +1983,7 @@ static inline bool sb_start_intwrite_trylock(struct super_block *sb) return __sb_start_write_trylock(sb, SB_FREEZE_FS); } -bool inode_owner_or_capable(struct mnt_idmap *idmap, - const struct inode *inode); +bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode); /* * VFS helper functions.. @@ -2259,6 +2258,9 @@ struct inode_operations { struct dentry *dentry, struct fileattr *fa); int (*fileattr_get)(struct dentry *dentry, struct fileattr *fa); struct offset_ctx *(*get_offset_ctx)(struct inode *inode); + int (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode); + int (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode1, + struct inode *inode2); } ____cacheline_aligned; /* Did the driver provide valid mmap hook configuration? */