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_set_acl(struct dentry *dentry, int type, struct posix_acl *acl) 101 { 102 int retval; 103 char *name; 104 size_t size; 105 void *buffer; 106 struct inode *inode = dentry->d_inode; 107 108 set_cached_acl(inode, type, acl); 109 /* Set a setxattr request to server */ 110 size = posix_acl_xattr_size(acl->a_count); 111 buffer = kmalloc(size, GFP_KERNEL); 112 if (!buffer) 113 return -ENOMEM; 114 retval = posix_acl_to_xattr(acl, buffer, size); 115 if (retval < 0) 116 goto err_free_out; 117 switch (type) { 118 case ACL_TYPE_ACCESS: 119 name = POSIX_ACL_XATTR_ACCESS; 120 break; 121 case ACL_TYPE_DEFAULT: 122 name = POSIX_ACL_XATTR_DEFAULT; 123 break; 124 default: 125 BUG(); 126 } 127 retval = v9fs_xattr_set(dentry, name, buffer, size, 0); 128 err_free_out: 129 kfree(buffer); 130 return retval; 131 } 132 133 int v9fs_acl_chmod(struct dentry *dentry) 134 { 135 int retval = 0; 136 struct posix_acl *acl, *clone; 137 struct inode *inode = dentry->d_inode; 138 139 if (S_ISLNK(inode->i_mode)) 140 return -EOPNOTSUPP; 141 acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); 142 if (acl) { 143 clone = posix_acl_clone(acl, GFP_KERNEL); 144 posix_acl_release(acl); 145 if (!clone) 146 return -ENOMEM; 147 retval = posix_acl_chmod_masq(clone, inode->i_mode); 148 if (!retval) 149 retval = v9fs_set_acl(dentry, ACL_TYPE_ACCESS, clone); 150 posix_acl_release(clone); 151 } 152 return retval; 153 } 154 155 static int v9fs_xattr_get_acl(struct dentry *dentry, const char *name, 156 void *buffer, size_t size, int type) 157 { 158 struct posix_acl *acl; 159 int error; 160 161 if (strcmp(name, "") != 0) 162 return -EINVAL; 163 164 acl = v9fs_get_cached_acl(dentry->d_inode, type); 165 if (IS_ERR(acl)) 166 return PTR_ERR(acl); 167 if (acl == NULL) 168 return -ENODATA; 169 error = posix_acl_to_xattr(acl, buffer, size); 170 posix_acl_release(acl); 171 172 return error; 173 } 174 175 static int v9fs_xattr_set_acl(struct dentry *dentry, const char *name, 176 const void *value, size_t size, 177 int flags, int type) 178 { 179 int retval; 180 struct posix_acl *acl; 181 struct inode *inode = dentry->d_inode; 182 183 if (strcmp(name, "") != 0) 184 return -EINVAL; 185 if (S_ISLNK(inode->i_mode)) 186 return -EOPNOTSUPP; 187 if (!is_owner_or_cap(inode)) 188 return -EPERM; 189 if (value) { 190 /* update the cached acl value */ 191 acl = posix_acl_from_xattr(value, size); 192 if (IS_ERR(acl)) 193 return PTR_ERR(acl); 194 else if (acl) { 195 retval = posix_acl_valid(acl); 196 if (retval) 197 goto err_out; 198 } 199 } else 200 acl = NULL; 201 202 switch (type) { 203 case ACL_TYPE_ACCESS: 204 name = POSIX_ACL_XATTR_ACCESS; 205 if (acl) { 206 mode_t mode = inode->i_mode; 207 retval = posix_acl_equiv_mode(acl, &mode); 208 if (retval < 0) 209 goto err_out; 210 else { 211 struct iattr iattr; 212 if (retval == 0) { 213 /* 214 * ACL can be represented 215 * by the mode bits. So don't 216 * update ACL. 217 */ 218 acl = NULL; 219 value = NULL; 220 size = 0; 221 } 222 /* Updte the mode bits */ 223 iattr.ia_mode = ((mode & S_IALLUGO) | 224 (inode->i_mode & ~S_IALLUGO)); 225 iattr.ia_valid = ATTR_MODE; 226 /* FIXME should we update ctime ? 227 * What is the following setxattr update the 228 * mode ? 229 */ 230 v9fs_vfs_setattr_dotl(dentry, &iattr); 231 } 232 } 233 break; 234 case ACL_TYPE_DEFAULT: 235 name = POSIX_ACL_XATTR_DEFAULT; 236 if (!S_ISDIR(inode->i_mode)) { 237 retval = -EINVAL; 238 goto err_out; 239 } 240 break; 241 default: 242 BUG(); 243 } 244 retval = v9fs_xattr_set(dentry, name, value, size, flags); 245 if (!retval) 246 set_cached_acl(inode, type, acl); 247 err_out: 248 posix_acl_release(acl); 249 return retval; 250 } 251 252 const struct xattr_handler v9fs_xattr_acl_access_handler = { 253 .prefix = POSIX_ACL_XATTR_ACCESS, 254 .flags = ACL_TYPE_ACCESS, 255 .get = v9fs_xattr_get_acl, 256 .set = v9fs_xattr_set_acl, 257 }; 258 259 const struct xattr_handler v9fs_xattr_acl_default_handler = { 260 .prefix = POSIX_ACL_XATTR_DEFAULT, 261 .flags = ACL_TYPE_DEFAULT, 262 .get = v9fs_xattr_get_acl, 263 .set = v9fs_xattr_set_acl, 264 }; 265