at v2.6.34-rc4 218 lines 4.6 kB view raw
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 if (error == 0) { 98 posix_acl_release(acl); 99 acl = NULL; 100 } 101 break; 102 case ACL_TYPE_DEFAULT: 103 if (!S_ISDIR(inode->i_mode)) { 104 error = -EINVAL; 105 goto failed; 106 } 107 break; 108 } 109 } 110 set_cached_acl(inode, type, acl); 111 error = 0; 112failed: 113 posix_acl_release(acl); 114 return error; 115} 116 117/** 118 * generic_acl_init - Take care of acl inheritance at @inode create time 119 * 120 * Files created inside a directory with a default ACL inherit the 121 * directory's default ACL. 122 */ 123int 124generic_acl_init(struct inode *inode, struct inode *dir) 125{ 126 struct posix_acl *acl = NULL; 127 mode_t mode = inode->i_mode; 128 int error; 129 130 inode->i_mode = mode & ~current_umask(); 131 if (!S_ISLNK(inode->i_mode)) 132 acl = get_cached_acl(dir, ACL_TYPE_DEFAULT); 133 if (acl) { 134 struct posix_acl *clone; 135 136 if (S_ISDIR(inode->i_mode)) { 137 clone = posix_acl_clone(acl, GFP_KERNEL); 138 error = -ENOMEM; 139 if (!clone) 140 goto cleanup; 141 set_cached_acl(inode, ACL_TYPE_DEFAULT, clone); 142 posix_acl_release(clone); 143 } 144 clone = posix_acl_clone(acl, GFP_KERNEL); 145 error = -ENOMEM; 146 if (!clone) 147 goto cleanup; 148 error = posix_acl_create_masq(clone, &mode); 149 if (error >= 0) { 150 inode->i_mode = mode; 151 if (error > 0) 152 set_cached_acl(inode, ACL_TYPE_ACCESS, clone); 153 } 154 posix_acl_release(clone); 155 } 156 error = 0; 157 158cleanup: 159 posix_acl_release(acl); 160 return error; 161} 162 163/** 164 * generic_acl_chmod - change the access acl of @inode upon chmod() 165 * 166 * A chmod also changes the permissions of the owner, group/mask, and 167 * other ACL entries. 168 */ 169int 170generic_acl_chmod(struct inode *inode) 171{ 172 struct posix_acl *acl, *clone; 173 int error = 0; 174 175 if (S_ISLNK(inode->i_mode)) 176 return -EOPNOTSUPP; 177 acl = get_cached_acl(inode, ACL_TYPE_ACCESS); 178 if (acl) { 179 clone = posix_acl_clone(acl, GFP_KERNEL); 180 posix_acl_release(acl); 181 if (!clone) 182 return -ENOMEM; 183 error = posix_acl_chmod_masq(clone, inode->i_mode); 184 if (!error) 185 set_cached_acl(inode, ACL_TYPE_ACCESS, clone); 186 posix_acl_release(clone); 187 } 188 return error; 189} 190 191int 192generic_check_acl(struct inode *inode, int mask) 193{ 194 struct posix_acl *acl = get_cached_acl(inode, ACL_TYPE_ACCESS); 195 196 if (acl) { 197 int error = posix_acl_permission(inode, acl, mask); 198 posix_acl_release(acl); 199 return error; 200 } 201 return -EAGAIN; 202} 203 204struct xattr_handler generic_acl_access_handler = { 205 .prefix = POSIX_ACL_XATTR_ACCESS, 206 .flags = ACL_TYPE_ACCESS, 207 .list = generic_acl_list, 208 .get = generic_acl_get, 209 .set = generic_acl_set, 210}; 211 212struct xattr_handler generic_acl_default_handler = { 213 .prefix = POSIX_ACL_XATTR_DEFAULT, 214 .flags = ACL_TYPE_DEFAULT, 215 .list = generic_acl_list, 216 .get = generic_acl_get, 217 .set = generic_acl_set, 218};