1 /* 2 * Copyright IBM Corporation, 2010 3 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2.1 of the GNU Lesser General Public License 7 * as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 */ 14 15 #include <linux/module.h> 16 #include <linux/fs.h> 17 #include <net/9p/9p.h> 18 #include <net/9p/client.h> 19 #include <linux/slab.h> 20 #include <linux/sched.h> 21 #include <linux/posix_acl_xattr.h> 22 #include "xattr.h" 23 #include "acl.h" 24 #include "v9fs_vfs.h" 25 26 static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, char *name) 27 { 28 ssize_t size; 29 void *value = NULL; 30 struct posix_acl *acl = NULL;; 31 32 size = v9fs_fid_xattr_get(fid, name, NULL, 0); 33 if (size > 0) { 34 value = kzalloc(size, GFP_NOFS); 35 if (!value) 36 return ERR_PTR(-ENOMEM); 37 size = v9fs_fid_xattr_get(fid, name, value, size); 38 if (size > 0) { 39 acl = posix_acl_from_xattr(value, size); 40 if (IS_ERR(acl)) 41 goto err_out; 42 } 43 } else if (size == -ENODATA || size == 0 || 44 size == -ENOSYS || size == -EOPNOTSUPP) { 45 acl = NULL; 46 } else 47 acl = ERR_PTR(-EIO); 48 49 err_out: 50 kfree(value); 51 return acl; 52 } 53 54 int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) 55 { 56 int retval = 0; 57 struct posix_acl *pacl, *dacl; 58 59 /* get the default/access acl values and cache them */ 60 dacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_DEFAULT); 61 pacl = __v9fs_get_acl(fid, POSIX_ACL_XATTR_ACCESS); 62 63 if (!IS_ERR(dacl) && !IS_ERR(pacl)) { 64 set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 65 set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); 66 posix_acl_release(dacl); 67 posix_acl_release(pacl); 68 } else 69 retval = -EIO; 70 71 return retval; 72 } 73 74 static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) 75 { 76 struct posix_acl *acl; 77 /* 78 * 9p Always cache the acl value when 79 * instantiating the inode (v9fs_inode_from_fid) 80 */ 81 acl = get_cached_acl(inode, type); 82 BUG_ON(acl == ACL_NOT_CACHED); 83 return acl; 84 } 85 86 int v9fs_check_acl(struct inode *inode, int mask) 87 { 88 struct posix_acl *acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); 89 90 if (IS_ERR(acl)) 91 return PTR_ERR(acl); 92 if (acl) { 93 int error = posix_acl_permission(inode, acl, mask); 94 posix_acl_release(acl); 95 return error; 96 } 97 return -EAGAIN; 98 } 99 100 static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name, 101 void *buffer, size_t size, int type) 102 { 103 struct posix_acl *acl; 104 int error; 105 106 if (strcmp(name, "") != 0) 107 return -EINVAL; 108 109 acl = v9fs_get_cached_acl(dentry->d_inode, type); 110 if (IS_ERR(acl)) 111 return PTR_ERR(acl); 112 if (acl == NULL) 113 return -ENODATA; 114 error = posix_acl_to_xattr(acl, buffer, size); 115 posix_acl_release(acl); 116 117 return error; 118 } 119 120 static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, 121 const void *value, size_t size, 122 int flags, int type) 123 { 124 int retval; 125 struct posix_acl *acl; 126 struct inode *inode = dentry->d_inode; 127 128 if (strcmp(name, "") != 0) 129 return -EINVAL; 130 if (S_ISLNK(inode->i_mode)) 131 return -EOPNOTSUPP; 132 if (!is_owner_or_cap(inode)) 133 return -EPERM; 134 if (value) { 135 /* update the cached acl value */ 136 acl = posix_acl_from_xattr(value, size); 137 if (IS_ERR(acl)) 138 return PTR_ERR(acl); 139 else if (acl) { 140 retval = posix_acl_valid(acl); 141 if (retval) 142 goto err_out; 143 } 144 } else 145 acl = NULL; 146 147 switch (type) { 148 case ACL_TYPE_ACCESS: 149 name = POSIX_ACL_XATTR_ACCESS; 150 if (acl) { 151 mode_t mode = inode->i_mode; 152 retval = posix_acl_equiv_mode(acl, &mode); 153 if (retval < 0) 154 goto err_out; 155 else { 156 struct iattr iattr; 157 if (retval == 0) { 158 /* 159 * ACL can be represented 160 * by the mode bits. So don't 161 * update ACL. 162 */ 163 acl = NULL; 164 value = NULL; 165 size = 0; 166 } 167 /* Updte the mode bits */ 168 iattr.ia_mode = ((mode & S_IALLUGO) | 169 (inode->i_mode & ~S_IALLUGO)); 170 iattr.ia_valid = ATTR_MODE; 171 /* FIXME should we update ctime ? 172 * What is the following setxattr update the 173 * mode ? 174 */ 175 v9fs_vfs_setattr_dotl(dentry, &iattr); 176 } 177 } 178 break; 179 case ACL_TYPE_DEFAULT: 180 name = POSIX_ACL_XATTR_DEFAULT; 181 if (!S_ISDIR(inode->i_mode)) { 182 retval = -EINVAL; 183 goto err_out; 184 } 185 break; 186 default: 187 BUG(); 188 } 189 retval = v9fs_xattr_set(dentry, name, value, size, flags); 190 if (!retval) 191 set_cached_acl(inode, type, acl); 192 err_out: 193 posix_acl_release(acl); 194 return retval; 195 } 196 197 const struct xattr_handler v9fs_xattr_acl_access_handler = { 198 .prefix = POSIX_ACL_XATTR_ACCESS, 199 .flags = ACL_TYPE_ACCESS, 200 .get = v9fs_xattr_get_acl, 201 .set = v9fs_xattr_set_acl, 202 }; 203 204 const struct xattr_handler v9fs_xattr_acl_default_handler = { 205 .prefix = POSIX_ACL_XATTR_DEFAULT, 206 .flags = ACL_TYPE_DEFAULT, 207 .get = v9fs_xattr_get_acl, 208 .set = v9fs_xattr_set_acl, 209 }; 210