162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corporation, 2010 462306a36Sopenharmony_ci * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci#include <net/9p/9p.h> 1062306a36Sopenharmony_ci#include <net/9p/client.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/sched.h> 1362306a36Sopenharmony_ci#include <linux/posix_acl_xattr.h> 1462306a36Sopenharmony_ci#include "xattr.h" 1562306a36Sopenharmony_ci#include "acl.h" 1662306a36Sopenharmony_ci#include "v9fs.h" 1762306a36Sopenharmony_ci#include "v9fs_vfs.h" 1862306a36Sopenharmony_ci#include "fid.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci ssize_t size; 2362306a36Sopenharmony_ci void *value = NULL; 2462306a36Sopenharmony_ci struct posix_acl *acl = NULL; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci size = v9fs_fid_xattr_get(fid, name, NULL, 0); 2762306a36Sopenharmony_ci if (size < 0) 2862306a36Sopenharmony_ci return ERR_PTR(size); 2962306a36Sopenharmony_ci if (size == 0) 3062306a36Sopenharmony_ci return ERR_PTR(-ENODATA); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci value = kzalloc(size, GFP_NOFS); 3362306a36Sopenharmony_ci if (!value) 3462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci size = v9fs_fid_xattr_get(fid, name, value, size); 3762306a36Sopenharmony_ci if (size < 0) 3862306a36Sopenharmony_ci acl = ERR_PTR(size); 3962306a36Sopenharmony_ci else if (size == 0) 4062306a36Sopenharmony_ci acl = ERR_PTR(-ENODATA); 4162306a36Sopenharmony_ci else 4262306a36Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 4362306a36Sopenharmony_ci kfree(value); 4462306a36Sopenharmony_ci return acl; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct posix_acl *v9fs_acl_get(struct dentry *dentry, const char *name) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct p9_fid *fid; 5062306a36Sopenharmony_ci struct posix_acl *acl = NULL; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci fid = v9fs_fid_lookup(dentry); 5362306a36Sopenharmony_ci if (IS_ERR(fid)) 5462306a36Sopenharmony_ci return ERR_CAST(fid); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci acl = v9fs_fid_get_acl(fid, name); 5762306a36Sopenharmony_ci p9_fid_put(fid); 5862306a36Sopenharmony_ci return acl; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, const char *name) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci int retval; 6462306a36Sopenharmony_ci struct posix_acl *acl = NULL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci acl = v9fs_fid_get_acl(fid, name); 6762306a36Sopenharmony_ci if (!IS_ERR(acl)) 6862306a36Sopenharmony_ci return acl; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci retval = PTR_ERR(acl); 7162306a36Sopenharmony_ci if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP) 7262306a36Sopenharmony_ci return NULL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* map everything else to -EIO */ 7562306a36Sopenharmony_ci return ERR_PTR(-EIO); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciint v9fs_get_acl(struct inode *inode, struct p9_fid *fid) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int retval = 0; 8162306a36Sopenharmony_ci struct posix_acl *pacl, *dacl; 8262306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 8562306a36Sopenharmony_ci if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 8662306a36Sopenharmony_ci ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 8762306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); 8862306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci /* get the default/access acl values and cache them */ 9262306a36Sopenharmony_ci dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT); 9362306a36Sopenharmony_ci pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!IS_ERR(dacl) && !IS_ERR(pacl)) { 9662306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 9762306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); 9862306a36Sopenharmony_ci } else 9962306a36Sopenharmony_ci retval = -EIO; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (!IS_ERR(dacl)) 10262306a36Sopenharmony_ci posix_acl_release(dacl); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!IS_ERR(pacl)) 10562306a36Sopenharmony_ci posix_acl_release(pacl); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return retval; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct posix_acl *acl; 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * 9p Always cache the acl value when 11562306a36Sopenharmony_ci * instantiating the inode (v9fs_inode_from_fid) 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci acl = get_cached_acl(inode, type); 11862306a36Sopenharmony_ci BUG_ON(is_uncached_acl(acl)); 11962306a36Sopenharmony_ci return acl; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (rcu) 12762306a36Sopenharmony_ci return ERR_PTR(-ECHILD); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 13062306a36Sopenharmony_ci if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 13162306a36Sopenharmony_ci ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * On access = client and acl = on mode get the acl 13462306a36Sopenharmony_ci * values from the server 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci return NULL; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci return v9fs_get_cached_acl(inode, type); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistruct posix_acl *v9fs_iop_get_acl(struct mnt_idmap *idmap, 14362306a36Sopenharmony_ci struct dentry *dentry, int type) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 14862306a36Sopenharmony_ci /* We allow set/get/list of acl when access=client is not specified. */ 14962306a36Sopenharmony_ci if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) 15062306a36Sopenharmony_ci return v9fs_acl_get(dentry, posix_acl_xattr_name(type)); 15162306a36Sopenharmony_ci return v9fs_get_cached_acl(d_inode(dentry), type); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint v9fs_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 15562306a36Sopenharmony_ci struct posix_acl *acl, int type) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int retval; 15862306a36Sopenharmony_ci size_t size = 0; 15962306a36Sopenharmony_ci void *value = NULL; 16062306a36Sopenharmony_ci const char *acl_name; 16162306a36Sopenharmony_ci struct v9fs_session_info *v9ses; 16262306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (acl) { 16562306a36Sopenharmony_ci retval = posix_acl_valid(inode->i_sb->s_user_ns, acl); 16662306a36Sopenharmony_ci if (retval) 16762306a36Sopenharmony_ci goto err_out; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci value = kzalloc(size, GFP_NOFS); 17262306a36Sopenharmony_ci if (!value) { 17362306a36Sopenharmony_ci retval = -ENOMEM; 17462306a36Sopenharmony_ci goto err_out; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci retval = posix_acl_to_xattr(&init_user_ns, acl, value, size); 17862306a36Sopenharmony_ci if (retval < 0) 17962306a36Sopenharmony_ci goto err_out; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* 18362306a36Sopenharmony_ci * set the attribute on the remote. Without even looking at the 18462306a36Sopenharmony_ci * xattr value. We leave it to the server to validate 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci acl_name = posix_acl_xattr_name(type); 18762306a36Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 18862306a36Sopenharmony_ci if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { 18962306a36Sopenharmony_ci retval = v9fs_xattr_set(dentry, acl_name, value, size, 0); 19062306a36Sopenharmony_ci goto err_out; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) { 19462306a36Sopenharmony_ci retval = -EOPNOTSUPP; 19562306a36Sopenharmony_ci goto err_out; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) { 19962306a36Sopenharmony_ci retval = -EPERM; 20062306a36Sopenharmony_ci goto err_out; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci switch (type) { 20462306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 20562306a36Sopenharmony_ci if (acl) { 20662306a36Sopenharmony_ci struct iattr iattr = {}; 20762306a36Sopenharmony_ci struct posix_acl *acl_mode = acl; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci retval = posix_acl_update_mode(&nop_mnt_idmap, inode, 21062306a36Sopenharmony_ci &iattr.ia_mode, 21162306a36Sopenharmony_ci &acl_mode); 21262306a36Sopenharmony_ci if (retval) 21362306a36Sopenharmony_ci goto err_out; 21462306a36Sopenharmony_ci if (!acl_mode) { 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * ACL can be represented by the mode bits. 21762306a36Sopenharmony_ci * So don't update ACL below. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci kfree(value); 22062306a36Sopenharmony_ci value = NULL; 22162306a36Sopenharmony_ci size = 0; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci iattr.ia_valid = ATTR_MODE; 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * FIXME should we update ctime ? 22662306a36Sopenharmony_ci * What is the following setxattr update the mode ? 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci v9fs_vfs_setattr_dotl(&nop_mnt_idmap, dentry, &iattr); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 23262306a36Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 23362306a36Sopenharmony_ci retval = acl ? -EINVAL : 0; 23462306a36Sopenharmony_ci goto err_out; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci retval = v9fs_xattr_set(dentry, acl_name, value, size, 0); 24062306a36Sopenharmony_ci if (!retval) 24162306a36Sopenharmony_ci set_cached_acl(inode, type, acl); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cierr_out: 24462306a36Sopenharmony_ci kfree(value); 24562306a36Sopenharmony_ci return retval; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci int retval; 25162306a36Sopenharmony_ci char *name; 25262306a36Sopenharmony_ci size_t size; 25362306a36Sopenharmony_ci void *buffer; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!acl) 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Set a setxattr request to server */ 25962306a36Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 26062306a36Sopenharmony_ci buffer = kmalloc(size, GFP_KERNEL); 26162306a36Sopenharmony_ci if (!buffer) 26262306a36Sopenharmony_ci return -ENOMEM; 26362306a36Sopenharmony_ci retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 26462306a36Sopenharmony_ci if (retval < 0) 26562306a36Sopenharmony_ci goto err_free_out; 26662306a36Sopenharmony_ci switch (type) { 26762306a36Sopenharmony_ci case ACL_TYPE_ACCESS: 26862306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case ACL_TYPE_DEFAULT: 27162306a36Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci default: 27462306a36Sopenharmony_ci BUG(); 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0); 27762306a36Sopenharmony_cierr_free_out: 27862306a36Sopenharmony_ci kfree(buffer); 27962306a36Sopenharmony_ci return retval; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciint v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int retval = 0; 28562306a36Sopenharmony_ci struct posix_acl *acl; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 28862306a36Sopenharmony_ci return -EOPNOTSUPP; 28962306a36Sopenharmony_ci acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); 29062306a36Sopenharmony_ci if (acl) { 29162306a36Sopenharmony_ci retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); 29262306a36Sopenharmony_ci if (retval) 29362306a36Sopenharmony_ci return retval; 29462306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 29562306a36Sopenharmony_ci retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 29662306a36Sopenharmony_ci posix_acl_release(acl); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci return retval; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ciint v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid, 30262306a36Sopenharmony_ci struct posix_acl *dacl, struct posix_acl *acl) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 30562306a36Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 30662306a36Sopenharmony_ci v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl); 30762306a36Sopenharmony_ci v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_civoid v9fs_put_acl(struct posix_acl *dacl, 31262306a36Sopenharmony_ci struct posix_acl *acl) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci posix_acl_release(dacl); 31562306a36Sopenharmony_ci posix_acl_release(acl); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ciint v9fs_acl_mode(struct inode *dir, umode_t *modep, 31962306a36Sopenharmony_ci struct posix_acl **dpacl, struct posix_acl **pacl) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int retval = 0; 32262306a36Sopenharmony_ci umode_t mode = *modep; 32362306a36Sopenharmony_ci struct posix_acl *acl = NULL; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!S_ISLNK(mode)) { 32662306a36Sopenharmony_ci acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT); 32762306a36Sopenharmony_ci if (IS_ERR(acl)) 32862306a36Sopenharmony_ci return PTR_ERR(acl); 32962306a36Sopenharmony_ci if (!acl) 33062306a36Sopenharmony_ci mode &= ~current_umask(); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci if (acl) { 33362306a36Sopenharmony_ci if (S_ISDIR(mode)) 33462306a36Sopenharmony_ci *dpacl = posix_acl_dup(acl); 33562306a36Sopenharmony_ci retval = __posix_acl_create(&acl, GFP_NOFS, &mode); 33662306a36Sopenharmony_ci if (retval < 0) 33762306a36Sopenharmony_ci return retval; 33862306a36Sopenharmony_ci if (retval > 0) 33962306a36Sopenharmony_ci *pacl = acl; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci posix_acl_release(acl); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci *modep = mode; 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 346