10a8165d7SJaegeuk Kim /* 2af48b85bSJaegeuk Kim * fs/f2fs/acl.c 3af48b85bSJaegeuk Kim * 4af48b85bSJaegeuk Kim * Copyright (c) 2012 Samsung Electronics Co., Ltd. 5af48b85bSJaegeuk Kim * http://www.samsung.com/ 6af48b85bSJaegeuk Kim * 7af48b85bSJaegeuk Kim * Portions of this code from linux/fs/ext2/acl.c 8af48b85bSJaegeuk Kim * 9af48b85bSJaegeuk Kim * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de> 10af48b85bSJaegeuk Kim * 11af48b85bSJaegeuk Kim * This program is free software; you can redistribute it and/or modify 12af48b85bSJaegeuk Kim * it under the terms of the GNU General Public License version 2 as 13af48b85bSJaegeuk Kim * published by the Free Software Foundation. 14af48b85bSJaegeuk Kim */ 15af48b85bSJaegeuk Kim #include <linux/f2fs_fs.h> 16af48b85bSJaegeuk Kim #include "f2fs.h" 17af48b85bSJaegeuk Kim #include "xattr.h" 18af48b85bSJaegeuk Kim #include "acl.h" 19af48b85bSJaegeuk Kim 20af48b85bSJaegeuk Kim static inline size_t f2fs_acl_size(int count) 21af48b85bSJaegeuk Kim { 22af48b85bSJaegeuk Kim if (count <= 4) { 23af48b85bSJaegeuk Kim return sizeof(struct f2fs_acl_header) + 24af48b85bSJaegeuk Kim count * sizeof(struct f2fs_acl_entry_short); 25af48b85bSJaegeuk Kim } else { 26af48b85bSJaegeuk Kim return sizeof(struct f2fs_acl_header) + 27af48b85bSJaegeuk Kim 4 * sizeof(struct f2fs_acl_entry_short) + 28af48b85bSJaegeuk Kim (count - 4) * sizeof(struct f2fs_acl_entry); 29af48b85bSJaegeuk Kim } 30af48b85bSJaegeuk Kim } 31af48b85bSJaegeuk Kim 32af48b85bSJaegeuk Kim static inline int f2fs_acl_count(size_t size) 33af48b85bSJaegeuk Kim { 34af48b85bSJaegeuk Kim ssize_t s; 35af48b85bSJaegeuk Kim size -= sizeof(struct f2fs_acl_header); 36af48b85bSJaegeuk Kim s = size - 4 * sizeof(struct f2fs_acl_entry_short); 37af48b85bSJaegeuk Kim if (s < 0) { 38af48b85bSJaegeuk Kim if (size % sizeof(struct f2fs_acl_entry_short)) 39af48b85bSJaegeuk Kim return -1; 40af48b85bSJaegeuk Kim return size / sizeof(struct f2fs_acl_entry_short); 41af48b85bSJaegeuk Kim } else { 42af48b85bSJaegeuk Kim if (s % sizeof(struct f2fs_acl_entry)) 43af48b85bSJaegeuk Kim return -1; 44af48b85bSJaegeuk Kim return s / sizeof(struct f2fs_acl_entry) + 4; 45af48b85bSJaegeuk Kim } 46af48b85bSJaegeuk Kim } 47af48b85bSJaegeuk Kim 48af48b85bSJaegeuk Kim static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) 49af48b85bSJaegeuk Kim { 50af48b85bSJaegeuk Kim int i, count; 51af48b85bSJaegeuk Kim struct posix_acl *acl; 52af48b85bSJaegeuk Kim struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; 53af48b85bSJaegeuk Kim struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); 54af48b85bSJaegeuk Kim const char *end = value + size; 55af48b85bSJaegeuk Kim 56af48b85bSJaegeuk Kim if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) 57af48b85bSJaegeuk Kim return ERR_PTR(-EINVAL); 58af48b85bSJaegeuk Kim 59af48b85bSJaegeuk Kim count = f2fs_acl_count(size); 60af48b85bSJaegeuk Kim if (count < 0) 61af48b85bSJaegeuk Kim return ERR_PTR(-EINVAL); 62af48b85bSJaegeuk Kim if (count == 0) 63af48b85bSJaegeuk Kim return NULL; 64af48b85bSJaegeuk Kim 65af48b85bSJaegeuk Kim acl = posix_acl_alloc(count, GFP_KERNEL); 66af48b85bSJaegeuk Kim if (!acl) 67af48b85bSJaegeuk Kim return ERR_PTR(-ENOMEM); 68af48b85bSJaegeuk Kim 69af48b85bSJaegeuk Kim for (i = 0; i < count; i++) { 70af48b85bSJaegeuk Kim 71af48b85bSJaegeuk Kim if ((char *)entry > end) 72af48b85bSJaegeuk Kim goto fail; 73af48b85bSJaegeuk Kim 74af48b85bSJaegeuk Kim acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); 75af48b85bSJaegeuk Kim acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); 76af48b85bSJaegeuk Kim 77af48b85bSJaegeuk Kim switch (acl->a_entries[i].e_tag) { 78af48b85bSJaegeuk Kim case ACL_USER_OBJ: 79af48b85bSJaegeuk Kim case ACL_GROUP_OBJ: 80af48b85bSJaegeuk Kim case ACL_MASK: 81af48b85bSJaegeuk Kim case ACL_OTHER: 82af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 83af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry_short)); 84af48b85bSJaegeuk Kim break; 85af48b85bSJaegeuk Kim 86af48b85bSJaegeuk Kim case ACL_USER: 87af48b85bSJaegeuk Kim acl->a_entries[i].e_uid = 88af48b85bSJaegeuk Kim make_kuid(&init_user_ns, 89af48b85bSJaegeuk Kim le32_to_cpu(entry->e_id)); 90af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 91af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry)); 92af48b85bSJaegeuk Kim break; 93af48b85bSJaegeuk Kim case ACL_GROUP: 94af48b85bSJaegeuk Kim acl->a_entries[i].e_gid = 95af48b85bSJaegeuk Kim make_kgid(&init_user_ns, 96af48b85bSJaegeuk Kim le32_to_cpu(entry->e_id)); 97af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 98af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry)); 99af48b85bSJaegeuk Kim break; 100af48b85bSJaegeuk Kim default: 101af48b85bSJaegeuk Kim goto fail; 102af48b85bSJaegeuk Kim } 103af48b85bSJaegeuk Kim } 104af48b85bSJaegeuk Kim if ((char *)entry != end) 105af48b85bSJaegeuk Kim goto fail; 106af48b85bSJaegeuk Kim return acl; 107af48b85bSJaegeuk Kim fail: 108af48b85bSJaegeuk Kim posix_acl_release(acl); 109af48b85bSJaegeuk Kim return ERR_PTR(-EINVAL); 110af48b85bSJaegeuk Kim } 111af48b85bSJaegeuk Kim 112af48b85bSJaegeuk Kim static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) 113af48b85bSJaegeuk Kim { 114af48b85bSJaegeuk Kim struct f2fs_acl_header *f2fs_acl; 115af48b85bSJaegeuk Kim struct f2fs_acl_entry *entry; 116af48b85bSJaegeuk Kim int i; 117af48b85bSJaegeuk Kim 118af48b85bSJaegeuk Kim f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * 119af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry), GFP_KERNEL); 120af48b85bSJaegeuk Kim if (!f2fs_acl) 121af48b85bSJaegeuk Kim return ERR_PTR(-ENOMEM); 122af48b85bSJaegeuk Kim 123af48b85bSJaegeuk Kim f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION); 124af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)(f2fs_acl + 1); 125af48b85bSJaegeuk Kim 126af48b85bSJaegeuk Kim for (i = 0; i < acl->a_count; i++) { 127af48b85bSJaegeuk Kim 128af48b85bSJaegeuk Kim entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag); 129af48b85bSJaegeuk Kim entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm); 130af48b85bSJaegeuk Kim 131af48b85bSJaegeuk Kim switch (acl->a_entries[i].e_tag) { 132af48b85bSJaegeuk Kim case ACL_USER: 133af48b85bSJaegeuk Kim entry->e_id = cpu_to_le32( 134af48b85bSJaegeuk Kim from_kuid(&init_user_ns, 135af48b85bSJaegeuk Kim acl->a_entries[i].e_uid)); 136af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 137af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry)); 138af48b85bSJaegeuk Kim break; 139af48b85bSJaegeuk Kim case ACL_GROUP: 140af48b85bSJaegeuk Kim entry->e_id = cpu_to_le32( 141af48b85bSJaegeuk Kim from_kgid(&init_user_ns, 142af48b85bSJaegeuk Kim acl->a_entries[i].e_gid)); 143af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 144af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry)); 145af48b85bSJaegeuk Kim break; 146af48b85bSJaegeuk Kim case ACL_USER_OBJ: 147af48b85bSJaegeuk Kim case ACL_GROUP_OBJ: 148af48b85bSJaegeuk Kim case ACL_MASK: 149af48b85bSJaegeuk Kim case ACL_OTHER: 150af48b85bSJaegeuk Kim entry = (struct f2fs_acl_entry *)((char *)entry + 151af48b85bSJaegeuk Kim sizeof(struct f2fs_acl_entry_short)); 152af48b85bSJaegeuk Kim break; 153af48b85bSJaegeuk Kim default: 154af48b85bSJaegeuk Kim goto fail; 155af48b85bSJaegeuk Kim } 156af48b85bSJaegeuk Kim } 157af48b85bSJaegeuk Kim *size = f2fs_acl_size(acl->a_count); 158af48b85bSJaegeuk Kim return (void *)f2fs_acl; 159af48b85bSJaegeuk Kim 160af48b85bSJaegeuk Kim fail: 161af48b85bSJaegeuk Kim kfree(f2fs_acl); 162af48b85bSJaegeuk Kim return ERR_PTR(-EINVAL); 163af48b85bSJaegeuk Kim } 164af48b85bSJaegeuk Kim 165af48b85bSJaegeuk Kim struct posix_acl *f2fs_get_acl(struct inode *inode, int type) 166af48b85bSJaegeuk Kim { 167af48b85bSJaegeuk Kim int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 168af48b85bSJaegeuk Kim void *value = NULL; 169af48b85bSJaegeuk Kim struct posix_acl *acl; 170af48b85bSJaegeuk Kim int retval; 171af48b85bSJaegeuk Kim 172af48b85bSJaegeuk Kim if (type == ACL_TYPE_ACCESS) 173af48b85bSJaegeuk Kim name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 174af48b85bSJaegeuk Kim 175af48b85bSJaegeuk Kim retval = f2fs_getxattr(inode, name_index, "", NULL, 0); 176af48b85bSJaegeuk Kim if (retval > 0) { 177*808a1d74SJaegeuk Kim value = kmalloc(retval, GFP_F2FS_ZERO); 178af48b85bSJaegeuk Kim if (!value) 179af48b85bSJaegeuk Kim return ERR_PTR(-ENOMEM); 180af48b85bSJaegeuk Kim retval = f2fs_getxattr(inode, name_index, "", value, retval); 181af48b85bSJaegeuk Kim } 182af48b85bSJaegeuk Kim 183c1b75eabSJaegeuk Kim if (retval > 0) 184c1b75eabSJaegeuk Kim acl = f2fs_acl_from_disk(value, retval); 185c1b75eabSJaegeuk Kim else if (retval == -ENODATA) 186af48b85bSJaegeuk Kim acl = NULL; 187af48b85bSJaegeuk Kim else 188af48b85bSJaegeuk Kim acl = ERR_PTR(retval); 189af48b85bSJaegeuk Kim kfree(value); 190c1b75eabSJaegeuk Kim 191af48b85bSJaegeuk Kim if (!IS_ERR(acl)) 192af48b85bSJaegeuk Kim set_cached_acl(inode, type, acl); 193af48b85bSJaegeuk Kim 194af48b85bSJaegeuk Kim return acl; 195af48b85bSJaegeuk Kim } 196af48b85bSJaegeuk Kim 197a6dda0e6SChristoph Hellwig static int __f2fs_set_acl(struct inode *inode, int type, 1982ed2d5b3SJaegeuk Kim struct posix_acl *acl, struct page *ipage) 199af48b85bSJaegeuk Kim { 200af48b85bSJaegeuk Kim struct f2fs_inode_info *fi = F2FS_I(inode); 201af48b85bSJaegeuk Kim int name_index; 202af48b85bSJaegeuk Kim void *value = NULL; 203af48b85bSJaegeuk Kim size_t size = 0; 204af48b85bSJaegeuk Kim int error; 205af48b85bSJaegeuk Kim 206af48b85bSJaegeuk Kim switch (type) { 207af48b85bSJaegeuk Kim case ACL_TYPE_ACCESS: 208af48b85bSJaegeuk Kim name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; 209af48b85bSJaegeuk Kim if (acl) { 210af48b85bSJaegeuk Kim error = posix_acl_equiv_mode(acl, &inode->i_mode); 211af48b85bSJaegeuk Kim if (error < 0) 212af48b85bSJaegeuk Kim return error; 213af48b85bSJaegeuk Kim set_acl_inode(fi, inode->i_mode); 214af48b85bSJaegeuk Kim if (error == 0) 215af48b85bSJaegeuk Kim acl = NULL; 216af48b85bSJaegeuk Kim } 217af48b85bSJaegeuk Kim break; 218af48b85bSJaegeuk Kim 219af48b85bSJaegeuk Kim case ACL_TYPE_DEFAULT: 220af48b85bSJaegeuk Kim name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; 221af48b85bSJaegeuk Kim if (!S_ISDIR(inode->i_mode)) 222af48b85bSJaegeuk Kim return acl ? -EACCES : 0; 223af48b85bSJaegeuk Kim break; 224af48b85bSJaegeuk Kim 225af48b85bSJaegeuk Kim default: 226af48b85bSJaegeuk Kim return -EINVAL; 227af48b85bSJaegeuk Kim } 228af48b85bSJaegeuk Kim 229af48b85bSJaegeuk Kim if (acl) { 230af48b85bSJaegeuk Kim value = f2fs_acl_to_disk(acl, &size); 231af48b85bSJaegeuk Kim if (IS_ERR(value)) { 232af48b85bSJaegeuk Kim cond_clear_inode_flag(fi, FI_ACL_MODE); 233af48b85bSJaegeuk Kim return (int)PTR_ERR(value); 234af48b85bSJaegeuk Kim } 235af48b85bSJaegeuk Kim } 236af48b85bSJaegeuk Kim 2372ed2d5b3SJaegeuk Kim error = f2fs_setxattr(inode, name_index, "", value, size, ipage); 238af48b85bSJaegeuk Kim 239af48b85bSJaegeuk Kim kfree(value); 240af48b85bSJaegeuk Kim if (!error) 241af48b85bSJaegeuk Kim set_cached_acl(inode, type, acl); 242af48b85bSJaegeuk Kim 243af48b85bSJaegeuk Kim cond_clear_inode_flag(fi, FI_ACL_MODE); 244af48b85bSJaegeuk Kim return error; 245af48b85bSJaegeuk Kim } 246af48b85bSJaegeuk Kim 247a6dda0e6SChristoph Hellwig int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type) 248a6dda0e6SChristoph Hellwig { 249a6dda0e6SChristoph Hellwig return __f2fs_set_acl(inode, type, acl, NULL); 250a6dda0e6SChristoph Hellwig } 251a6dda0e6SChristoph Hellwig 2522ed2d5b3SJaegeuk Kim int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage) 253af48b85bSJaegeuk Kim { 254a6dda0e6SChristoph Hellwig struct posix_acl *default_acl, *acl; 255af48b85bSJaegeuk Kim int error = 0; 256af48b85bSJaegeuk Kim 257a6dda0e6SChristoph Hellwig error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); 258af48b85bSJaegeuk Kim if (error) 259af48b85bSJaegeuk Kim return error; 260b8b60e1aSJaegeuk Kim 261a6dda0e6SChristoph Hellwig if (default_acl) { 262a6dda0e6SChristoph Hellwig error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl, 263a6dda0e6SChristoph Hellwig ipage); 264a6dda0e6SChristoph Hellwig posix_acl_release(default_acl); 265af48b85bSJaegeuk Kim } 266af48b85bSJaegeuk Kim if (acl) { 267af48b85bSJaegeuk Kim if (error) 268a6dda0e6SChristoph Hellwig error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl, 269a6dda0e6SChristoph Hellwig ipage); 270af48b85bSJaegeuk Kim posix_acl_release(acl); 271a6dda0e6SChristoph Hellwig } 272a6dda0e6SChristoph Hellwig 273af48b85bSJaegeuk Kim return error; 274af48b85bSJaegeuk Kim } 275