162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/attr.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 662306a36Sopenharmony_ci * changes by Thomas Schoebel-Theuer 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/export.h> 1062306a36Sopenharmony_ci#include <linux/time.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <linux/capability.h> 1562306a36Sopenharmony_ci#include <linux/fsnotify.h> 1662306a36Sopenharmony_ci#include <linux/fcntl.h> 1762306a36Sopenharmony_ci#include <linux/filelock.h> 1862306a36Sopenharmony_ci#include <linux/security.h> 1962306a36Sopenharmony_ci#include <linux/evm.h> 2062306a36Sopenharmony_ci#include <linux/ima.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "internal.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/** 2562306a36Sopenharmony_ci * setattr_should_drop_sgid - determine whether the setgid bit needs to be 2662306a36Sopenharmony_ci * removed 2762306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 2862306a36Sopenharmony_ci * @inode: inode to check 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * This function determines whether the setgid bit needs to be removed. 3162306a36Sopenharmony_ci * We retain backwards compatibility and require setgid bit to be removed 3262306a36Sopenharmony_ci * unconditionally if S_IXGRP is set. Otherwise we have the exact same 3362306a36Sopenharmony_ci * requirements as setattr_prepare() and setattr_copy(). 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * Return: ATTR_KILL_SGID if setgid bit needs to be removed, 0 otherwise. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ciint setattr_should_drop_sgid(struct mnt_idmap *idmap, 3862306a36Sopenharmony_ci const struct inode *inode) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci umode_t mode = inode->i_mode; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci if (!(mode & S_ISGID)) 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci if (mode & S_IXGRP) 4562306a36Sopenharmony_ci return ATTR_KILL_SGID; 4662306a36Sopenharmony_ci if (!in_group_or_capable(idmap, inode, i_gid_into_vfsgid(idmap, inode))) 4762306a36Sopenharmony_ci return ATTR_KILL_SGID; 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ciEXPORT_SYMBOL(setattr_should_drop_sgid); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * setattr_should_drop_suidgid - determine whether the set{g,u}id bit needs to 5462306a36Sopenharmony_ci * be dropped 5562306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 5662306a36Sopenharmony_ci * @inode: inode to check 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * This function determines whether the set{g,u}id bits need to be removed. 5962306a36Sopenharmony_ci * If the setuid bit needs to be removed ATTR_KILL_SUID is returned. If the 6062306a36Sopenharmony_ci * setgid bit needs to be removed ATTR_KILL_SGID is returned. If both 6162306a36Sopenharmony_ci * set{g,u}id bits need to be removed the corresponding mask of both flags is 6262306a36Sopenharmony_ci * returned. 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * Return: A mask of ATTR_KILL_S{G,U}ID indicating which - if any - setid bits 6562306a36Sopenharmony_ci * to remove, 0 otherwise. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ciint setattr_should_drop_suidgid(struct mnt_idmap *idmap, 6862306a36Sopenharmony_ci struct inode *inode) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci umode_t mode = inode->i_mode; 7162306a36Sopenharmony_ci int kill = 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* suid always must be killed */ 7462306a36Sopenharmony_ci if (unlikely(mode & S_ISUID)) 7562306a36Sopenharmony_ci kill = ATTR_KILL_SUID; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci kill |= setattr_should_drop_sgid(idmap, inode); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode))) 8062306a36Sopenharmony_ci return kill; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ciEXPORT_SYMBOL(setattr_should_drop_suidgid); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/** 8762306a36Sopenharmony_ci * chown_ok - verify permissions to chown inode 8862306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 8962306a36Sopenharmony_ci * @inode: inode to check permissions on 9062306a36Sopenharmony_ci * @ia_vfsuid: uid to chown @inode to 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 9362306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 9462306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 9562306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 9662306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic bool chown_ok(struct mnt_idmap *idmap, 9962306a36Sopenharmony_ci const struct inode *inode, vfsuid_t ia_vfsuid) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); 10262306a36Sopenharmony_ci if (vfsuid_eq_kuid(vfsuid, current_fsuid()) && 10362306a36Sopenharmony_ci vfsuid_eq(ia_vfsuid, vfsuid)) 10462306a36Sopenharmony_ci return true; 10562306a36Sopenharmony_ci if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN)) 10662306a36Sopenharmony_ci return true; 10762306a36Sopenharmony_ci if (!vfsuid_valid(vfsuid) && 10862306a36Sopenharmony_ci ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) 10962306a36Sopenharmony_ci return true; 11062306a36Sopenharmony_ci return false; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/** 11462306a36Sopenharmony_ci * chgrp_ok - verify permissions to chgrp inode 11562306a36Sopenharmony_ci * @idmap: idmap of the mount @inode was found from 11662306a36Sopenharmony_ci * @inode: inode to check permissions on 11762306a36Sopenharmony_ci * @ia_vfsgid: gid to chown @inode to 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 12062306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 12162306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 12262306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 12362306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic bool chgrp_ok(struct mnt_idmap *idmap, 12662306a36Sopenharmony_ci const struct inode *inode, vfsgid_t ia_vfsgid) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); 12962306a36Sopenharmony_ci vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); 13062306a36Sopenharmony_ci if (vfsuid_eq_kuid(vfsuid, current_fsuid())) { 13162306a36Sopenharmony_ci if (vfsgid_eq(ia_vfsgid, vfsgid)) 13262306a36Sopenharmony_ci return true; 13362306a36Sopenharmony_ci if (vfsgid_in_group_p(ia_vfsgid)) 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN)) 13762306a36Sopenharmony_ci return true; 13862306a36Sopenharmony_ci if (!vfsgid_valid(vfsgid) && 13962306a36Sopenharmony_ci ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN)) 14062306a36Sopenharmony_ci return true; 14162306a36Sopenharmony_ci return false; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/** 14562306a36Sopenharmony_ci * setattr_prepare - check if attribute changes to a dentry are allowed 14662306a36Sopenharmony_ci * @idmap: idmap of the mount the inode was found from 14762306a36Sopenharmony_ci * @dentry: dentry to check 14862306a36Sopenharmony_ci * @attr: attributes to change 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * Check if we are allowed to change the attributes contained in @attr 15162306a36Sopenharmony_ci * in the given dentry. This includes the normal unix access permission 15262306a36Sopenharmony_ci * checks, as well as checks for rlimits and others. The function also clears 15362306a36Sopenharmony_ci * SGID bit from mode if user is not allowed to set it. Also file capabilities 15462306a36Sopenharmony_ci * and IMA extended attributes are cleared if ATTR_KILL_PRIV is set. 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 15762306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 15862306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 15962306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 16062306a36Sopenharmony_ci * performed on the raw inode simply passs @nop_mnt_idmap. 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Should be called as the first thing in ->setattr implementations, 16362306a36Sopenharmony_ci * possibly after taking additional locks. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ciint setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, 16662306a36Sopenharmony_ci struct iattr *attr) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 16962306a36Sopenharmony_ci unsigned int ia_valid = attr->ia_valid; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* 17262306a36Sopenharmony_ci * First check size constraints. These can't be overriden using 17362306a36Sopenharmony_ci * ATTR_FORCE. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci if (ia_valid & ATTR_SIZE) { 17662306a36Sopenharmony_ci int error = inode_newsize_ok(inode, attr->ia_size); 17762306a36Sopenharmony_ci if (error) 17862306a36Sopenharmony_ci return error; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* If force is set do it anyway. */ 18262306a36Sopenharmony_ci if (ia_valid & ATTR_FORCE) 18362306a36Sopenharmony_ci goto kill_priv; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Make sure a caller can chown. */ 18662306a36Sopenharmony_ci if ((ia_valid & ATTR_UID) && 18762306a36Sopenharmony_ci !chown_ok(idmap, inode, attr->ia_vfsuid)) 18862306a36Sopenharmony_ci return -EPERM; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Make sure caller can chgrp. */ 19162306a36Sopenharmony_ci if ((ia_valid & ATTR_GID) && 19262306a36Sopenharmony_ci !chgrp_ok(idmap, inode, attr->ia_vfsgid)) 19362306a36Sopenharmony_ci return -EPERM; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Make sure a caller can chmod. */ 19662306a36Sopenharmony_ci if (ia_valid & ATTR_MODE) { 19762306a36Sopenharmony_ci vfsgid_t vfsgid; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 20062306a36Sopenharmony_ci return -EPERM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (ia_valid & ATTR_GID) 20362306a36Sopenharmony_ci vfsgid = attr->ia_vfsgid; 20462306a36Sopenharmony_ci else 20562306a36Sopenharmony_ci vfsgid = i_gid_into_vfsgid(idmap, inode); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Also check the setgid bit! */ 20862306a36Sopenharmony_ci if (!in_group_or_capable(idmap, inode, vfsgid)) 20962306a36Sopenharmony_ci attr->ia_mode &= ~S_ISGID; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Check for setting the inode time. */ 21362306a36Sopenharmony_ci if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { 21462306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 21562306a36Sopenharmony_ci return -EPERM; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cikill_priv: 21962306a36Sopenharmony_ci /* User has permission for the change */ 22062306a36Sopenharmony_ci if (ia_valid & ATTR_KILL_PRIV) { 22162306a36Sopenharmony_ci int error; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci error = security_inode_killpriv(idmap, dentry); 22462306a36Sopenharmony_ci if (error) 22562306a36Sopenharmony_ci return error; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ciEXPORT_SYMBOL(setattr_prepare); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/** 23362306a36Sopenharmony_ci * inode_newsize_ok - may this inode be truncated to a given size 23462306a36Sopenharmony_ci * @inode: the inode to be truncated 23562306a36Sopenharmony_ci * @offset: the new size to assign to the inode 23662306a36Sopenharmony_ci * 23762306a36Sopenharmony_ci * inode_newsize_ok must be called with i_mutex held. 23862306a36Sopenharmony_ci * 23962306a36Sopenharmony_ci * inode_newsize_ok will check filesystem limits and ulimits to check that the 24062306a36Sopenharmony_ci * new inode size is within limits. inode_newsize_ok will also send SIGXFSZ 24162306a36Sopenharmony_ci * when necessary. Caller must not proceed with inode size change if failure is 24262306a36Sopenharmony_ci * returned. @inode must be a file (not directory), with appropriate 24362306a36Sopenharmony_ci * permissions to allow truncate (inode_newsize_ok does NOT check these 24462306a36Sopenharmony_ci * conditions). 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Return: 0 on success, -ve errno on failure 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ciint inode_newsize_ok(const struct inode *inode, loff_t offset) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (offset < 0) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci if (inode->i_size < offset) { 25362306a36Sopenharmony_ci unsigned long limit; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci limit = rlimit(RLIMIT_FSIZE); 25662306a36Sopenharmony_ci if (limit != RLIM_INFINITY && offset > limit) 25762306a36Sopenharmony_ci goto out_sig; 25862306a36Sopenharmony_ci if (offset > inode->i_sb->s_maxbytes) 25962306a36Sopenharmony_ci goto out_big; 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * truncation of in-use swapfiles is disallowed - it would 26362306a36Sopenharmony_ci * cause subsequent swapout to scribble on the now-freed 26462306a36Sopenharmony_ci * blocks. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (IS_SWAPFILE(inode)) 26762306a36Sopenharmony_ci return -ETXTBSY; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ciout_sig: 27262306a36Sopenharmony_ci send_sig(SIGXFSZ, current, 0); 27362306a36Sopenharmony_ciout_big: 27462306a36Sopenharmony_ci return -EFBIG; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ciEXPORT_SYMBOL(inode_newsize_ok); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/** 27962306a36Sopenharmony_ci * setattr_copy - copy simple metadata updates into the generic inode 28062306a36Sopenharmony_ci * @idmap: idmap of the mount the inode was found from 28162306a36Sopenharmony_ci * @inode: the inode to be updated 28262306a36Sopenharmony_ci * @attr: the new attributes 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * setattr_copy must be called with i_mutex held. 28562306a36Sopenharmony_ci * 28662306a36Sopenharmony_ci * setattr_copy updates the inode's metadata with that specified 28762306a36Sopenharmony_ci * in attr on idmapped mounts. Necessary permission checks to determine 28862306a36Sopenharmony_ci * whether or not the S_ISGID property needs to be removed are performed with 28962306a36Sopenharmony_ci * the correct idmapped mount permission helpers. 29062306a36Sopenharmony_ci * Noticeably missing is inode size update, which is more complex 29162306a36Sopenharmony_ci * as it requires pagecache updates. 29262306a36Sopenharmony_ci * 29362306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 29462306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 29562306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 29662306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 29762306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap. 29862306a36Sopenharmony_ci * 29962306a36Sopenharmony_ci * The inode is not marked as dirty after this operation. The rationale is 30062306a36Sopenharmony_ci * that for "simple" filesystems, the struct inode is the inode storage. 30162306a36Sopenharmony_ci * The caller is free to mark the inode dirty afterwards if needed. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_civoid setattr_copy(struct mnt_idmap *idmap, struct inode *inode, 30462306a36Sopenharmony_ci const struct iattr *attr) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci unsigned int ia_valid = attr->ia_valid; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci i_uid_update(idmap, attr, inode); 30962306a36Sopenharmony_ci i_gid_update(idmap, attr, inode); 31062306a36Sopenharmony_ci if (ia_valid & ATTR_ATIME) 31162306a36Sopenharmony_ci inode->i_atime = attr->ia_atime; 31262306a36Sopenharmony_ci if (ia_valid & ATTR_MTIME) 31362306a36Sopenharmony_ci inode->i_mtime = attr->ia_mtime; 31462306a36Sopenharmony_ci if (ia_valid & ATTR_CTIME) 31562306a36Sopenharmony_ci inode_set_ctime_to_ts(inode, attr->ia_ctime); 31662306a36Sopenharmony_ci if (ia_valid & ATTR_MODE) { 31762306a36Sopenharmony_ci umode_t mode = attr->ia_mode; 31862306a36Sopenharmony_ci if (!in_group_or_capable(idmap, inode, 31962306a36Sopenharmony_ci i_gid_into_vfsgid(idmap, inode))) 32062306a36Sopenharmony_ci mode &= ~S_ISGID; 32162306a36Sopenharmony_ci inode->i_mode = mode; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ciEXPORT_SYMBOL(setattr_copy); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciint may_setattr(struct mnt_idmap *idmap, struct inode *inode, 32762306a36Sopenharmony_ci unsigned int ia_valid) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci int error; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) { 33262306a36Sopenharmony_ci if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) 33362306a36Sopenharmony_ci return -EPERM; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* 33762306a36Sopenharmony_ci * If utimes(2) and friends are called with times == NULL (or both 33862306a36Sopenharmony_ci * times are UTIME_NOW), then we need to check for write permission 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci if (ia_valid & ATTR_TOUCH) { 34162306a36Sopenharmony_ci if (IS_IMMUTABLE(inode)) 34262306a36Sopenharmony_ci return -EPERM; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) { 34562306a36Sopenharmony_ci error = inode_permission(idmap, inode, MAY_WRITE); 34662306a36Sopenharmony_ci if (error) 34762306a36Sopenharmony_ci return error; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci return 0; 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ciEXPORT_SYMBOL(may_setattr); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/** 35562306a36Sopenharmony_ci * notify_change - modify attributes of a filesytem object 35662306a36Sopenharmony_ci * @idmap: idmap of the mount the inode was found from 35762306a36Sopenharmony_ci * @dentry: object affected 35862306a36Sopenharmony_ci * @attr: new attributes 35962306a36Sopenharmony_ci * @delegated_inode: returns inode, if the inode is delegated 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * The caller must hold the i_mutex on the affected object. 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * If notify_change discovers a delegation in need of breaking, 36462306a36Sopenharmony_ci * it will return -EWOULDBLOCK and return a reference to the inode in 36562306a36Sopenharmony_ci * delegated_inode. The caller should then break the delegation and 36662306a36Sopenharmony_ci * retry. Because breaking a delegation may take a long time, the 36762306a36Sopenharmony_ci * caller should drop the i_mutex before doing so. 36862306a36Sopenharmony_ci * 36962306a36Sopenharmony_ci * Alternatively, a caller may pass NULL for delegated_inode. This may 37062306a36Sopenharmony_ci * be appropriate for callers that expect the underlying filesystem not 37162306a36Sopenharmony_ci * to be NFS exported. Also, passing NULL is fine for callers holding 37262306a36Sopenharmony_ci * the file open for write, as there can be no conflicting delegation in 37362306a36Sopenharmony_ci * that case. 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of 37662306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then 37762306a36Sopenharmony_ci * take care to map the inode according to @idmap before checking 37862306a36Sopenharmony_ci * permissions. On non-idmapped mounts or if permission checking is to be 37962306a36Sopenharmony_ci * performed on the raw inode simply pass @nop_mnt_idmap. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ciint notify_change(struct mnt_idmap *idmap, struct dentry *dentry, 38262306a36Sopenharmony_ci struct iattr *attr, struct inode **delegated_inode) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct inode *inode = dentry->d_inode; 38562306a36Sopenharmony_ci umode_t mode = inode->i_mode; 38662306a36Sopenharmony_ci int error; 38762306a36Sopenharmony_ci struct timespec64 now; 38862306a36Sopenharmony_ci unsigned int ia_valid = attr->ia_valid; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci WARN_ON_ONCE(!inode_is_locked(inode)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci error = may_setattr(idmap, inode, ia_valid); 39362306a36Sopenharmony_ci if (error) 39462306a36Sopenharmony_ci return error; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if ((ia_valid & ATTR_MODE)) { 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * Don't allow changing the mode of symlinks: 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * (1) The vfs doesn't take the mode of symlinks into account 40162306a36Sopenharmony_ci * during permission checking. 40262306a36Sopenharmony_ci * (2) This has never worked correctly. Most major filesystems 40362306a36Sopenharmony_ci * did return EOPNOTSUPP due to interactions with POSIX ACLs 40462306a36Sopenharmony_ci * but did still updated the mode of the symlink. 40562306a36Sopenharmony_ci * This inconsistency led system call wrapper providers such 40662306a36Sopenharmony_ci * as libc to block changing the mode of symlinks with 40762306a36Sopenharmony_ci * EOPNOTSUPP already. 40862306a36Sopenharmony_ci * (3) To even do this in the first place one would have to use 40962306a36Sopenharmony_ci * specific file descriptors and quite some effort. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 41262306a36Sopenharmony_ci return -EOPNOTSUPP; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Flag setting protected by i_mutex */ 41562306a36Sopenharmony_ci if (is_sxid(attr->ia_mode)) 41662306a36Sopenharmony_ci inode->i_flags &= ~S_NOSEC; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci now = current_time(inode); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci attr->ia_ctime = now; 42262306a36Sopenharmony_ci if (!(ia_valid & ATTR_ATIME_SET)) 42362306a36Sopenharmony_ci attr->ia_atime = now; 42462306a36Sopenharmony_ci else 42562306a36Sopenharmony_ci attr->ia_atime = timestamp_truncate(attr->ia_atime, inode); 42662306a36Sopenharmony_ci if (!(ia_valid & ATTR_MTIME_SET)) 42762306a36Sopenharmony_ci attr->ia_mtime = now; 42862306a36Sopenharmony_ci else 42962306a36Sopenharmony_ci attr->ia_mtime = timestamp_truncate(attr->ia_mtime, inode); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (ia_valid & ATTR_KILL_PRIV) { 43262306a36Sopenharmony_ci error = security_inode_need_killpriv(dentry); 43362306a36Sopenharmony_ci if (error < 0) 43462306a36Sopenharmony_ci return error; 43562306a36Sopenharmony_ci if (error == 0) 43662306a36Sopenharmony_ci ia_valid = attr->ia_valid &= ~ATTR_KILL_PRIV; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* 44062306a36Sopenharmony_ci * We now pass ATTR_KILL_S*ID to the lower level setattr function so 44162306a36Sopenharmony_ci * that the function has the ability to reinterpret a mode change 44262306a36Sopenharmony_ci * that's due to these bits. This adds an implicit restriction that 44362306a36Sopenharmony_ci * no function will ever call notify_change with both ATTR_MODE and 44462306a36Sopenharmony_ci * ATTR_KILL_S*ID set. 44562306a36Sopenharmony_ci */ 44662306a36Sopenharmony_ci if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) && 44762306a36Sopenharmony_ci (ia_valid & ATTR_MODE)) 44862306a36Sopenharmony_ci BUG(); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (ia_valid & ATTR_KILL_SUID) { 45162306a36Sopenharmony_ci if (mode & S_ISUID) { 45262306a36Sopenharmony_ci ia_valid = attr->ia_valid |= ATTR_MODE; 45362306a36Sopenharmony_ci attr->ia_mode = (inode->i_mode & ~S_ISUID); 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci if (ia_valid & ATTR_KILL_SGID) { 45762306a36Sopenharmony_ci if (mode & S_ISGID) { 45862306a36Sopenharmony_ci if (!(ia_valid & ATTR_MODE)) { 45962306a36Sopenharmony_ci ia_valid = attr->ia_valid |= ATTR_MODE; 46062306a36Sopenharmony_ci attr->ia_mode = inode->i_mode; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci attr->ia_mode &= ~S_ISGID; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID))) 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci /* 46962306a36Sopenharmony_ci * Verify that uid/gid changes are valid in the target 47062306a36Sopenharmony_ci * namespace of the superblock. 47162306a36Sopenharmony_ci */ 47262306a36Sopenharmony_ci if (ia_valid & ATTR_UID && 47362306a36Sopenharmony_ci !vfsuid_has_fsmapping(idmap, inode->i_sb->s_user_ns, 47462306a36Sopenharmony_ci attr->ia_vfsuid)) 47562306a36Sopenharmony_ci return -EOVERFLOW; 47662306a36Sopenharmony_ci if (ia_valid & ATTR_GID && 47762306a36Sopenharmony_ci !vfsgid_has_fsmapping(idmap, inode->i_sb->s_user_ns, 47862306a36Sopenharmony_ci attr->ia_vfsgid)) 47962306a36Sopenharmony_ci return -EOVERFLOW; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Don't allow modifications of files with invalid uids or 48262306a36Sopenharmony_ci * gids unless those uids & gids are being made valid. 48362306a36Sopenharmony_ci */ 48462306a36Sopenharmony_ci if (!(ia_valid & ATTR_UID) && 48562306a36Sopenharmony_ci !vfsuid_valid(i_uid_into_vfsuid(idmap, inode))) 48662306a36Sopenharmony_ci return -EOVERFLOW; 48762306a36Sopenharmony_ci if (!(ia_valid & ATTR_GID) && 48862306a36Sopenharmony_ci !vfsgid_valid(i_gid_into_vfsgid(idmap, inode))) 48962306a36Sopenharmony_ci return -EOVERFLOW; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci error = security_inode_setattr(idmap, dentry, attr); 49262306a36Sopenharmony_ci if (error) 49362306a36Sopenharmony_ci return error; 49462306a36Sopenharmony_ci error = try_break_deleg(inode, delegated_inode); 49562306a36Sopenharmony_ci if (error) 49662306a36Sopenharmony_ci return error; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (inode->i_op->setattr) 49962306a36Sopenharmony_ci error = inode->i_op->setattr(idmap, dentry, attr); 50062306a36Sopenharmony_ci else 50162306a36Sopenharmony_ci error = simple_setattr(idmap, dentry, attr); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!error) { 50462306a36Sopenharmony_ci fsnotify_change(dentry, ia_valid); 50562306a36Sopenharmony_ci ima_inode_post_setattr(idmap, dentry); 50662306a36Sopenharmony_ci evm_inode_post_setattr(dentry, ia_valid); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return error; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ciEXPORT_SYMBOL(notify_change); 512