diff options
Diffstat (limited to 'fs/overlayfs')
| -rw-r--r-- | fs/overlayfs/copy_up.c | 4 | ||||
| -rw-r--r-- | fs/overlayfs/inode.c | 87 | ||||
| -rw-r--r-- | fs/overlayfs/overlayfs.h | 15 | 
3 files changed, 84 insertions, 22 deletions
| diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 714ec569d25b..245e2cb62708 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -331,8 +331,8 @@ int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry,  	if (!err) {  		struct iattr attr = {  			.ia_valid = ATTR_UID | ATTR_GID, -			.ia_uid = stat->uid, -			.ia_gid = stat->gid, +			.ia_vfsuid = VFSUIDT_INIT(stat->uid), +			.ia_vfsgid = VFSGIDT_INIT(stat->gid),  		};  		err = ovl_do_notify_change(ofs, upperdentry, &attr);  	} diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 492eddeb481f..7922b619f6c8 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -454,23 +454,94 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)  	return res;  } +/* + * Apply the idmapping of the layer to POSIX ACLs. The caller must pass a clone + * of the POSIX ACLs retrieved from the lower layer to this function to not + * alter the POSIX ACLs for the underlying filesystem. + */ +static void ovl_idmap_posix_acl(struct user_namespace *mnt_userns, +				struct posix_acl *acl) +{ +	for (unsigned int i = 0; i < acl->a_count; i++) { +		vfsuid_t vfsuid; +		vfsgid_t vfsgid; + +		struct posix_acl_entry *e = &acl->a_entries[i]; +		switch (e->e_tag) { +		case ACL_USER: +			vfsuid = make_vfsuid(mnt_userns, &init_user_ns, e->e_uid); +			e->e_uid = vfsuid_into_kuid(vfsuid); +			break; +		case ACL_GROUP: +			vfsgid = make_vfsgid(mnt_userns, &init_user_ns, e->e_gid); +			e->e_gid = vfsgid_into_kgid(vfsgid); +			break; +		} +	} +} + +/* + * When the relevant layer is an idmapped mount we need to take the idmapping + * of the layer into account and translate any ACL_{GROUP,USER} values + * according to the idmapped mount. + * + * We cannot alter the ACLs returned from the relevant layer as that would + * alter the cached values filesystem wide for the lower filesystem. Instead we + * can clone the ACLs and then apply the relevant idmapping of the layer. + * + * This is obviously only relevant when idmapped layers are used. + */  struct posix_acl *ovl_get_acl(struct inode *inode, int type, bool rcu)  {  	struct inode *realinode = ovl_inode_real(inode); -	const struct cred *old_cred; -	struct posix_acl *acl; +	struct posix_acl *acl, *clone; +	struct path realpath;  	if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))  		return NULL; -	if (rcu) -		return get_cached_acl_rcu(realinode, type); +	/* Careful in RCU walk mode */ +	ovl_i_path_real(inode, &realpath); +	if (!realpath.dentry) { +		WARN_ON(!rcu); +		return ERR_PTR(-ECHILD); +	} -	old_cred = ovl_override_creds(inode->i_sb); -	acl = get_acl(realinode, type); -	revert_creds(old_cred); +	if (rcu) { +		acl = get_cached_acl_rcu(realinode, type); +	} else { +		const struct cred *old_cred; + +		old_cred = ovl_override_creds(inode->i_sb); +		acl = get_acl(realinode, type); +		revert_creds(old_cred); +	} +	/* +	 * If there are no POSIX ACLs, or we encountered an error, +	 * or the layer isn't idmapped we don't need to do anything. +	 */ +	if (!is_idmapped_mnt(realpath.mnt) || IS_ERR_OR_NULL(acl)) +		return acl; -	return acl; +	/* +	 * We only get here if the layer is idmapped. So drop out of RCU path +	 * walk so we can clone the ACLs. There's no need to release the ACLs +	 * since get_cached_acl_rcu() doesn't take a reference on the ACLs. +	 */ +	if (rcu) +		return ERR_PTR(-ECHILD); + +	clone = posix_acl_clone(acl, GFP_KERNEL); +	if (!clone) +		clone = ERR_PTR(-ENOMEM); +	else +		ovl_idmap_posix_acl(mnt_user_ns(realpath.mnt), clone); +	/* +	 * Since we're not in RCU path walk we always need to release the +	 * original ACLs. +	 */ +	posix_acl_release(acl); +	return clone;  }  int ovl_update_time(struct inode *inode, struct timespec64 *ts, int flags) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 4f34b7e02eee..6ec815b84d48 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -139,17 +139,7 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs,  				       struct dentry *upperdentry,  				       struct iattr *attr)  { -	struct user_namespace *upper_mnt_userns = ovl_upper_mnt_userns(ofs); -	struct user_namespace *fs_userns = i_user_ns(d_inode(upperdentry)); - -	if (attr->ia_valid & ATTR_UID) -		attr->ia_uid = mapped_kuid_user(upper_mnt_userns, -						fs_userns, attr->ia_uid); -	if (attr->ia_valid & ATTR_GID) -		attr->ia_gid = mapped_kgid_user(upper_mnt_userns, -						fs_userns, attr->ia_gid); - -	return notify_change(upper_mnt_userns, upperdentry, attr, NULL); +	return notify_change(ovl_upper_mnt_userns(ofs), upperdentry, attr, NULL);  }  static inline int ovl_do_rmdir(struct ovl_fs *ofs, @@ -259,7 +249,8 @@ static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,  				  const char *name, const void *value,  				  size_t size, int flags)  { -	int err = vfs_setxattr(ovl_upper_mnt_userns(ofs), dentry, name, value, size, flags); +	int err = vfs_setxattr(ovl_upper_mnt_userns(ofs), dentry, name, +			       (void *)value, size, flags);  	pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, %d) = %i\n",  		 dentry, name, min((int)size, 48), value, size, flags, err); |