18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright IBM Corporation, 2010 38c2ecf20Sopenharmony_ci * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify it 68c2ecf20Sopenharmony_ci * under the terms of version 2.1 of the GNU Lesser General Public License 78c2ecf20Sopenharmony_ci * as published by the Free Software Foundation. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is distributed in the hope that it would be useful, but 108c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 118c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/fs.h> 178c2ecf20Sopenharmony_ci#include <net/9p/9p.h> 188c2ecf20Sopenharmony_ci#include <net/9p/client.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/posix_acl_xattr.h> 228c2ecf20Sopenharmony_ci#include "xattr.h" 238c2ecf20Sopenharmony_ci#include "acl.h" 248c2ecf20Sopenharmony_ci#include "v9fs.h" 258c2ecf20Sopenharmony_ci#include "v9fs_vfs.h" 268c2ecf20Sopenharmony_ci#include "fid.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci ssize_t size; 318c2ecf20Sopenharmony_ci void *value = NULL; 328c2ecf20Sopenharmony_ci struct posix_acl *acl = NULL; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci size = v9fs_fid_xattr_get(fid, name, NULL, 0); 358c2ecf20Sopenharmony_ci if (size > 0) { 368c2ecf20Sopenharmony_ci value = kzalloc(size, GFP_NOFS); 378c2ecf20Sopenharmony_ci if (!value) 388c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 398c2ecf20Sopenharmony_ci size = v9fs_fid_xattr_get(fid, name, value, size); 408c2ecf20Sopenharmony_ci if (size > 0) { 418c2ecf20Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 428c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 438c2ecf20Sopenharmony_ci goto err_out; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci } else if (size == -ENODATA || size == 0 || 468c2ecf20Sopenharmony_ci size == -ENOSYS || size == -EOPNOTSUPP) { 478c2ecf20Sopenharmony_ci acl = NULL; 488c2ecf20Sopenharmony_ci } else 498c2ecf20Sopenharmony_ci acl = ERR_PTR(-EIO); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cierr_out: 528c2ecf20Sopenharmony_ci kfree(value); 538c2ecf20Sopenharmony_ci return acl; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ciint v9fs_get_acl(struct inode *inode, struct p9_fid *fid) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int retval = 0; 598c2ecf20Sopenharmony_ci struct posix_acl *pacl, *dacl; 608c2ecf20Sopenharmony_ci struct v9fs_session_info *v9ses; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 638c2ecf20Sopenharmony_ci if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 648c2ecf20Sopenharmony_ci ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 658c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); 668c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci /* get the default/access acl values and cache them */ 708c2ecf20Sopenharmony_ci dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT); 718c2ecf20Sopenharmony_ci pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!IS_ERR(dacl) && !IS_ERR(pacl)) { 748c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 758c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); 768c2ecf20Sopenharmony_ci } else 778c2ecf20Sopenharmony_ci retval = -EIO; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (!IS_ERR(dacl)) 808c2ecf20Sopenharmony_ci posix_acl_release(dacl); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (!IS_ERR(pacl)) 838c2ecf20Sopenharmony_ci posix_acl_release(pacl); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return retval; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct posix_acl *acl; 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * 9p Always cache the acl value when 938c2ecf20Sopenharmony_ci * instantiating the inode (v9fs_inode_from_fid) 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci acl = get_cached_acl(inode, type); 968c2ecf20Sopenharmony_ci BUG_ON(is_uncached_acl(acl)); 978c2ecf20Sopenharmony_ci return acl; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct posix_acl *v9fs_iop_get_acl(struct inode *inode, int type) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct v9fs_session_info *v9ses; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci v9ses = v9fs_inode2v9ses(inode); 1058c2ecf20Sopenharmony_ci if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 1068c2ecf20Sopenharmony_ci ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 1078c2ecf20Sopenharmony_ci /* 1088c2ecf20Sopenharmony_ci * On access = client and acl = on mode get the acl 1098c2ecf20Sopenharmony_ci * values from the server 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci return NULL; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci return v9fs_get_cached_acl(inode, type); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int retval; 1208c2ecf20Sopenharmony_ci char *name; 1218c2ecf20Sopenharmony_ci size_t size; 1228c2ecf20Sopenharmony_ci void *buffer; 1238c2ecf20Sopenharmony_ci if (!acl) 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Set a setxattr request to server */ 1278c2ecf20Sopenharmony_ci size = posix_acl_xattr_size(acl->a_count); 1288c2ecf20Sopenharmony_ci buffer = kmalloc(size, GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (!buffer) 1308c2ecf20Sopenharmony_ci return -ENOMEM; 1318c2ecf20Sopenharmony_ci retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 1328c2ecf20Sopenharmony_ci if (retval < 0) 1338c2ecf20Sopenharmony_ci goto err_free_out; 1348c2ecf20Sopenharmony_ci switch (type) { 1358c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 1368c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_ACCESS; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 1398c2ecf20Sopenharmony_ci name = XATTR_NAME_POSIX_ACL_DEFAULT; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci default: 1428c2ecf20Sopenharmony_ci BUG(); 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0); 1458c2ecf20Sopenharmony_cierr_free_out: 1468c2ecf20Sopenharmony_ci kfree(buffer); 1478c2ecf20Sopenharmony_ci return retval; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ciint v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int retval = 0; 1538c2ecf20Sopenharmony_ci struct posix_acl *acl; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 1568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1578c2ecf20Sopenharmony_ci acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); 1588c2ecf20Sopenharmony_ci if (acl) { 1598c2ecf20Sopenharmony_ci retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); 1608c2ecf20Sopenharmony_ci if (retval) 1618c2ecf20Sopenharmony_ci return retval; 1628c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 1638c2ecf20Sopenharmony_ci retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 1648c2ecf20Sopenharmony_ci posix_acl_release(acl); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci return retval; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciint v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid, 1708c2ecf20Sopenharmony_ci struct posix_acl *dacl, struct posix_acl *acl) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 1738c2ecf20Sopenharmony_ci set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 1748c2ecf20Sopenharmony_ci v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl); 1758c2ecf20Sopenharmony_ci v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_civoid v9fs_put_acl(struct posix_acl *dacl, 1808c2ecf20Sopenharmony_ci struct posix_acl *acl) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci posix_acl_release(dacl); 1838c2ecf20Sopenharmony_ci posix_acl_release(acl); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ciint v9fs_acl_mode(struct inode *dir, umode_t *modep, 1878c2ecf20Sopenharmony_ci struct posix_acl **dpacl, struct posix_acl **pacl) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int retval = 0; 1908c2ecf20Sopenharmony_ci umode_t mode = *modep; 1918c2ecf20Sopenharmony_ci struct posix_acl *acl = NULL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!S_ISLNK(mode)) { 1948c2ecf20Sopenharmony_ci acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT); 1958c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 1968c2ecf20Sopenharmony_ci return PTR_ERR(acl); 1978c2ecf20Sopenharmony_ci if (!acl) 1988c2ecf20Sopenharmony_ci mode &= ~current_umask(); 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci if (acl) { 2018c2ecf20Sopenharmony_ci if (S_ISDIR(mode)) 2028c2ecf20Sopenharmony_ci *dpacl = posix_acl_dup(acl); 2038c2ecf20Sopenharmony_ci retval = __posix_acl_create(&acl, GFP_NOFS, &mode); 2048c2ecf20Sopenharmony_ci if (retval < 0) 2058c2ecf20Sopenharmony_ci return retval; 2068c2ecf20Sopenharmony_ci if (retval > 0) 2078c2ecf20Sopenharmony_ci *pacl = acl; 2088c2ecf20Sopenharmony_ci else 2098c2ecf20Sopenharmony_ci posix_acl_release(acl); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci *modep = mode; 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int v9fs_xattr_get_acl(const struct xattr_handler *handler, 2168c2ecf20Sopenharmony_ci struct dentry *dentry, struct inode *inode, 2178c2ecf20Sopenharmony_ci const char *name, void *buffer, size_t size) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct v9fs_session_info *v9ses; 2208c2ecf20Sopenharmony_ci struct posix_acl *acl; 2218c2ecf20Sopenharmony_ci int error; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * We allow set/get/list of acl when access=client is not specified 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) 2288c2ecf20Sopenharmony_ci return v9fs_xattr_get(dentry, handler->name, buffer, size); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci acl = v9fs_get_cached_acl(inode, handler->flags); 2318c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 2328c2ecf20Sopenharmony_ci return PTR_ERR(acl); 2338c2ecf20Sopenharmony_ci if (acl == NULL) 2348c2ecf20Sopenharmony_ci return -ENODATA; 2358c2ecf20Sopenharmony_ci error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 2368c2ecf20Sopenharmony_ci posix_acl_release(acl); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return error; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int v9fs_xattr_set_acl(const struct xattr_handler *handler, 2428c2ecf20Sopenharmony_ci struct dentry *dentry, struct inode *inode, 2438c2ecf20Sopenharmony_ci const char *name, const void *value, 2448c2ecf20Sopenharmony_ci size_t size, int flags) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int retval; 2478c2ecf20Sopenharmony_ci struct posix_acl *acl; 2488c2ecf20Sopenharmony_ci struct v9fs_session_info *v9ses; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci v9ses = v9fs_dentry2v9ses(dentry); 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * set the attribute on the remote. Without even looking at the 2538c2ecf20Sopenharmony_ci * xattr value. We leave it to the server to validate 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) 2568c2ecf20Sopenharmony_ci return v9fs_xattr_set(dentry, handler->name, value, size, 2578c2ecf20Sopenharmony_ci flags); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (S_ISLNK(inode->i_mode)) 2608c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2618c2ecf20Sopenharmony_ci if (!inode_owner_or_capable(inode)) 2628c2ecf20Sopenharmony_ci return -EPERM; 2638c2ecf20Sopenharmony_ci if (value) { 2648c2ecf20Sopenharmony_ci /* update the cached acl value */ 2658c2ecf20Sopenharmony_ci acl = posix_acl_from_xattr(&init_user_ns, value, size); 2668c2ecf20Sopenharmony_ci if (IS_ERR(acl)) 2678c2ecf20Sopenharmony_ci return PTR_ERR(acl); 2688c2ecf20Sopenharmony_ci else if (acl) { 2698c2ecf20Sopenharmony_ci retval = posix_acl_valid(inode->i_sb->s_user_ns, acl); 2708c2ecf20Sopenharmony_ci if (retval) 2718c2ecf20Sopenharmony_ci goto err_out; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } else 2748c2ecf20Sopenharmony_ci acl = NULL; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci switch (handler->flags) { 2778c2ecf20Sopenharmony_ci case ACL_TYPE_ACCESS: 2788c2ecf20Sopenharmony_ci if (acl) { 2798c2ecf20Sopenharmony_ci struct iattr iattr = { 0 }; 2808c2ecf20Sopenharmony_ci struct posix_acl *old_acl = acl; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci retval = posix_acl_update_mode(inode, &iattr.ia_mode, &acl); 2838c2ecf20Sopenharmony_ci if (retval) 2848c2ecf20Sopenharmony_ci goto err_out; 2858c2ecf20Sopenharmony_ci if (!acl) { 2868c2ecf20Sopenharmony_ci /* 2878c2ecf20Sopenharmony_ci * ACL can be represented 2888c2ecf20Sopenharmony_ci * by the mode bits. So don't 2898c2ecf20Sopenharmony_ci * update ACL. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci posix_acl_release(old_acl); 2928c2ecf20Sopenharmony_ci value = NULL; 2938c2ecf20Sopenharmony_ci size = 0; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci iattr.ia_valid = ATTR_MODE; 2968c2ecf20Sopenharmony_ci /* FIXME should we update ctime ? 2978c2ecf20Sopenharmony_ci * What is the following setxattr update the 2988c2ecf20Sopenharmony_ci * mode ? 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci v9fs_vfs_setattr_dotl(dentry, &iattr); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case ACL_TYPE_DEFAULT: 3048c2ecf20Sopenharmony_ci if (!S_ISDIR(inode->i_mode)) { 3058c2ecf20Sopenharmony_ci retval = acl ? -EINVAL : 0; 3068c2ecf20Sopenharmony_ci goto err_out; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci default: 3108c2ecf20Sopenharmony_ci BUG(); 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci retval = v9fs_xattr_set(dentry, handler->name, value, size, flags); 3138c2ecf20Sopenharmony_ci if (!retval) 3148c2ecf20Sopenharmony_ci set_cached_acl(inode, handler->flags, acl); 3158c2ecf20Sopenharmony_cierr_out: 3168c2ecf20Sopenharmony_ci posix_acl_release(acl); 3178c2ecf20Sopenharmony_ci return retval; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ciconst struct xattr_handler v9fs_xattr_acl_access_handler = { 3218c2ecf20Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_ACCESS, 3228c2ecf20Sopenharmony_ci .flags = ACL_TYPE_ACCESS, 3238c2ecf20Sopenharmony_ci .get = v9fs_xattr_get_acl, 3248c2ecf20Sopenharmony_ci .set = v9fs_xattr_set_acl, 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciconst struct xattr_handler v9fs_xattr_acl_default_handler = { 3288c2ecf20Sopenharmony_ci .name = XATTR_NAME_POSIX_ACL_DEFAULT, 3298c2ecf20Sopenharmony_ci .flags = ACL_TYPE_DEFAULT, 3308c2ecf20Sopenharmony_ci .get = v9fs_xattr_get_acl, 3318c2ecf20Sopenharmony_ci .set = v9fs_xattr_set_acl, 3328c2ecf20Sopenharmony_ci}; 333