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 entry = (struct f2fs_acl_entry *)((char *)entry + 86 sizeof(struct f2fs_acl_entry_short)); 87 break; 88 89 case ACL_USER: 90 acl->a_entries[i].e_uid = 91 make_kuid(&init_user_ns, 92 le32_to_cpu(entry->e_id)); 93 entry = (struct f2fs_acl_entry *)((char *)entry + 94 sizeof(struct f2fs_acl_entry)); 95 break; 96 case ACL_GROUP: 97 acl->a_entries[i].e_gid = 98 make_kgid(&init_user_ns, 99 le32_to_cpu(entry->e_id)); 100 entry = (struct f2fs_acl_entry *)((char *)entry + 101 sizeof(struct f2fs_acl_entry)); 102 break; 103 default: 104 goto fail; 105 } 106 } 107 if ((char *)entry != end) 108 goto fail; 109 return acl; 110 fail: 111 posix_acl_release(acl); 112 return ERR_PTR(-EINVAL); 113 } 114 115 static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) 116 { 117 struct f2fs_acl_header *f2fs_acl; 118 struct f2fs_acl_entry *entry; 119 int i; 120 121 f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * 122 sizeof(struct f2fs_acl_entry), GFP_KERNEL); 123 if (!f2fs_acl) 124 return ERR_PTR(-ENOMEM); 125 126 f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION); 127 entry = (struct f2fs_acl_entry *)(f2fs_acl + 1); 128 129 for (i = 0; i < acl->a_count; i++) { 130 131 entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag); 132 entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm); 133 134 switch (acl->a_entries[i].e_tag) { 135 case ACL_USER: 136 entry->e_id = cpu_to_le32( 137 from_kuid(&init_user_ns, 138 acl->a_entries[i].e_uid)); 139 entry = (struct f2fs_acl_entry *)((char *)entry + 140 sizeof(struct f2fs_acl_entry)); 141 break; 142 case ACL_GROUP: 143 entry->e_id = cpu_to_le32( 144 from_kgid(&init_user_ns, 145 acl->a_entries[i].e_gid)); 146 entry = (struct f2fs_acl_entry *)((char *)entry + 147 sizeof(struct f2fs_acl_entry)); 148 break; 149 case ACL_USER_OBJ: 150 case ACL_GROUP_OBJ: 151 case ACL_MASK: 152 case ACL_OTHER: 153 entry = (struct f2fs_acl_entry *)((char *)entry + 154 sizeof(struct f2fs_acl_entry_short)); 155 break; 156 default: 157 goto fail; 158 } 159 } 160 *size = f2fs_acl_size(acl->a_count); 161 return (void *)f2fs_acl; 162 163 fail: 164 kfree(f2fs_acl); 165 return ERR_PTR(-EINVAL); 166 } 167 168 struct posix_acl *f2fs_get_acl(struct inode *inode, int type) 169 { 170 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 171 int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 172 void *value = NULL; 173 struct posix_acl *acl; 174 int retval; 175 176 if (!test_opt(sbi, POSIX_ACL)) 177 return NULL; 178 179 acl = get_cached_acl(inode, type); 180 if (acl != ACL_NOT_CACHED) 181 return acl; 182 183 if (type == ACL_TYPE_ACCESS) 184 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 185 186 retval = f2fs_getxattr(inode, name_index, "", NULL, 0); 187 if (retval > 0) { 188 value = kmalloc(retval, GFP_KERNEL); 189 if (!value) 190 return ERR_PTR(-ENOMEM); 191 retval = f2fs_getxattr(inode, name_index, "", value, retval); 192 } 193 194 if (retval < 0) { 195 if (retval == -ENODATA) 196 acl = NULL; 197 else 198 acl = ERR_PTR(retval); 199 } else { 200 acl = f2fs_acl_from_disk(value, retval); 201 } 202 kfree(value); 203 if (!IS_ERR(acl)) 204 set_cached_acl(inode, type, acl); 205 206 return acl; 207 } 208 209 static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl) 210 { 211 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 212 struct f2fs_inode_info *fi = F2FS_I(inode); 213 int name_index; 214 void *value = NULL; 215 size_t size = 0; 216 int error; 217 218 if (!test_opt(sbi, POSIX_ACL)) 219 return 0; 220 if (S_ISLNK(inode->i_mode)) 221 return -EOPNOTSUPP; 222 223 switch (type) { 224 case ACL_TYPE_ACCESS: 225 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 226 if (acl) { 227 error = posix_acl_equiv_mode(acl, &inode->i_mode); 228 if (error < 0) 229 return error; 230 set_acl_inode(fi, inode->i_mode); 231 if (error == 0) 232 acl = NULL; 233 } 234 break; 235 236 case ACL_TYPE_DEFAULT: 237 name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 238 if (!S_ISDIR(inode->i_mode)) 239 return acl ? -EACCES : 0; 240 break; 241 242 default: 243 return -EINVAL; 244 } 245 246 if (acl) { 247 value = f2fs_acl_to_disk(acl, &size); 248 if (IS_ERR(value)) { 249 cond_clear_inode_flag(fi, FI_ACL_MODE); 250 return (int)PTR_ERR(value); 251 } 252 } 253 254 error = f2fs_setxattr(inode, name_index, "", value, size); 255 256 kfree(value); 257 if (!error) 258 set_cached_acl(inode, type, acl); 259 260 cond_clear_inode_flag(fi, FI_ACL_MODE); 261 return error; 262 } 263 264 int f2fs_init_acl(struct inode *inode, struct inode *dir) 265 { 266 struct posix_acl *acl = NULL; 267 struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); 268 int error = 0; 269 270 if (!S_ISLNK(inode->i_mode)) { 271 if (test_opt(sbi, POSIX_ACL)) { 272 acl = f2fs_get_acl(dir, ACL_TYPE_DEFAULT); 273 if (IS_ERR(acl)) 274 return PTR_ERR(acl); 275 } 276 if (!acl) 277 inode->i_mode &= ~current_umask(); 278 } 279 280 if (test_opt(sbi, POSIX_ACL) && acl) { 281 282 if (S_ISDIR(inode->i_mode)) { 283 error = f2fs_set_acl(inode, ACL_TYPE_DEFAULT, acl); 284 if (error) 285 goto cleanup; 286 } 287 error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); 288 if (error < 0) 289 return error; 290 if (error > 0) 291 error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); 292 } 293 cleanup: 294 posix_acl_release(acl); 295 return error; 296 } 297 298 int f2fs_acl_chmod(struct inode *inode) 299 { 300 struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); 301 struct posix_acl *acl; 302 int error; 303 mode_t mode = get_inode_mode(inode); 304 305 if (!test_opt(sbi, POSIX_ACL)) 306 return 0; 307 if (S_ISLNK(mode)) 308 return -EOPNOTSUPP; 309 310 acl = f2fs_get_acl(inode, ACL_TYPE_ACCESS); 311 if (IS_ERR(acl) || !acl) 312 return PTR_ERR(acl); 313 314 error = posix_acl_chmod(&acl, GFP_KERNEL, mode); 315 if (error) 316 return error; 317 error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); 318 posix_acl_release(acl); 319 return error; 320 } 321 322 static size_t f2fs_xattr_list_acl(struct dentry *dentry, char *list, 323 size_t list_size, const char *name, size_t name_len, int type) 324 { 325 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 326 const char *xname = POSIX_ACL_XATTR_DEFAULT; 327 size_t size; 328 329 if (!test_opt(sbi, POSIX_ACL)) 330 return 0; 331 332 if (type == ACL_TYPE_ACCESS) 333 xname = POSIX_ACL_XATTR_ACCESS; 334 335 size = strlen(xname) + 1; 336 if (list && size <= list_size) 337 memcpy(list, xname, size); 338 return size; 339 } 340 341 static int f2fs_xattr_get_acl(struct dentry *dentry, const char *name, 342 void *buffer, size_t size, int type) 343 { 344 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 345 struct posix_acl *acl; 346 int error; 347 348 if (strcmp(name, "") != 0) 349 return -EINVAL; 350 if (!test_opt(sbi, POSIX_ACL)) 351 return -EOPNOTSUPP; 352 353 acl = f2fs_get_acl(dentry->d_inode, type); 354 if (IS_ERR(acl)) 355 return PTR_ERR(acl); 356 if (!acl) 357 return -ENODATA; 358 error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); 359 posix_acl_release(acl); 360 361 return error; 362 } 363 364 static int f2fs_xattr_set_acl(struct dentry *dentry, const char *name, 365 const void *value, size_t size, int flags, int type) 366 { 367 struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); 368 struct inode *inode = dentry->d_inode; 369 struct posix_acl *acl = NULL; 370 int error; 371 372 if (strcmp(name, "") != 0) 373 return -EINVAL; 374 if (!test_opt(sbi, POSIX_ACL)) 375 return -EOPNOTSUPP; 376 if (!inode_owner_or_capable(inode)) 377 return -EPERM; 378 379 if (value) { 380 acl = posix_acl_from_xattr(&init_user_ns, value, size); 381 if (IS_ERR(acl)) 382 return PTR_ERR(acl); 383 if (acl) { 384 error = posix_acl_valid(acl); 385 if (error) 386 goto release_and_out; 387 } 388 } else { 389 acl = NULL; 390 } 391 392 error = f2fs_set_acl(inode, type, acl); 393 394 release_and_out: 395 posix_acl_release(acl); 396 return error; 397 } 398 399 const struct xattr_handler f2fs_xattr_acl_default_handler = { 400 .prefix = POSIX_ACL_XATTR_DEFAULT, 401 .flags = ACL_TYPE_DEFAULT, 402 .list = f2fs_xattr_list_acl, 403 .get = f2fs_xattr_get_acl, 404 .set = f2fs_xattr_set_acl, 405 }; 406 407 const struct xattr_handler f2fs_xattr_acl_access_handler = { 408 .prefix = POSIX_ACL_XATTR_ACCESS, 409 .flags = ACL_TYPE_ACCESS, 410 .list = f2fs_xattr_list_acl, 411 .get = f2fs_xattr_get_acl, 412 .set = f2fs_xattr_set_acl, 413 }; 414