1 // SPDX-License-Identifier: LGPL-2.1 2 /* 3 * Copyright IBM Corporation, 2010 4 * Author Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 5 */ 6 7 #include <linux/module.h> 8 #include <linux/fs.h> 9 #include <net/9p/9p.h> 10 #include <net/9p/client.h> 11 #include <linux/slab.h> 12 #include <linux/sched.h> 13 #include <linux/posix_acl_xattr.h> 14 #include "xattr.h" 15 #include "acl.h" 16 #include "v9fs.h" 17 #include "v9fs_vfs.h" 18 #include "fid.h" 19 20 static struct posix_acl *v9fs_fid_get_acl(struct p9_fid *fid, const char *name) 21 { 22 ssize_t size; 23 void *value = NULL; 24 struct posix_acl *acl = NULL; 25 26 size = v9fs_fid_xattr_get(fid, name, NULL, 0); 27 if (size < 0) 28 return ERR_PTR(size); 29 if (size == 0) 30 return ERR_PTR(-ENODATA); 31 32 value = kzalloc(size, GFP_NOFS); 33 if (!value) 34 return ERR_PTR(-ENOMEM); 35 36 size = v9fs_fid_xattr_get(fid, name, value, size); 37 if (size < 0) 38 acl = ERR_PTR(size); 39 else if (size == 0) 40 acl = ERR_PTR(-ENODATA); 41 else 42 acl = posix_acl_from_xattr(&init_user_ns, value, size); 43 kfree(value); 44 return acl; 45 } 46 47 static struct posix_acl *v9fs_acl_get(struct dentry *dentry, const char *name) 48 { 49 struct p9_fid *fid; 50 struct posix_acl *acl = NULL; 51 52 fid = v9fs_fid_lookup(dentry); 53 if (IS_ERR(fid)) 54 return ERR_CAST(fid); 55 56 acl = v9fs_fid_get_acl(fid, name); 57 p9_fid_put(fid); 58 return acl; 59 } 60 61 static struct posix_acl *__v9fs_get_acl(struct p9_fid *fid, const char *name) 62 { 63 int retval; 64 struct posix_acl *acl = NULL; 65 66 acl = v9fs_fid_get_acl(fid, name); 67 if (!IS_ERR(acl)) 68 return acl; 69 70 retval = PTR_ERR(acl); 71 if (retval == -ENODATA || retval == -ENOSYS || retval == -EOPNOTSUPP) 72 return NULL; 73 74 /* map everything else to -EIO */ 75 return ERR_PTR(-EIO); 76 } 77 78 int v9fs_get_acl(struct inode *inode, struct p9_fid *fid) 79 { 80 int retval = 0; 81 struct posix_acl *pacl, *dacl; 82 struct v9fs_session_info *v9ses; 83 84 v9ses = v9fs_inode2v9ses(inode); 85 if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 86 ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 87 set_cached_acl(inode, ACL_TYPE_DEFAULT, NULL); 88 set_cached_acl(inode, ACL_TYPE_ACCESS, NULL); 89 return 0; 90 } 91 /* get the default/access acl values and cache them */ 92 dacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_DEFAULT); 93 pacl = __v9fs_get_acl(fid, XATTR_NAME_POSIX_ACL_ACCESS); 94 95 if (!IS_ERR(dacl) && !IS_ERR(pacl)) { 96 set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 97 set_cached_acl(inode, ACL_TYPE_ACCESS, pacl); 98 } else 99 retval = -EIO; 100 101 if (!IS_ERR(dacl)) 102 posix_acl_release(dacl); 103 104 if (!IS_ERR(pacl)) 105 posix_acl_release(pacl); 106 107 return retval; 108 } 109 110 static struct posix_acl *v9fs_get_cached_acl(struct inode *inode, int type) 111 { 112 struct posix_acl *acl; 113 /* 114 * 9p Always cache the acl value when 115 * instantiating the inode (v9fs_inode_from_fid) 116 */ 117 acl = get_cached_acl(inode, type); 118 BUG_ON(is_uncached_acl(acl)); 119 return acl; 120 } 121 122 struct posix_acl *v9fs_iop_get_inode_acl(struct inode *inode, int type, bool rcu) 123 { 124 struct v9fs_session_info *v9ses; 125 126 if (rcu) 127 return ERR_PTR(-ECHILD); 128 129 v9ses = v9fs_inode2v9ses(inode); 130 if (((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) || 131 ((v9ses->flags & V9FS_ACL_MASK) != V9FS_POSIX_ACL)) { 132 /* 133 * On access = client and acl = on mode get the acl 134 * values from the server 135 */ 136 return NULL; 137 } 138 return v9fs_get_cached_acl(inode, type); 139 140 } 141 142 struct posix_acl *v9fs_iop_get_acl(struct mnt_idmap *idmap, 143 struct dentry *dentry, int type) 144 { 145 struct v9fs_session_info *v9ses; 146 147 v9ses = v9fs_dentry2v9ses(dentry); 148 /* We allow set/get/list of acl when access=client is not specified. */ 149 if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) 150 return v9fs_acl_get(dentry, posix_acl_xattr_name(type)); 151 return v9fs_get_cached_acl(d_inode(dentry), type); 152 } 153 154 int v9fs_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry, 155 struct posix_acl *acl, int type) 156 { 157 int retval; 158 size_t size = 0; 159 void *value = NULL; 160 const char *acl_name; 161 struct v9fs_session_info *v9ses; 162 struct inode *inode = d_inode(dentry); 163 164 if (acl) { 165 retval = posix_acl_valid(inode->i_sb->s_user_ns, acl); 166 if (retval) 167 goto err_out; 168 169 size = posix_acl_xattr_size(acl->a_count); 170 171 value = kzalloc(size, GFP_NOFS); 172 if (!value) { 173 retval = -ENOMEM; 174 goto err_out; 175 } 176 177 retval = posix_acl_to_xattr(&init_user_ns, acl, value, size); 178 if (retval < 0) 179 goto err_out; 180 } 181 182 /* 183 * set the attribute on the remote. Without even looking at the 184 * xattr value. We leave it to the server to validate 185 */ 186 acl_name = posix_acl_xattr_name(type); 187 v9ses = v9fs_dentry2v9ses(dentry); 188 if ((v9ses->flags & V9FS_ACCESS_MASK) != V9FS_ACCESS_CLIENT) { 189 retval = v9fs_xattr_set(dentry, acl_name, value, size, 0); 190 goto err_out; 191 } 192 193 if (S_ISLNK(inode->i_mode)) { 194 retval = -EOPNOTSUPP; 195 goto err_out; 196 } 197 198 if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) { 199 retval = -EPERM; 200 goto err_out; 201 } 202 203 switch (type) { 204 case ACL_TYPE_ACCESS: 205 if (acl) { 206 struct iattr iattr = {}; 207 struct posix_acl *acl_mode = acl; 208 209 retval = posix_acl_update_mode(&nop_mnt_idmap, inode, 210 &iattr.ia_mode, 211 &acl_mode); 212 if (retval) 213 goto err_out; 214 if (!acl_mode) { 215 /* 216 * ACL can be represented by the mode bits. 217 * So don't update ACL below. 218 */ 219 kfree(value); 220 value = NULL; 221 size = 0; 222 } 223 iattr.ia_valid = ATTR_MODE; 224 /* 225 * FIXME should we update ctime ? 226 * What is the following setxattr update the mode ? 227 */ 228 v9fs_vfs_setattr_dotl(&nop_mnt_idmap, dentry, &iattr); 229 } 230 break; 231 case ACL_TYPE_DEFAULT: 232 if (!S_ISDIR(inode->i_mode)) { 233 retval = acl ? -EINVAL : 0; 234 goto err_out; 235 } 236 break; 237 } 238 239 retval = v9fs_xattr_set(dentry, acl_name, value, size, 0); 240 if (!retval) 241 set_cached_acl(inode, type, acl); 242 243 err_out: 244 kfree(value); 245 return retval; 246 } 247 248 static int v9fs_set_acl(struct p9_fid *fid, int type, struct posix_acl *acl) 249 { 250 int retval; 251 char *name; 252 size_t size; 253 void *buffer; 254 255 if (!acl) 256 return 0; 257 258 /* Set a setxattr request to server */ 259 size = posix_acl_xattr_size(acl->a_count); 260 buffer = kmalloc(size, GFP_KERNEL); 261 if (!buffer) 262 return -ENOMEM; 263 retval = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 264 if (retval < 0) 265 goto err_free_out; 266 switch (type) { 267 case ACL_TYPE_ACCESS: 268 name = XATTR_NAME_POSIX_ACL_ACCESS; 269 break; 270 case ACL_TYPE_DEFAULT: 271 name = XATTR_NAME_POSIX_ACL_DEFAULT; 272 break; 273 default: 274 BUG(); 275 } 276 retval = v9fs_fid_xattr_set(fid, name, buffer, size, 0); 277 err_free_out: 278 kfree(buffer); 279 return retval; 280 } 281 282 int v9fs_acl_chmod(struct inode *inode, struct p9_fid *fid) 283 { 284 int retval = 0; 285 struct posix_acl *acl; 286 287 if (S_ISLNK(inode->i_mode)) 288 return -EOPNOTSUPP; 289 acl = v9fs_get_cached_acl(inode, ACL_TYPE_ACCESS); 290 if (acl) { 291 retval = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); 292 if (retval) 293 return retval; 294 set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 295 retval = v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 296 posix_acl_release(acl); 297 } 298 return retval; 299 } 300 301 int v9fs_set_create_acl(struct inode *inode, struct p9_fid *fid, 302 struct posix_acl *dacl, struct posix_acl *acl) 303 { 304 set_cached_acl(inode, ACL_TYPE_DEFAULT, dacl); 305 set_cached_acl(inode, ACL_TYPE_ACCESS, acl); 306 v9fs_set_acl(fid, ACL_TYPE_DEFAULT, dacl); 307 v9fs_set_acl(fid, ACL_TYPE_ACCESS, acl); 308 return 0; 309 } 310 311 void v9fs_put_acl(struct posix_acl *dacl, 312 struct posix_acl *acl) 313 { 314 posix_acl_release(dacl); 315 posix_acl_release(acl); 316 } 317 318 int v9fs_acl_mode(struct inode *dir, umode_t *modep, 319 struct posix_acl **dpacl, struct posix_acl **pacl) 320 { 321 int retval = 0; 322 umode_t mode = *modep; 323 struct posix_acl *acl = NULL; 324 325 if (!S_ISLNK(mode)) { 326 acl = v9fs_get_cached_acl(dir, ACL_TYPE_DEFAULT); 327 if (IS_ERR(acl)) 328 return PTR_ERR(acl); 329 if (!acl) 330 mode &= ~current_umask(); 331 } 332 if (acl) { 333 if (S_ISDIR(mode)) 334 *dpacl = posix_acl_dup(acl); 335 retval = __posix_acl_create(&acl, GFP_NOFS, &mode); 336 if (retval < 0) 337 return retval; 338 if (retval > 0) 339 *pacl = acl; 340 else 341 posix_acl_release(acl); 342 } 343 *modep = mode; 344 return 0; 345 } 346
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.