277 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * linux/fs/ext2/acl.c4 *5 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>6 */7 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 */19static struct posix_acl *20ext2_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 83fail:84 posix_acl_release(acl);85 return ERR_PTR(-EINVAL);86}87 88/*89 * Convert from in-memory to filesystem representation.90 */91static void *92ext2_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 135fail:136 kfree(ext_acl);137 return ERR_PTR(-EINVAL);138}139 140/*141 * inode->i_mutex: don't care142 */143struct posix_acl *144ext2_get_acl(struct inode *inode, int type, bool rcu)145{146 int name_index;147 char *value = NULL;148 struct posix_acl *acl;149 int retval;150 151 if (rcu)152 return ERR_PTR(-ECHILD);153 154 switch (type) {155 case ACL_TYPE_ACCESS:156 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;157 break;158 case ACL_TYPE_DEFAULT:159 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;160 break;161 default:162 BUG();163 }164 retval = ext2_xattr_get(inode, name_index, "", NULL, 0);165 if (retval > 0) {166 value = kmalloc(retval, GFP_KERNEL);167 if (!value)168 return ERR_PTR(-ENOMEM);169 retval = ext2_xattr_get(inode, name_index, "", value, retval);170 }171 if (retval > 0)172 acl = ext2_acl_from_disk(value, retval);173 else if (retval == -ENODATA || retval == -ENOSYS)174 acl = NULL;175 else176 acl = ERR_PTR(retval);177 kfree(value);178 179 return acl;180}181 182static int183__ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)184{185 int name_index;186 void *value = NULL;187 size_t size = 0;188 int error;189 190 switch(type) {191 case ACL_TYPE_ACCESS:192 name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;193 break;194 195 case ACL_TYPE_DEFAULT:196 name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;197 if (!S_ISDIR(inode->i_mode))198 return acl ? -EACCES : 0;199 break;200 201 default:202 return -EINVAL;203 }204 if (acl) {205 value = ext2_acl_to_disk(acl, &size);206 if (IS_ERR(value))207 return (int)PTR_ERR(value);208 }209 210 error = ext2_xattr_set(inode, name_index, "", value, size, 0);211 212 kfree(value);213 if (!error)214 set_cached_acl(inode, type, acl);215 return error;216}217 218/*219 * inode->i_mutex: down220 */221int222ext2_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,223 struct posix_acl *acl, int type)224{225 int error;226 int update_mode = 0;227 struct inode *inode = d_inode(dentry);228 umode_t mode = inode->i_mode;229 230 if (type == ACL_TYPE_ACCESS && acl) {231 error = posix_acl_update_mode(&nop_mnt_idmap, inode, &mode,232 &acl);233 if (error)234 return error;235 update_mode = 1;236 }237 error = __ext2_set_acl(inode, acl, type);238 if (!error && update_mode) {239 inode->i_mode = mode;240 inode_set_ctime_current(inode);241 mark_inode_dirty(inode);242 }243 return error;244}245 246/*247 * Initialize the ACLs of a new inode. Called from ext2_new_inode.248 *249 * dir->i_mutex: down250 * inode->i_mutex: up (access to inode is still exclusive)251 */252int253ext2_init_acl(struct inode *inode, struct inode *dir)254{255 struct posix_acl *default_acl, *acl;256 int error;257 258 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);259 if (error)260 return error;261 262 if (default_acl) {263 error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);264 posix_acl_release(default_acl);265 } else {266 inode->i_default_acl = NULL;267 }268 if (acl) {269 if (!error)270 error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);271 posix_acl_release(acl);272 } else {273 inode->i_acl = NULL;274 }275 return error;276}277