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