305 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * linux/fs/ext4/acl.c4 *5 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>6 */7 8#include <linux/quotaops.h>9#include "ext4_jbd2.h"10#include "ext4.h"11#include "xattr.h"12#include "acl.h"13 14/*15 * Convert from filesystem to in-memory representation.16 */17static struct posix_acl *18ext4_acl_from_disk(const void *value, size_t size)19{20 const char *end = (char *)value + size;21 int n, count;22 struct posix_acl *acl;23 24 if (!value)25 return NULL;26 if (size < sizeof(ext4_acl_header))27 return ERR_PTR(-EINVAL);28 if (((ext4_acl_header *)value)->a_version !=29 cpu_to_le32(EXT4_ACL_VERSION))30 return ERR_PTR(-EINVAL);31 value = (char *)value + sizeof(ext4_acl_header);32 count = ext4_acl_count(size);33 if (count < 0)34 return ERR_PTR(-EINVAL);35 if (count == 0)36 return NULL;37 acl = posix_acl_alloc(count, GFP_NOFS);38 if (!acl)39 return ERR_PTR(-ENOMEM);40 for (n = 0; n < count; n++) {41 ext4_acl_entry *entry =42 (ext4_acl_entry *)value;43 if ((char *)value + sizeof(ext4_acl_entry_short) > end)44 goto fail;45 acl->a_entries[n].e_tag = le16_to_cpu(entry->e_tag);46 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);47 48 switch (acl->a_entries[n].e_tag) {49 case ACL_USER_OBJ:50 case ACL_GROUP_OBJ:51 case ACL_MASK:52 case ACL_OTHER:53 value = (char *)value +54 sizeof(ext4_acl_entry_short);55 break;56 57 case ACL_USER:58 value = (char *)value + sizeof(ext4_acl_entry);59 if ((char *)value > end)60 goto fail;61 acl->a_entries[n].e_uid =62 make_kuid(&init_user_ns,63 le32_to_cpu(entry->e_id));64 break;65 case ACL_GROUP:66 value = (char *)value + sizeof(ext4_acl_entry);67 if ((char *)value > end)68 goto fail;69 acl->a_entries[n].e_gid =70 make_kgid(&init_user_ns,71 le32_to_cpu(entry->e_id));72 break;73 74 default:75 goto fail;76 }77 }78 if (value != end)79 goto fail;80 return acl;81 82fail:83 posix_acl_release(acl);84 return ERR_PTR(-EINVAL);85}86 87/*88 * Convert from in-memory to filesystem representation.89 */90static void *91ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)92{93 ext4_acl_header *ext_acl;94 char *e;95 size_t n;96 97 *size = ext4_acl_size(acl->a_count);98 ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *99 sizeof(ext4_acl_entry), GFP_NOFS);100 if (!ext_acl)101 return ERR_PTR(-ENOMEM);102 ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);103 e = (char *)ext_acl + sizeof(ext4_acl_header);104 for (n = 0; n < acl->a_count; n++) {105 const struct posix_acl_entry *acl_e = &acl->a_entries[n];106 ext4_acl_entry *entry = (ext4_acl_entry *)e;107 entry->e_tag = cpu_to_le16(acl_e->e_tag);108 entry->e_perm = cpu_to_le16(acl_e->e_perm);109 switch (acl_e->e_tag) {110 case ACL_USER:111 entry->e_id = cpu_to_le32(112 from_kuid(&init_user_ns, acl_e->e_uid));113 e += sizeof(ext4_acl_entry);114 break;115 case ACL_GROUP:116 entry->e_id = cpu_to_le32(117 from_kgid(&init_user_ns, acl_e->e_gid));118 e += sizeof(ext4_acl_entry);119 break;120 121 case ACL_USER_OBJ:122 case ACL_GROUP_OBJ:123 case ACL_MASK:124 case ACL_OTHER:125 e += sizeof(ext4_acl_entry_short);126 break;127 128 default:129 goto fail;130 }131 }132 return (char *)ext_acl;133 134fail:135 kfree(ext_acl);136 return ERR_PTR(-EINVAL);137}138 139/*140 * Inode operation get_posix_acl().141 *142 * inode->i_rwsem: don't care143 */144struct posix_acl *145ext4_get_acl(struct inode *inode, int type, bool rcu)146{147 int name_index;148 char *value = NULL;149 struct posix_acl *acl;150 int retval;151 152 if (rcu)153 return ERR_PTR(-ECHILD);154 155 switch (type) {156 case ACL_TYPE_ACCESS:157 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;158 break;159 case ACL_TYPE_DEFAULT:160 name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;161 break;162 default:163 BUG();164 }165 retval = ext4_xattr_get(inode, name_index, "", NULL, 0);166 if (retval > 0) {167 value = kmalloc(retval, GFP_NOFS);168 if (!value)169 return ERR_PTR(-ENOMEM);170 retval = ext4_xattr_get(inode, name_index, "", value, retval);171 }172 if (retval > 0)173 acl = ext4_acl_from_disk(value, retval);174 else if (retval == -ENODATA || retval == -ENOSYS)175 acl = NULL;176 else177 acl = ERR_PTR(retval);178 kfree(value);179 180 return acl;181}182 183/*184 * Set the access or default ACL of an inode.185 *186 * inode->i_rwsem: down unless called from ext4_new_inode187 */188static int189__ext4_set_acl(handle_t *handle, struct inode *inode, int type,190 struct posix_acl *acl, int xattr_flags)191{192 int name_index;193 void *value = NULL;194 size_t size = 0;195 int error;196 197 switch (type) {198 case ACL_TYPE_ACCESS:199 name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;200 break;201 202 case ACL_TYPE_DEFAULT:203 name_index = EXT4_XATTR_INDEX_POSIX_ACL_DEFAULT;204 if (!S_ISDIR(inode->i_mode))205 return acl ? -EACCES : 0;206 break;207 208 default:209 return -EINVAL;210 }211 if (acl) {212 value = ext4_acl_to_disk(acl, &size);213 if (IS_ERR(value))214 return (int)PTR_ERR(value);215 }216 217 error = ext4_xattr_set_handle(handle, inode, name_index, "",218 value, size, xattr_flags);219 220 kfree(value);221 if (!error)222 set_cached_acl(inode, type, acl);223 224 return error;225}226 227int228ext4_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,229 struct posix_acl *acl, int type)230{231 handle_t *handle;232 int error, credits, retries = 0;233 size_t acl_size = acl ? ext4_acl_size(acl->a_count) : 0;234 struct inode *inode = d_inode(dentry);235 umode_t mode = inode->i_mode;236 int update_mode = 0;237 238 error = dquot_initialize(inode);239 if (error)240 return error;241retry:242 error = ext4_xattr_set_credits(inode, acl_size, false /* is_create */,243 &credits);244 if (error)245 return error;246 247 handle = ext4_journal_start(inode, EXT4_HT_XATTR, credits);248 if (IS_ERR(handle))249 return PTR_ERR(handle);250 251 if ((type == ACL_TYPE_ACCESS) && acl) {252 error = posix_acl_update_mode(idmap, inode, &mode, &acl);253 if (error)254 goto out_stop;255 if (mode != inode->i_mode)256 update_mode = 1;257 }258 259 error = __ext4_set_acl(handle, inode, type, acl, 0 /* xattr_flags */);260 if (!error && update_mode) {261 inode->i_mode = mode;262 inode_set_ctime_current(inode);263 error = ext4_mark_inode_dirty(handle, inode);264 }265out_stop:266 ext4_journal_stop(handle);267 if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))268 goto retry;269 return error;270}271 272/*273 * Initialize the ACLs of a new inode. Called from ext4_new_inode.274 *275 * dir->i_rwsem: down276 * inode->i_rwsem: up (access to inode is still exclusive)277 */278int279ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)280{281 struct posix_acl *default_acl, *acl;282 int error;283 284 error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);285 if (error)286 return error;287 288 if (default_acl) {289 error = __ext4_set_acl(handle, inode, ACL_TYPE_DEFAULT,290 default_acl, XATTR_CREATE);291 posix_acl_release(default_acl);292 } else {293 inode->i_default_acl = NULL;294 }295 if (acl) {296 if (!error)297 error = __ext4_set_acl(handle, inode, ACL_TYPE_ACCESS,298 acl, XATTR_CREATE);299 posix_acl_release(acl);300 } else {301 inode->i_acl = NULL;302 }303 return error;304}305