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