1 /* 2 * linux/fs/ext2/acl.c 3 * 4 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 5 */ 6 7 #include <linux/capability.h> 8 #include <linux/init.h> 9 #include <linux/sched.h> 10 #include <linux/slab.h> 11 #include <linux/fs.h> 12 #include "ext2.h" 13 #include "xattr.h" 14 #include "acl.h" 15 16 /* 17 * Convert from filesystem to in-memory representation. 18 */ 19 static struct posix_acl * 20 ext2_acl_from_disk(const void *value, size_t size) 21 { 22 const char *end = (char *)value + size; 23 int n, count; 24 struct posix_acl *acl; 25 26 if (!value) 27 return NULL; 28 if (size < sizeof(ext2_acl_header)) 29 return ERR_PTR(-EINVAL); 30 if (((ext2_acl_header *)value)->a_version != 31 cpu_to_le32(EXT2_ACL_VERSION)) 32 return ERR_PTR(-EINVAL); 33 value = (char *)value + sizeof(ext2_acl_header); 34 count = ext2_acl_count(size); 35 if (count < 0) 36 return ERR_PTR(-EINVAL); 37 if (count == 0) 38 return NULL; 39 acl = posix_acl_alloc(count, GFP_KERNEL); 40 if (!acl) 41 return ERR_PTR(-ENOMEM); 42 for (n=0; n < count; n++) { 43 ext2_acl_entry *entry = 44 (ext2_acl_entry *)value; 45 if ((char *)value + sizeof(ext2_acl_entry_short) > end) 46 goto fail; 47 acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag); 48 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm); 49 switch(acl->a_entries[n].e_tag) { 50 case ACL_USER_OBJ: 51 case ACL_GROUP_OBJ: 52 case ACL_MASK: 53 case ACL_OTHER: 54 value = (char *)value + 55 sizeof(ext2_acl_entry_short); 56 break; 57 58 case ACL_USER: 59 value = (char *)value + sizeof(ext2_acl_entry); 60 if ((char *)value > end) 61 goto fail; 62 acl->a_entries[n].e_uid = 63 make_kuid(&init_user_ns, 64 le32_to_cpu(entry->e_id)); 65 break; 66 case ACL_GROUP: 67 value = (char *)value + sizeof(ext2_acl_entry); 68 if ((char *)value > end) 69 goto fail; 70 acl->a_entries[n].e_gid = 71 make_kgid(&init_user_ns, 72 le32_to_cpu(entry->e_id)); 73 break; 74 75 default: 76 goto fail; 77 } 78 } 79 if (value != end) 80 goto fail; 81 return acl; 82 83 fail: 84 posix_acl_release(acl); 85 return ERR_PTR(-EINVAL); 86 } 87 88 /* 89 * Convert from in-memory to filesystem representation. 90 */ 91 static void * 92 ext2_acl_to_disk(const struct posix_acl *acl, size_t *size) 93 { 94 ext2_acl_header *ext_acl; 95 char *e; 96 size_t n; 97 98 *size = ext2_acl_size(acl->a_count); 99 ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count * 100 sizeof(ext2_acl_entry), GFP_KERNEL); 101 if (!ext_acl) 102 return ERR_PTR(-ENOMEM); 103 ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION); 104 e = (char *)ext_acl + sizeof(ext2_acl_header); 105 for (n=0; n < acl->a_count; n++) { 106 const struct posix_acl_entry *acl_e = &acl->a_entries[n]; 107 ext2_acl_entry *entry = (ext2_acl_entry *)e; 108 entry->e_tag = cpu_to_le16(acl_e->e_tag); 109 entry->e_perm = cpu_to_le16(acl_e->e_perm); 110 switch(acl_e->e_tag) { 111 case ACL_USER: 112 entry->e_id = cpu_to_le32( 113 from_kuid(&init_user_ns, acl_e->e_uid)); 114 e += sizeof(ext2_acl_entry); 115 break; 116 case ACL_GROUP: 117 entry->e_id = cpu_to_le32( 118 from_kgid(&init_user_ns, acl_e->e_gid)); 119 e += sizeof(ext2_acl_entry); 120 break; 121 122 case ACL_USER_OBJ: 123 case ACL_GROUP_OBJ: 124 case ACL_MASK: 125 case ACL_OTHER: 126 e += sizeof(ext2_acl_entry_short); 127 break; 128 129 default: 130 goto fail; 131 } 132 } 133 return (char *)ext_acl; 134 135 fail: 136 kfree(ext_acl); 137 return ERR_PTR(-EINVAL); 138 } 139 140 /* 141 * inode->i_mutex: don't care 142 */ 143 struct posix_acl * 144 ext2_get_acl(struct inode *inode, int type) 145 { 146 int name_index; 147 char *value = NULL; 148 struct posix_acl *acl; 149 int retval; 150 151 if (!test_opt(inode->i_sb, POSIX_ACL)) 152 return NULL; 153 154 acl = get_cached_acl(inode, type); 155 if (acl != ACL_NOT_CACHED) 156 return acl; 157 158 switch (type) { 159 case ACL_TYPE_ACCESS: 160 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 161 break; 162 case ACL_TYPE_DEFAULT: 163 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; 164 break; 165 default: 166 BUG(); 167 } 168 retval = ext2_xattr_get(inode, name_index, "", NULL, 0); 169 if (retval > 0) { 170 value = kmalloc(retval, GFP_KERNEL); 171 if (!value) 172 return ERR_PTR(-ENOMEM); 173 retval = ext2_xattr_get(inode, name_index, "", value, retval); 174 } 175 if (retval > 0) 176 acl = ext2_acl_from_disk(value, retval); 177 else if (retval == -ENODATA || retval == -ENOSYS) 178 acl = NULL; 179 else 180 acl = ERR_PTR(retval); 181 kfree(value); 182 183 if (!IS_ERR(acl)) 184 set_cached_acl(inode, type, acl); 185 186 return acl; 187 } 188 189 /* 190 * inode->i_mutex: down 191 */ 192 static int 193 ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) 194 { 195 int name_index; 196 void *value = NULL; 197 size_t size = 0; 198 int error; 199 200 if (S_ISLNK(inode->i_mode)) 201 return -EOPNOTSUPP; 202 if (!test_opt(inode->i_sb, POSIX_ACL)) 203 return 0; 204 205 switch(type) { 206 case ACL_TYPE_ACCESS: 207 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS; 208 if (acl) { 209 error = posix_acl_equiv_mode(acl, &inode->i_mode); 210 if (error < 0) 211 return error; 212 else { 213 inode->i_ctime = CURRENT_TIME_SEC; 214 mark_inode_dirty(inode); 215 if (error == 0) 216 acl = NULL; 217 } 218 } 219 break; 220 221 case ACL_TYPE_DEFAULT: 222 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT; 223 if (!S_ISDIR(inode->i_mode)) 224 return acl ? -EACCES : 0; 225 break; 226 227 default: 228 return -EINVAL; 229 } 230 if (acl) { 231 value = ext2_acl_to_disk(acl, &size); 232 if (IS_ERR(value)) 233 return (int)PTR_ERR(value); 234 } 235 236 error = ext2_xattr_set(inode, name_index, "", value, size, 0); 237 238 kfree(value); 239 if (!error) 240 set_cached_acl(inode, type, acl); 241 return error; 242 } 243 244 /* 245 * Initialize the ACLs of a new inode. Called from ext2_new_inode. 246 * 247 * dir->i_mutex: down 248 * inode->i_mutex: up (access to inode is still exclusive) 249 */ 250 int 251 ext2_init_acl(struct inode *inode, struct inode *dir) 252 { 253 struct posix_acl *acl = NULL; 254 int error = 0; 255 256 if (!S_ISLNK(inode->i_mode)) { 257 if (test_opt(dir->i_sb, POSIX_ACL)) { 258 acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); 259 if (IS_ERR(acl)) 260 return PTR_ERR(acl); 261 } 262 if (!acl) 263 inode->i_mode &= ~current_umask(); 264 } 265 if (test_opt(inode->i_sb, POSIX_ACL) && acl) { 266 if (S_ISDIR(inode->i_mode)) { 267 error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); 268 if (error) 269 goto cleanup; 270 } 271 error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); 272 if (error < 0) 273 return error; 274 if (error > 0) { 275 /* This is an extended ACL */ 276 error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl); 277 } 278 } 279 cleanup: 280 posix_acl_release(acl); 281 return error; 282 } 283 284 /* 285 * Does chmod for an inode that may have an Access Control List. The 286 * inode->i_mode field must be updated to the desired value by the caller 287 * before calling this function. 288 * Returns 0 on success, or a negative error number. 289 * 290 * We change the ACL rather than storing some ACL entries in the file 291 * mode permission bits (which would be more efficient), because that 292 * would break once additional permissions (like ACL_APPEND, ACL_DELETE 293 * for directories) are added. There are no more bits available in the 294 * file mode. 295 * 296 * inode->i_mutex: down 297 */ 298 int 299 ext2_acl_chmod(struct inode *inode) 300 { 301 struct posix_acl *acl; 302 int error; 303 304 if (!test_opt(inode->i_sb, POSIX_ACL)) 305 return 0; 306 if (S_ISLNK(inode->i_mode)) 307 return -EOPNOTSUPP; 308 acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); 309 if (IS_ERR(acl) || !acl) 310 return PTR_ERR(acl); 311 error = posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode); 312 if (error) 313 return error; 314 error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl); 315 posix_acl_release(acl); 316 return error; 317 } 318 319 /* 320 * Extended attribut handlers 321 */ 322 static size_t 323 ext2_xattr_list_acl_access(struct dentry *dentry, char *list, size_t list_size, 324 const char *name, size_t name_len, int type) 325 { 326 const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS); 327 328 if (!test_opt(dentry->d_sb, POSIX_ACL)) 329 return 0; 330 if (list && size <= list_size) 331 memcpy(list, POSIX_ACL_XATTR_ACCESS, size); 332 return size; 333 } 334 335 static size_t 336 ext2_xattr_list_acl_default(struct dentry *dentry, char *list, size_t list_size, 337 const char *name, size_t name_len, int type) 338 { 339 const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT); 340 341 if (!test_opt(dentry->d_sb, POSIX_ACL)) 342 return 0; 343 if (list && size <= list_size) 344 memcpy(list, POSIX_ACL_XATTR_DEFAULT, size); 345 return size; 346 } 347 348 static int 349 ext2_xattr_get_acl(struct dentry *dentry, const char *name, void *buffer, 350 size_t size, int type) 351 { 352 struct posix_acl *acl; 353 int error; 354 355 if (strcmp(name, "") != 0) 356 return -EINVAL; 357 if (!test_opt(dentry->d_sb, POSIX_ACL)) 358 return -EOPNOTSUPP; 359 360 acl = ext2_get_acl(dentry->d_inode, type); 361 if (IS_ERR(acl)) 362 return PTR_ERR(acl); 363 if (acl == NULL) 364 return -ENODATA; 365 error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 366 posix_acl_release(acl); 367 368 return error; 369 } 370 371 static int 372 ext2_xattr_set_acl(struct dentry *dentry, const char *name, const void *value, 373 size_t size, int flags, int type) 374 { 375 struct posix_acl *acl; 376 int error; 377 378 if (strcmp(name, "") != 0) 379 return -EINVAL; 380 if (!test_opt(dentry->d_sb, POSIX_ACL)) 381 return -EOPNOTSUPP; 382 if (!inode_owner_or_capable(dentry->d_inode)) 383 return -EPERM; 384 385 if (value) { 386 acl = posix_acl_from_xattr(&init_user_ns, value, size); 387 if (IS_ERR(acl)) 388 return PTR_ERR(acl); 389 else if (acl) { 390 error = posix_acl_valid(acl); 391 if (error) 392 goto release_and_out; 393 } 394 } else 395 acl = NULL; 396 397 error = ext2_set_acl(dentry->d_inode, type, acl); 398 399 release_and_out: 400 posix_acl_release(acl); 401 return error; 402 } 403 404 const struct xattr_handler ext2_xattr_acl_access_handler = { 405 .prefix = POSIX_ACL_XATTR_ACCESS, 406 .flags = ACL_TYPE_ACCESS, 407 .list = ext2_xattr_list_acl_access, 408 .get = ext2_xattr_get_acl, 409 .set = ext2_xattr_set_acl, 410 }; 411 412 const struct xattr_handler ext2_xattr_acl_default_handler = { 413 .prefix = POSIX_ACL_XATTR_DEFAULT, 414 .flags = ACL_TYPE_DEFAULT, 415 .list = ext2_xattr_list_acl_default, 416 .get = ext2_xattr_get_acl, 417 .set = ext2_xattr_set_acl, 418 }; 419