1/* 2 * (C) 2005 Andreas Gruenbacher <agruen@suse.de> 3 * 4 * This file is released under the GPL. 5 * 6 * Generic ACL support for in-memory filesystems. 7 */ 8 9#include <linux/sched.h> 10#include <linux/gfp.h> 11#include <linux/fs.h> 12#include <linux/generic_acl.h> 13#include <linux/posix_acl.h> 14#include <linux/posix_acl_xattr.h> 15 16 17static size_t 18generic_acl_list(struct dentry *dentry, char *list, size_t list_size, 19 const char *name, size_t name_len, int type) 20{ 21 struct posix_acl *acl; 22 const char *xname; 23 size_t size; 24 25 acl = get_cached_acl(dentry->d_inode, type); 26 if (!acl) 27 return 0; 28 posix_acl_release(acl); 29 30 switch (type) { 31 case ACL_TYPE_ACCESS: 32 xname = POSIX_ACL_XATTR_ACCESS; 33 break; 34 case ACL_TYPE_DEFAULT: 35 xname = POSIX_ACL_XATTR_DEFAULT; 36 break; 37 default: 38 return 0; 39 } 40 size = strlen(xname) + 1; 41 if (list && size <= list_size) 42 memcpy(list, xname, size); 43 return size; 44} 45 46static int 47generic_acl_get(struct dentry *dentry, const char *name, void *buffer, 48 size_t size, int type) 49{ 50 struct posix_acl *acl; 51 int error; 52 53 if (strcmp(name, "") != 0) 54 return -EINVAL; 55 56 acl = get_cached_acl(dentry->d_inode, type); 57 if (!acl) 58 return -ENODATA; 59 error = posix_acl_to_xattr(acl, buffer, size); 60 posix_acl_release(acl); 61 62 return error; 63} 64 65static int 66generic_acl_set(struct dentry *dentry, const char *name, const void *value, 67 size_t size, int flags, int type) 68{ 69 struct inode *inode = dentry->d_inode; 70 struct posix_acl *acl = NULL; 71 int error; 72 73 if (strcmp(name, "") != 0) 74 return -EINVAL; 75 if (S_ISLNK(inode->i_mode)) 76 return -EOPNOTSUPP; 77 if (!is_owner_or_cap(inode)) 78 return -EPERM; 79 if (value) { 80 acl = posix_acl_from_xattr(value, size); 81 if (IS_ERR(acl)) 82 return PTR_ERR(acl); 83 } 84 if (acl) { 85 mode_t mode; 86 87 error = posix_acl_valid(acl); 88 if (error) 89 goto failed; 90 switch (type) { 91 case ACL_TYPE_ACCESS: 92 mode = inode->i_mode; 93 error = posix_acl_equiv_mode(acl, &mode); 94 if (error < 0) 95 goto failed; 96 inode->i_mode = mode; 97 inode->i_ctime = CURRENT_TIME; 98 if (error == 0) { 99 posix_acl_release(acl); 100 acl = NULL; 101 } 102 break; 103 case ACL_TYPE_DEFAULT: 104 if (!S_ISDIR(inode->i_mode)) { 105 error = -EINVAL; 106 goto failed; 107 } 108 break; 109 } 110 } 111 set_cached_acl(inode, type, acl); 112 error = 0; 113failed: 114 posix_acl_release(acl); 115 return error; 116} 117 118/** 119 * generic_acl_init - Take care of acl inheritance at @inode create time 120 * 121 * Files created inside a directory with a default ACL inherit the 122 * directory's default ACL. 123 */ 124int 125generic_acl_init(struct inode *inode, struct inode *dir) 126{ 127 struct posix_acl *acl = NULL; 128 mode_t mode = inode->i_mode; 129 int error; 130 131 inode->i_mode = mode & ~current_umask(); 132 if (!S_ISLNK(inode->i_mode)) 133 acl = get_cached_acl(dir, ACL_TYPE_DEFAULT); 134 if (acl) { 135 struct posix_acl *clone; 136 137 if (S_ISDIR(inode->i_mode)) { 138 clone = posix_acl_clone(acl, GFP_KERNEL); 139 error = -ENOMEM; 140 if (!clone) 141 goto cleanup; 142 set_cached_acl(inode, ACL_TYPE_DEFAULT, clone); 143 posix_acl_release(clone); 144 } 145 clone = posix_acl_clone(acl, GFP_KERNEL); 146 error = -ENOMEM; 147 if (!clone) 148 goto cleanup; 149 error = posix_acl_create_masq(clone, &mode); 150 if (error >= 0) { 151 inode->i_mode = mode; 152 if (error > 0) 153 set_cached_acl(inode, ACL_TYPE_ACCESS, clone); 154 } 155 posix_acl_release(clone); 156 } 157 error = 0; 158 159cleanup: 160 posix_acl_release(acl); 161 return error; 162} 163 164/** 165 * generic_acl_chmod - change the access acl of @inode upon chmod() 166 * 167 * A chmod also changes the permissions of the owner, group/mask, and 168 * other ACL entries. 169 */ 170int 171generic_acl_chmod(struct inode *inode) 172{ 173 struct posix_acl *acl, *clone; 174 int error = 0; 175 176 if (S_ISLNK(inode->i_mode)) 177 return -EOPNOTSUPP; 178 acl = get_cached_acl(inode, ACL_TYPE_ACCESS); 179 if (acl) { 180 clone = posix_acl_clone(acl, GFP_KERNEL); 181 posix_acl_release(acl); 182 if (!clone) 183 return -ENOMEM; 184 error = posix_acl_chmod_masq(clone, inode->i_mode); 185 if (!error) 186 set_cached_acl(inode, ACL_TYPE_ACCESS, clone); 187 posix_acl_release(clone); 188 } 189 return error; 190} 191 192int 193generic_check_acl(struct inode *inode, int mask) 194{ 195 struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS); 196 197 if (acl) { 198 int error = posix_acl_permission(inode, acl, mask); 199 posix_acl_release(acl); 200 return error; 201 } 202 return -EAGAIN; 203} 204 205const struct xattr_handler generic_acl_access_handler = { 206 .prefix = POSIX_ACL_XATTR_ACCESS, 207 .flags = ACL_TYPE_ACCESS, 208 .list = generic_acl_list, 209 .get = generic_acl_get, 210 .set = generic_acl_set, 211}; 212 213const struct xattr_handler generic_acl_default_handler = { 214 .prefix = POSIX_ACL_XATTR_DEFAULT, 215 .flags = ACL_TYPE_DEFAULT, 216 .list = generic_acl_list, 217 .get = generic_acl_get, 218 .set = generic_acl_set, 219}; 220