1 /* 2 * fs/f2fs/acl.c 3 * 4 * Copyright (c) 2012 Samsung Electronics Co., Ltd. 5 * http://www.samsung.com/ 6 * 7 * Portions of this code from linux/fs/ext2/acl.c 8 * 9 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 */ 15 #include <linux/f2fs_fs.h> 16 #include "f2fs.h" 17 #include "xattr.h" 18 #include "acl.h" 19 20 #define get_inode_mode(i) ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ 21 (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) 22 23 static inline size_t f2fs_acl_size(int count) 24 { 25 if (count <= 4) { 26 return sizeof(struct f2fs_acl_header) + 27 count * sizeof(struct f2fs_acl_entry_short); 28 } else { 29 return sizeof(struct f2fs_acl_header) + 30 4 * sizeof(struct f2fs_acl_entry_short) + 31 (count - 4) * sizeof(struct f2fs_acl_entry); 32 } 33 } 34 35 static inline int f2fs_acl_count(size_t size) 36 { 37 ssize_t s; 38 size -= sizeof(struct f2fs_acl_header); 39 s = size - 4 * sizeof(struct f2fs_acl_entry_short); 40 if (s < 0) { 41 if (size % sizeof(struct f2fs_acl_entry_short)) 42 return -1; 43 return size / sizeof(struct f2fs_acl_entry_short); 44 } else { 45 if (s % sizeof(struct f2fs_acl_entry)) 46 return -1; 47 return s / sizeof(struct f2fs_acl_entry) + 4; 48 } 49 } 50 51 static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) 52 { 53 int i, count; 54 struct posix_acl *acl; 55 struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; 56 struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); 57 const char *end = value + size; 58 59 if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) 60 return ERR_PTR(-EINVAL); 61 62 count = f2fs_acl_count(size); 63 if (count < 0) 64 return ERR_PTR(-EINVAL); 65 if (count == 0) 66 return NULL; 67 68 acl = posix_acl_alloc(count, GFP_KERNEL); 69 if (!acl) 70 return ERR_PTR(-ENOMEM); 71 72 for (i = 0; i < count; i++) { 73 74 if ((char *)entry > end) 75 goto fail; 76 77 acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); 78 acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); 79 80 switch (acl->a_entries[i].e_tag) { 81 case ACL_USER_OBJ: 82 case ACL_GROUP_OBJ: 83 case ACL_MASK: 84 case ACL_OTHER: 85 acl->a_entries[i].e_id = ACL_UNDEFINED_ID; 86 entry = (struct f2fs_acl_entry *)((char *)entry + 87 sizeof(struct f2fs_acl_entry_short)); 88 break; 89 90 case ACL_USER: 91 acl->a_entries[i].e_uid = 92 make_kuid(&init_user_ns, 93 le32_to_cpu(entry->e_id)); 94 entry = (struct f2fs_acl_entry *)((char *)entry + 95 sizeof(struct f2fs_acl_entry)); 96 break; 97 case ACL_GROUP: 98 acl->a_entries[i].e_gid = 99 make_kgid(&init_user_ns, 100 le32_to_cpu(entry->e_id)); 101 entry = (struct f2fs_acl_entry *)((char *)entry + 102 sizeof(struct f2fs_acl_entry)); 103 break; 104 default: 105 goto fail; 106 } 107 } 108 if ((char *)entry != end) 109 goto fail; 110 return acl; 111 fail: 112 posix_acl_release(acl); 113 return ERR_PTR(-EINVAL); 114 } 115 116 static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) 117 { 118 struct f2fs_acl_header *f2fs_acl; 119 struct f2fs_acl_entry *entry; 120 int i; 121 122 f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * 123 sizeof(struct f2fs_acl_entry), GFP_KERNEL); 124 if (!f2fs_acl) 125 return ERR_PTR(-ENOMEM); 126 127 f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION); 128 entry = (struct f2fs_acl_entry *)(f2fs_acl + 1); 129 130 for (i = 0; i < acl->a_count; i++) { 131 132 entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag); 133 entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm); 134 135 switch (acl->a_entries[i].e_tag) { 136 case ACL_USER: 137 entry->e_id = cpu_to_le32( 138 from_kuid(&init_user_ns, 139 acl->a_entries[i].e_uid)); 140 entry = (struct f2fs_acl_entry *)((char *)entry + 141 sizeof(struct f2fs_acl_entry)); 142 break; 143 case ACL_GROUP: 144 entry->e_id = cpu_to_le32( 145 from_kgid(&init_user_ns, 146 acl->a_entries[i].e_gid)); 147 entry = (struct f2fs_acl_entry *)((char *)entry + 148 sizeof(struct f2fs_acl_entry)); 149 break; 150 case ACL_USER_OBJ: 151 case ACL_GROUP_OBJ: 152 case ACL_MASK: 153 case ACL_OTHER: 154 entry = (struct f2fs_acl_entry *)((char *)entry + 155 sizeof(struct f2fs_acl_entry_short)); 156 break; 157 default: 158 goto fail; 159 } 160 } 161 *size = f2fs_acl_size(acl->a_count); 162 return (void *)f2fs_acl; 163 164 fail: 165 kfree(f2fs_acl); 166 return ERR_PTR(-EINVAL); 167 } 168 169 struct posix_acl *f2fs_get_acl(struct inode *inode, int type) 170 { 171 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 172 int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 173 void *value = NULL; 174 struct posix_acl *acl; 175 int retval; 176 177 if (!test_opt(sbi, POSIX_ACL)) 178 return NULL; 179 180 acl = get_cached_acl(inode, type); 181 if (acl != ACL_NOT_CACHED) 182 return acl; 183 184 if (type == ACL_TYPE_ACCESS) 185 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 186 187 retval = f2fs_getxattr(inode, name_index, "", NULL, 0); 188 if (retval > 0) { 189 value = kmalloc(retval, GFP_KERNEL); 190 if (!value) 191 return ERR_PTR(-ENOMEM); 192 retval = f2fs_getxattr(inode, name_index, "", value, retval); 193 } 194 195 if (retval < 0) { 196 if (retval == -ENODATA) 197 acl = NULL; 198 else 199 acl = ERR_PTR(retval); 200 } else { 201 acl = f2fs_acl_from_disk(value, retval); 202 } 203 kfree(value); 204 if (!IS_ERR(acl)) 205 set_cached_acl(inode, type, acl); 206 207 return acl; 208 } 209 210 static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl) 211 { 212 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 213 struct f2fs_inode_info *fi = F2FS_I(inode); 214 int name_index; 215 void *value = NULL; 216 size_t size = 0; 217 int error; 218 219 if (!test_opt(sbi, POSIX_ACL)) 220 return 0; 221 if (S_ISLNK(inode->i_mode)) 222 return -EOPNOTSUPP; 223 224 switch (type) { 225 case ACL_TYPE_ACCESS: 226 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 227 if (acl) { 228 error = posix_acl_equiv_mode(acl, &inode->i_mode); 229 if (error < 0) 230 return error; 231 set_acl_inode(fi, inode->i_mode); 232 if (error == 0) 233 acl = NULL; 234 } 235 break; 236 237 case ACL_TYPE_DEFAULT: 238 name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 239 if (!S_ISDIR(inode->i_mode)) 240 return acl ? -EACCES : 0; 241 break; 242 243 default: 244 return -EINVAL; 245 } 246 247 if (acl) { 248 value = f2fs_acl_to_disk(acl, &size); 249 if (IS_ERR(value)) { 250 cond_clear_inode_flag(fi, FI_ACL_MODE); 251 return (int)PTR_ERR(value); 252 } 253 } 254 255 error = f2fs_setxattr(inode, name_index, "", value, size); 256 257 kfree(value); 258 if (!error) 259 set_cached_acl(inode, type, acl); 260 261 cond_clear_inode_flag(fi, FI_ACL_MODE); 262 return error; 263 } 264 265 int f2fs_init_acl(struct inode *inode, struct inode *dir) 266 { 267 struct posix_acl *acl = NULL; 268 struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); 269 int error = 0; 270 271 if (!S_ISLNK(inode->i_mode)) { 272 if (test_opt(sbi, POSIX_ACL)) { 273 acl = f2fs_get_acl(dir, ACL_TYPE_DEFAULT); 274 if (IS_ERR(acl)) 275 return PTR_ERR(acl); 276 } 277 if (!acl) 278 inode->i_mode &= ~current_umask(); 279 } 280 281 if (test_opt(sbi, POSIX_ACL) && acl) { 282 283 if (S_ISDIR(inode->i_mode)) { 284 error = f2fs_set_acl(inode, ACL_TYPE_DEFAULT, acl); 285 if (error) 286 goto cleanup; 287 } 288 error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); 289 if (error < 0) 290 return error; 291 if (error > 0) 292 error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); 293 } 294 cleanup: 295 posix_acl_release(acl); 296 return error; 297 } 298 299 int f2fs_acl_chmod(struct inode *inode) 300 { 301 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 302 struct posix_acl *acl; 303 int error; 304 mode_t mode = get_inode_mode(inode); 305 306 if (!test_opt(sbi, POSIX_ACL)) 307 return 0; 308 if (S_ISLNK(mode)) 309 return -EOPNOTSUPP; 310 311 acl = f2fs_get_acl(inode, ACL_TYPE_ACCESS); 312 if (IS_ERR(acl) || !acl) 313 return PTR_ERR(acl); 314 315 error = posix_acl_chmod(&acl, GFP_KERNEL, mode); 316 if (error) 317 return error; 318 error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); 319 posix_acl_release(acl); 320 return error; 321 } 322 323 static size_t f2fs_xattr_list_acl(struct dentry *dentry, char *list, 324 size_t list_size, const char *name, size_t name_len, int type) 325 { 326 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 327 const char *xname = POSIX_ACL_XATTR_DEFAULT; 328 size_t size; 329 330 if (!test_opt(sbi, POSIX_ACL)) 331 return 0; 332 333 if (type == ACL_TYPE_ACCESS) 334 xname = POSIX_ACL_XATTR_ACCESS; 335 336 size = strlen(xname) + 1; 337 if (list && size <= list_size) 338 memcpy(list, xname, size); 339 return size; 340 } 341 342 static int f2fs_xattr_get_acl(struct dentry *dentry, const char *name, 343 void *buffer, size_t size, int type) 344 { 345 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 346 struct posix_acl *acl; 347 int error; 348 349 if (strcmp(name, "") != 0) 350 return -EINVAL; 351 if (!test_opt(sbi, POSIX_ACL)) 352 return -EOPNOTSUPP; 353 354 acl = f2fs_get_acl(dentry->d_inode, type); 355 if (IS_ERR(acl)) 356 return PTR_ERR(acl); 357 if (!acl) 358 return -ENODATA; 359 error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 360 posix_acl_release(acl); 361 362 return error; 363 } 364 365 static int f2fs_xattr_set_acl(struct dentry *dentry, const char *name, 366 const void *value, size_t size, int flags, int type) 367 { 368 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 369 struct inode *inode = dentry->d_inode; 370 struct posix_acl *acl = NULL; 371 int error; 372 373 if (strcmp(name, "") != 0) 374 return -EINVAL; 375 if (!test_opt(sbi, POSIX_ACL)) 376 return -EOPNOTSUPP; 377 if (!inode_owner_or_capable(inode)) 378 return -EPERM; 379 380 if (value) { 381 acl = posix_acl_from_xattr(&init_user_ns, value, size); 382 if (IS_ERR(acl)) 383 return PTR_ERR(acl); 384 if (acl) { 385 error = posix_acl_valid(acl); 386 if (error) 387 goto release_and_out; 388 } 389 } else { 390 acl = NULL; 391 } 392 393 error = f2fs_set_acl(inode, type, acl); 394 395 release_and_out: 396 posix_acl_release(acl); 397 return error; 398 } 399 400 const struct xattr_handler f2fs_xattr_acl_default_handler = { 401 .prefix = POSIX_ACL_XATTR_DEFAULT, 402 .flags = ACL_TYPE_DEFAULT, 403 .list = f2fs_xattr_list_acl, 404 .get = f2fs_xattr_get_acl, 405 .set = f2fs_xattr_set_acl, 406 }; 407 408 const struct xattr_handler f2fs_xattr_acl_access_handler = { 409 .prefix = POSIX_ACL_XATTR_ACCESS, 410 .flags = ACL_TYPE_ACCESS, 411 .list = f2fs_xattr_list_acl, 412 .get = f2fs_xattr_get_acl, 413 .set = f2fs_xattr_set_acl, 414 }; 415