cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

acl.c (9434B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * acl.c
      4 *
      5 * Copyright (C) 2004, 2008 Oracle.  All rights reserved.
      6 *
      7 * CREDITS:
      8 * Lots of code in this file is copy from linux/fs/ext3/acl.c.
      9 * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
     10 */
     11
     12#include <linux/init.h>
     13#include <linux/module.h>
     14#include <linux/slab.h>
     15#include <linux/string.h>
     16
     17#include <cluster/masklog.h>
     18
     19#include "ocfs2.h"
     20#include "alloc.h"
     21#include "dlmglue.h"
     22#include "file.h"
     23#include "inode.h"
     24#include "journal.h"
     25#include "ocfs2_fs.h"
     26
     27#include "xattr.h"
     28#include "acl.h"
     29
     30/*
     31 * Convert from xattr value to acl struct.
     32 */
     33static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size)
     34{
     35	int n, count;
     36	struct posix_acl *acl;
     37
     38	if (!value)
     39		return NULL;
     40	if (size < sizeof(struct posix_acl_entry))
     41		return ERR_PTR(-EINVAL);
     42
     43	count = size / sizeof(struct posix_acl_entry);
     44
     45	acl = posix_acl_alloc(count, GFP_NOFS);
     46	if (!acl)
     47		return ERR_PTR(-ENOMEM);
     48	for (n = 0; n < count; n++) {
     49		struct ocfs2_acl_entry *entry =
     50			(struct ocfs2_acl_entry *)value;
     51
     52		acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
     53		acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
     54		switch(acl->a_entries[n].e_tag) {
     55		case ACL_USER:
     56			acl->a_entries[n].e_uid =
     57				make_kuid(&init_user_ns,
     58					  le32_to_cpu(entry->e_id));
     59			break;
     60		case ACL_GROUP:
     61			acl->a_entries[n].e_gid =
     62				make_kgid(&init_user_ns,
     63					  le32_to_cpu(entry->e_id));
     64			break;
     65		default:
     66			break;
     67		}
     68		value += sizeof(struct posix_acl_entry);
     69
     70	}
     71	return acl;
     72}
     73
     74/*
     75 * Convert acl struct to xattr value.
     76 */
     77static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)
     78{
     79	struct ocfs2_acl_entry *entry = NULL;
     80	char *ocfs2_acl;
     81	size_t n;
     82
     83	*size = acl->a_count * sizeof(struct posix_acl_entry);
     84
     85	ocfs2_acl = kmalloc(*size, GFP_NOFS);
     86	if (!ocfs2_acl)
     87		return ERR_PTR(-ENOMEM);
     88
     89	entry = (struct ocfs2_acl_entry *)ocfs2_acl;
     90	for (n = 0; n < acl->a_count; n++, entry++) {
     91		entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
     92		entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
     93		switch(acl->a_entries[n].e_tag) {
     94		case ACL_USER:
     95			entry->e_id = cpu_to_le32(
     96				from_kuid(&init_user_ns,
     97					  acl->a_entries[n].e_uid));
     98			break;
     99		case ACL_GROUP:
    100			entry->e_id = cpu_to_le32(
    101				from_kgid(&init_user_ns,
    102					  acl->a_entries[n].e_gid));
    103			break;
    104		default:
    105			entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
    106			break;
    107		}
    108	}
    109	return ocfs2_acl;
    110}
    111
    112static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode,
    113					      int type,
    114					      struct buffer_head *di_bh)
    115{
    116	int name_index;
    117	char *value = NULL;
    118	struct posix_acl *acl;
    119	int retval;
    120
    121	switch (type) {
    122	case ACL_TYPE_ACCESS:
    123		name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
    124		break;
    125	case ACL_TYPE_DEFAULT:
    126		name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
    127		break;
    128	default:
    129		return ERR_PTR(-EINVAL);
    130	}
    131
    132	retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, "", NULL, 0);
    133	if (retval > 0) {
    134		value = kmalloc(retval, GFP_NOFS);
    135		if (!value)
    136			return ERR_PTR(-ENOMEM);
    137		retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index,
    138						"", value, retval);
    139	}
    140
    141	if (retval > 0)
    142		acl = ocfs2_acl_from_xattr(value, retval);
    143	else if (retval == -ENODATA || retval == 0)
    144		acl = NULL;
    145	else
    146		acl = ERR_PTR(retval);
    147
    148	kfree(value);
    149
    150	return acl;
    151}
    152
    153/*
    154 * Helper function to set i_mode in memory and disk. Some call paths
    155 * will not have di_bh or a journal handle to pass, in which case it
    156 * will create it's own.
    157 */
    158static int ocfs2_acl_set_mode(struct inode *inode, struct buffer_head *di_bh,
    159			      handle_t *handle, umode_t new_mode)
    160{
    161	int ret, commit_handle = 0;
    162	struct ocfs2_dinode *di;
    163
    164	if (di_bh == NULL) {
    165		ret = ocfs2_read_inode_block(inode, &di_bh);
    166		if (ret) {
    167			mlog_errno(ret);
    168			goto out;
    169		}
    170	} else
    171		get_bh(di_bh);
    172
    173	if (handle == NULL) {
    174		handle = ocfs2_start_trans(OCFS2_SB(inode->i_sb),
    175					   OCFS2_INODE_UPDATE_CREDITS);
    176		if (IS_ERR(handle)) {
    177			ret = PTR_ERR(handle);
    178			mlog_errno(ret);
    179			goto out_brelse;
    180		}
    181
    182		commit_handle = 1;
    183	}
    184
    185	di = (struct ocfs2_dinode *)di_bh->b_data;
    186	ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode), di_bh,
    187				      OCFS2_JOURNAL_ACCESS_WRITE);
    188	if (ret) {
    189		mlog_errno(ret);
    190		goto out_commit;
    191	}
    192
    193	inode->i_mode = new_mode;
    194	inode->i_ctime = current_time(inode);
    195	di->i_mode = cpu_to_le16(inode->i_mode);
    196	di->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec);
    197	di->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec);
    198	ocfs2_update_inode_fsync_trans(handle, inode, 0);
    199
    200	ocfs2_journal_dirty(handle, di_bh);
    201
    202out_commit:
    203	if (commit_handle)
    204		ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle);
    205out_brelse:
    206	brelse(di_bh);
    207out:
    208	return ret;
    209}
    210
    211/*
    212 * Set the access or default ACL of an inode.
    213 */
    214static int ocfs2_set_acl(handle_t *handle,
    215			 struct inode *inode,
    216			 struct buffer_head *di_bh,
    217			 int type,
    218			 struct posix_acl *acl,
    219			 struct ocfs2_alloc_context *meta_ac,
    220			 struct ocfs2_alloc_context *data_ac)
    221{
    222	int name_index;
    223	void *value = NULL;
    224	size_t size = 0;
    225	int ret;
    226
    227	if (S_ISLNK(inode->i_mode))
    228		return -EOPNOTSUPP;
    229
    230	switch (type) {
    231	case ACL_TYPE_ACCESS:
    232		name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
    233		break;
    234	case ACL_TYPE_DEFAULT:
    235		name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
    236		if (!S_ISDIR(inode->i_mode))
    237			return acl ? -EACCES : 0;
    238		break;
    239	default:
    240		return -EINVAL;
    241	}
    242
    243	if (acl) {
    244		value = ocfs2_acl_to_xattr(acl, &size);
    245		if (IS_ERR(value))
    246			return (int)PTR_ERR(value);
    247	}
    248
    249	if (handle)
    250		ret = ocfs2_xattr_set_handle(handle, inode, di_bh, name_index,
    251					     "", value, size, 0,
    252					     meta_ac, data_ac);
    253	else
    254		ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0);
    255
    256	kfree(value);
    257	if (!ret)
    258		set_cached_acl(inode, type, acl);
    259
    260	return ret;
    261}
    262
    263int ocfs2_iop_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
    264		      struct posix_acl *acl, int type)
    265{
    266	struct buffer_head *bh = NULL;
    267	int status, had_lock;
    268	struct ocfs2_lock_holder oh;
    269
    270	had_lock = ocfs2_inode_lock_tracker(inode, &bh, 1, &oh);
    271	if (had_lock < 0)
    272		return had_lock;
    273	if (type == ACL_TYPE_ACCESS && acl) {
    274		umode_t mode;
    275
    276		status = posix_acl_update_mode(&init_user_ns, inode, &mode,
    277					       &acl);
    278		if (status)
    279			goto unlock;
    280
    281		status = ocfs2_acl_set_mode(inode, bh, NULL, mode);
    282		if (status)
    283			goto unlock;
    284	}
    285	status = ocfs2_set_acl(NULL, inode, bh, type, acl, NULL, NULL);
    286unlock:
    287	ocfs2_inode_unlock_tracker(inode, 1, &oh, had_lock);
    288	brelse(bh);
    289	return status;
    290}
    291
    292struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu)
    293{
    294	struct ocfs2_super *osb;
    295	struct buffer_head *di_bh = NULL;
    296	struct posix_acl *acl;
    297	int had_lock;
    298	struct ocfs2_lock_holder oh;
    299
    300	if (rcu)
    301		return ERR_PTR(-ECHILD);
    302
    303	osb = OCFS2_SB(inode->i_sb);
    304	if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
    305		return NULL;
    306
    307	had_lock = ocfs2_inode_lock_tracker(inode, &di_bh, 0, &oh);
    308	if (had_lock < 0)
    309		return ERR_PTR(had_lock);
    310
    311	down_read(&OCFS2_I(inode)->ip_xattr_sem);
    312	acl = ocfs2_get_acl_nolock(inode, type, di_bh);
    313	up_read(&OCFS2_I(inode)->ip_xattr_sem);
    314
    315	ocfs2_inode_unlock_tracker(inode, 0, &oh, had_lock);
    316	brelse(di_bh);
    317	return acl;
    318}
    319
    320int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)
    321{
    322	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
    323	struct posix_acl *acl;
    324	int ret;
    325
    326	if (S_ISLNK(inode->i_mode))
    327		return -EOPNOTSUPP;
    328
    329	if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
    330		return 0;
    331
    332	down_read(&OCFS2_I(inode)->ip_xattr_sem);
    333	acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, bh);
    334	up_read(&OCFS2_I(inode)->ip_xattr_sem);
    335	if (IS_ERR_OR_NULL(acl))
    336		return PTR_ERR_OR_ZERO(acl);
    337	ret = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
    338	if (ret)
    339		return ret;
    340	ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS,
    341			    acl, NULL, NULL);
    342	posix_acl_release(acl);
    343	return ret;
    344}
    345
    346/*
    347 * Initialize the ACLs of a new inode. If parent directory has default ACL,
    348 * then clone to new inode. Called from ocfs2_mknod.
    349 */
    350int ocfs2_init_acl(handle_t *handle,
    351		   struct inode *inode,
    352		   struct inode *dir,
    353		   struct buffer_head *di_bh,
    354		   struct buffer_head *dir_bh,
    355		   struct ocfs2_alloc_context *meta_ac,
    356		   struct ocfs2_alloc_context *data_ac)
    357{
    358	struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
    359	struct posix_acl *acl = NULL;
    360	int ret = 0, ret2;
    361	umode_t mode;
    362
    363	if (!S_ISLNK(inode->i_mode)) {
    364		if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
    365			down_read(&OCFS2_I(dir)->ip_xattr_sem);
    366			acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
    367						   dir_bh);
    368			up_read(&OCFS2_I(dir)->ip_xattr_sem);
    369			if (IS_ERR(acl))
    370				return PTR_ERR(acl);
    371		}
    372		if (!acl) {
    373			mode = inode->i_mode & ~current_umask();
    374			ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
    375			if (ret) {
    376				mlog_errno(ret);
    377				goto cleanup;
    378			}
    379		}
    380	}
    381	if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) {
    382		if (S_ISDIR(inode->i_mode)) {
    383			ret = ocfs2_set_acl(handle, inode, di_bh,
    384					    ACL_TYPE_DEFAULT, acl,
    385					    meta_ac, data_ac);
    386			if (ret)
    387				goto cleanup;
    388		}
    389		mode = inode->i_mode;
    390		ret = __posix_acl_create(&acl, GFP_NOFS, &mode);
    391		if (ret < 0)
    392			return ret;
    393
    394		ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode);
    395		if (ret2) {
    396			mlog_errno(ret2);
    397			ret = ret2;
    398			goto cleanup;
    399		}
    400		if (ret > 0) {
    401			ret = ocfs2_set_acl(handle, inode,
    402					    di_bh, ACL_TYPE_ACCESS,
    403					    acl, meta_ac, data_ac);
    404		}
    405	}
    406cleanup:
    407	posix_acl_release(acl);
    408	return ret;
    409}