1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2007 Red Hat. All rights reserved. 4 */ 5 6 #include <linux/fs.h> 7 #include <linux/string.h> 8 #include <linux/xattr.h> 9 #include <linux/posix_acl_xattr.h> 10 #include <linux/posix_acl.h> 11 #include <linux/sched.h> 12 #include <linux/slab.h> 13 14 #include "ctree.h" 15 #include "btrfs_inode.h" 16 #include "xattr.h" 17 18 struct posix_acl *btrfs_get_acl(struct inode *inode, int type) 19 { 20 int size; 21 const char *name; 22 char *value = NULL; 23 struct posix_acl *acl; 24 25 switch (type) { 26 case ACL_TYPE_ACCESS: 27 name = XATTR_NAME_POSIX_ACL_ACCESS; 28 break; 29 case ACL_TYPE_DEFAULT: 30 name = XATTR_NAME_POSIX_ACL_DEFAULT; 31 break; 32 default: 33 return ERR_PTR(-EINVAL); 34 } 35 36 size = btrfs_getxattr(inode, name, NULL, 0); 37 if (size > 0) { 38 value = kzalloc(size, GFP_KERNEL); 39 if (!value) 40 return ERR_PTR(-ENOMEM); 41 size = btrfs_getxattr(inode, name, value, size); 42 } 43 if (size > 0) 44 acl = posix_acl_from_xattr(&init_user_ns, value, size); 45 else if (size == -ENODATA || size == 0) 46 acl = NULL; 47 else 48 acl = ERR_PTR(size); 49 kfree(value); 50 51 return acl; 52 } 53 54 static int __btrfs_set_acl(struct btrfs_trans_handle *trans, 55 struct inode *inode, struct posix_acl *acl, int type) 56 { 57 int ret, size = 0; 58 const char *name; 59 char *value = NULL; 60 61 switch (type) { 62 case ACL_TYPE_ACCESS: 63 name = XATTR_NAME_POSIX_ACL_ACCESS; 64 break; 65 case ACL_TYPE_DEFAULT: 66 if (!S_ISDIR(inode->i_mode)) 67 return acl ? -EINVAL : 0; 68 name = XATTR_NAME_POSIX_ACL_DEFAULT; 69 break; 70 default: 71 return -EINVAL; 72 } 73 74 if (acl) { 75 size = posix_acl_xattr_size(acl->a_count); 76 value = kmalloc(size, GFP_KERNEL); 77 if (!value) { 78 ret = -ENOMEM; 79 goto out; 80 } 81 82 ret = posix_acl_to_xattr(&init_user_ns, acl, value, size); 83 if (ret < 0) 84 goto out; 85 } 86 87 ret = btrfs_setxattr(trans, inode, name, value, size, 0); 88 out: 89 kfree(value); 90 91 if (!ret) 92 set_cached_acl(inode, type, acl); 93 94 return ret; 95 } 96 97 int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type) 98 { 99 int ret; 100 umode_t old_mode = inode->i_mode; 101 102 if (type == ACL_TYPE_ACCESS && acl) { 103 ret = posix_acl_update_mode(inode, &inode->i_mode, &acl); 104 if (ret) 105 return ret; 106 } 107 ret = __btrfs_set_acl(NULL, inode, acl, type); 108 if (ret) 109 inode->i_mode = old_mode; 110 return ret; 111 } 112 113 int btrfs_init_acl(struct btrfs_trans_handle *trans, 114 struct inode *inode, struct inode *dir) 115 { 116 struct posix_acl *default_acl, *acl; 117 int ret = 0; 118 119 /* this happens with subvols */ 120 if (!dir) 121 return 0; 122 123 ret = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 124 if (ret) 125 return ret; 126 127 if (default_acl) { 128 ret = __btrfs_set_acl(trans, inode, default_acl, 129 ACL_TYPE_DEFAULT); 130 posix_acl_release(default_acl); 131 } 132 133 if (acl) { 134 if (!ret) 135 ret = __btrfs_set_acl(trans, inode, acl, 136 ACL_TYPE_ACCESS); 137 posix_acl_release(acl); 138 } 139 140 if (!default_acl && !acl) 141 cache_no_acl(inode); 142 return ret; 143 } 144