brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 87f1be9 Raw
451 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include "bcachefs.h"4 5#include "acl.h"6#include "xattr.h"7 8#include <linux/posix_acl.h>9 10static const char * const acl_types[] = {11	[ACL_USER_OBJ]	= "user_obj",12	[ACL_USER]	= "user",13	[ACL_GROUP_OBJ]	= "group_obj",14	[ACL_GROUP]	= "group",15	[ACL_MASK]	= "mask",16	[ACL_OTHER]	= "other",17	NULL,18};19 20void bch2_acl_to_text(struct printbuf *out, const void *value, size_t size)21{22	const void *p, *end = value + size;23 24	if (!value ||25	    size < sizeof(bch_acl_header) ||26	    ((bch_acl_header *)value)->a_version != cpu_to_le32(BCH_ACL_VERSION))27		return;28 29	p = value + sizeof(bch_acl_header);30	while (p < end) {31		const bch_acl_entry *in = p;32		unsigned tag = le16_to_cpu(in->e_tag);33 34		prt_str(out, acl_types[tag]);35 36		switch (tag) {37		case ACL_USER_OBJ:38		case ACL_GROUP_OBJ:39		case ACL_MASK:40		case ACL_OTHER:41			p += sizeof(bch_acl_entry_short);42			break;43		case ACL_USER:44			prt_printf(out, " uid %u", le32_to_cpu(in->e_id));45			p += sizeof(bch_acl_entry);46			break;47		case ACL_GROUP:48			prt_printf(out, " gid %u", le32_to_cpu(in->e_id));49			p += sizeof(bch_acl_entry);50			break;51		}52 53		prt_printf(out, " %o", le16_to_cpu(in->e_perm));54 55		if (p != end)56			prt_char(out, ' ');57	}58}59 60#ifdef CONFIG_BCACHEFS_POSIX_ACL61 62#include "fs.h"63 64#include <linux/fs.h>65#include <linux/posix_acl_xattr.h>66#include <linux/sched.h>67#include <linux/slab.h>68 69static inline size_t bch2_acl_size(unsigned nr_short, unsigned nr_long)70{71	return sizeof(bch_acl_header) +72		sizeof(bch_acl_entry_short) * nr_short +73		sizeof(bch_acl_entry) * nr_long;74}75 76static inline int acl_to_xattr_type(int type)77{78	switch (type) {79	case ACL_TYPE_ACCESS:80		return KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS;81	case ACL_TYPE_DEFAULT:82		return KEY_TYPE_XATTR_INDEX_POSIX_ACL_DEFAULT;83	default:84		BUG();85	}86}87 88/*89 * Convert from filesystem to in-memory representation.90 */91static struct posix_acl *bch2_acl_from_disk(struct btree_trans *trans,92					    const void *value, size_t size)93{94	const void *p, *end = value + size;95	struct posix_acl *acl;96	struct posix_acl_entry *out;97	unsigned count = 0;98	int ret;99 100	if (!value)101		return NULL;102	if (size < sizeof(bch_acl_header))103		goto invalid;104	if (((bch_acl_header *)value)->a_version !=105	    cpu_to_le32(BCH_ACL_VERSION))106		goto invalid;107 108	p = value + sizeof(bch_acl_header);109	while (p < end) {110		const bch_acl_entry *entry = p;111 112		if (p + sizeof(bch_acl_entry_short) > end)113			goto invalid;114 115		switch (le16_to_cpu(entry->e_tag)) {116		case ACL_USER_OBJ:117		case ACL_GROUP_OBJ:118		case ACL_MASK:119		case ACL_OTHER:120			p += sizeof(bch_acl_entry_short);121			break;122		case ACL_USER:123		case ACL_GROUP:124			p += sizeof(bch_acl_entry);125			break;126		default:127			goto invalid;128		}129 130		count++;131	}132 133	if (p > end)134		goto invalid;135 136	if (!count)137		return NULL;138 139	acl = allocate_dropping_locks(trans, ret,140			posix_acl_alloc(count, _gfp));141	if (!acl)142		return ERR_PTR(-ENOMEM);143	if (ret) {144		kfree(acl);145		return ERR_PTR(ret);146	}147 148	out = acl->a_entries;149 150	p = value + sizeof(bch_acl_header);151	while (p < end) {152		const bch_acl_entry *in = p;153 154		out->e_tag  = le16_to_cpu(in->e_tag);155		out->e_perm = le16_to_cpu(in->e_perm);156 157		switch (out->e_tag) {158		case ACL_USER_OBJ:159		case ACL_GROUP_OBJ:160		case ACL_MASK:161		case ACL_OTHER:162			p += sizeof(bch_acl_entry_short);163			break;164		case ACL_USER:165			out->e_uid = make_kuid(&init_user_ns,166					       le32_to_cpu(in->e_id));167			p += sizeof(bch_acl_entry);168			break;169		case ACL_GROUP:170			out->e_gid = make_kgid(&init_user_ns,171					       le32_to_cpu(in->e_id));172			p += sizeof(bch_acl_entry);173			break;174		}175 176		out++;177	}178 179	BUG_ON(out != acl->a_entries + acl->a_count);180 181	return acl;182invalid:183	pr_err("invalid acl entry");184	return ERR_PTR(-EINVAL);185}186 187#define acl_for_each_entry(acl, acl_e)			\188	for (acl_e = acl->a_entries;			\189	     acl_e < acl->a_entries + acl->a_count;	\190	     acl_e++)191 192/*193 * Convert from in-memory to filesystem representation.194 */195static struct bkey_i_xattr *196bch2_acl_to_xattr(struct btree_trans *trans,197		  const struct posix_acl *acl,198		  int type)199{200	struct bkey_i_xattr *xattr;201	bch_acl_header *acl_header;202	const struct posix_acl_entry *acl_e;203	void *outptr;204	unsigned nr_short = 0, nr_long = 0, acl_len, u64s;205 206	acl_for_each_entry(acl, acl_e) {207		switch (acl_e->e_tag) {208		case ACL_USER:209		case ACL_GROUP:210			nr_long++;211			break;212		case ACL_USER_OBJ:213		case ACL_GROUP_OBJ:214		case ACL_MASK:215		case ACL_OTHER:216			nr_short++;217			break;218		default:219			return ERR_PTR(-EINVAL);220		}221	}222 223	acl_len = bch2_acl_size(nr_short, nr_long);224	u64s = BKEY_U64s + xattr_val_u64s(0, acl_len);225 226	if (u64s > U8_MAX)227		return ERR_PTR(-E2BIG);228 229	xattr = bch2_trans_kmalloc(trans, u64s * sizeof(u64));230	if (IS_ERR(xattr))231		return xattr;232 233	bkey_xattr_init(&xattr->k_i);234	xattr->k.u64s		= u64s;235	xattr->v.x_type		= acl_to_xattr_type(type);236	xattr->v.x_name_len	= 0;237	xattr->v.x_val_len	= cpu_to_le16(acl_len);238 239	acl_header = xattr_val(&xattr->v);240	acl_header->a_version = cpu_to_le32(BCH_ACL_VERSION);241 242	outptr = (void *) acl_header + sizeof(*acl_header);243 244	acl_for_each_entry(acl, acl_e) {245		bch_acl_entry *entry = outptr;246 247		entry->e_tag = cpu_to_le16(acl_e->e_tag);248		entry->e_perm = cpu_to_le16(acl_e->e_perm);249		switch (acl_e->e_tag) {250		case ACL_USER:251			entry->e_id = cpu_to_le32(252				from_kuid(&init_user_ns, acl_e->e_uid));253			outptr += sizeof(bch_acl_entry);254			break;255		case ACL_GROUP:256			entry->e_id = cpu_to_le32(257				from_kgid(&init_user_ns, acl_e->e_gid));258			outptr += sizeof(bch_acl_entry);259			break;260 261		case ACL_USER_OBJ:262		case ACL_GROUP_OBJ:263		case ACL_MASK:264		case ACL_OTHER:265			outptr += sizeof(bch_acl_entry_short);266			break;267		}268	}269 270	BUG_ON(outptr != xattr_val(&xattr->v) + acl_len);271 272	return xattr;273}274 275struct posix_acl *bch2_get_acl(struct inode *vinode, int type, bool rcu)276{277	struct bch_inode_info *inode = to_bch_ei(vinode);278	struct bch_fs *c = inode->v.i_sb->s_fs_info;279	struct bch_hash_info hash = bch2_hash_info_init(c, &inode->ei_inode);280	struct xattr_search_key search = X_SEARCH(acl_to_xattr_type(type), "", 0);281	struct btree_iter iter = { NULL };282	struct posix_acl *acl = NULL;283 284	if (rcu)285		return ERR_PTR(-ECHILD);286 287	struct btree_trans *trans = bch2_trans_get(c);288retry:289	bch2_trans_begin(trans);290 291	struct bkey_s_c k = bch2_hash_lookup(trans, &iter, bch2_xattr_hash_desc,292					     &hash, inode_inum(inode), &search, 0);293	int ret = bkey_err(k);294	if (ret)295		goto err;296 297	struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);298	acl = bch2_acl_from_disk(trans, xattr_val(xattr.v),299				 le16_to_cpu(xattr.v->x_val_len));300	ret = PTR_ERR_OR_ZERO(acl);301err:302	if (bch2_err_matches(ret, BCH_ERR_transaction_restart))303		goto retry;304 305	if (ret)306		acl = !bch2_err_matches(ret, ENOENT) ? ERR_PTR(ret) : NULL;307 308	if (!IS_ERR_OR_NULL(acl))309		set_cached_acl(&inode->v, type, acl);310 311	bch2_trans_iter_exit(trans, &iter);312	bch2_trans_put(trans);313	return acl;314}315 316int bch2_set_acl_trans(struct btree_trans *trans, subvol_inum inum,317		       struct bch_inode_unpacked *inode_u,318		       struct posix_acl *acl, int type)319{320	struct bch_hash_info hash_info = bch2_hash_info_init(trans->c, inode_u);321	int ret;322 323	if (type == ACL_TYPE_DEFAULT &&324	    !S_ISDIR(inode_u->bi_mode))325		return acl ? -EACCES : 0;326 327	if (acl) {328		struct bkey_i_xattr *xattr =329			bch2_acl_to_xattr(trans, acl, type);330		if (IS_ERR(xattr))331			return PTR_ERR(xattr);332 333		ret = bch2_hash_set(trans, bch2_xattr_hash_desc, &hash_info,334				    inum, &xattr->k_i, 0);335	} else {336		struct xattr_search_key search =337			X_SEARCH(acl_to_xattr_type(type), "", 0);338 339		ret = bch2_hash_delete(trans, bch2_xattr_hash_desc, &hash_info,340				       inum, &search);341	}342 343	return bch2_err_matches(ret, ENOENT) ? 0 : ret;344}345 346int bch2_set_acl(struct mnt_idmap *idmap,347		 struct dentry *dentry,348		 struct posix_acl *_acl, int type)349{350	struct bch_inode_info *inode = to_bch_ei(dentry->d_inode);351	struct bch_fs *c = inode->v.i_sb->s_fs_info;352	struct btree_iter inode_iter = { NULL };353	struct bch_inode_unpacked inode_u;354	struct posix_acl *acl;355	umode_t mode;356	int ret;357 358	mutex_lock(&inode->ei_update_lock);359	struct btree_trans *trans = bch2_trans_get(c);360retry:361	bch2_trans_begin(trans);362	acl = _acl;363 364	ret   = bch2_subvol_is_ro_trans(trans, inode->ei_inum.subvol) ?:365		bch2_inode_peek(trans, &inode_iter, &inode_u, inode_inum(inode),366			      BTREE_ITER_intent);367	if (ret)368		goto btree_err;369 370	mode = inode_u.bi_mode;371 372	if (type == ACL_TYPE_ACCESS) {373		ret = posix_acl_update_mode(idmap, &inode->v, &mode, &acl);374		if (ret)375			goto btree_err;376	}377 378	ret = bch2_set_acl_trans(trans, inode_inum(inode), &inode_u, acl, type);379	if (ret)380		goto btree_err;381 382	inode_u.bi_ctime	= bch2_current_time(c);383	inode_u.bi_mode		= mode;384 385	ret =   bch2_inode_write(trans, &inode_iter, &inode_u) ?:386		bch2_trans_commit(trans, NULL, NULL, 0);387btree_err:388	bch2_trans_iter_exit(trans, &inode_iter);389 390	if (bch2_err_matches(ret, BCH_ERR_transaction_restart))391		goto retry;392	if (unlikely(ret))393		goto err;394 395	bch2_inode_update_after_write(trans, inode, &inode_u,396				      ATTR_CTIME|ATTR_MODE);397 398	set_cached_acl(&inode->v, type, acl);399err:400	bch2_trans_put(trans);401	mutex_unlock(&inode->ei_update_lock);402 403	return ret;404}405 406int bch2_acl_chmod(struct btree_trans *trans, subvol_inum inum,407		   struct bch_inode_unpacked *inode,408		   umode_t mode,409		   struct posix_acl **new_acl)410{411	struct bch_hash_info hash_info = bch2_hash_info_init(trans->c, inode);412	struct xattr_search_key search = X_SEARCH(KEY_TYPE_XATTR_INDEX_POSIX_ACL_ACCESS, "", 0);413	struct btree_iter iter;414	struct posix_acl *acl = NULL;415 416	struct bkey_s_c k = bch2_hash_lookup(trans, &iter, bch2_xattr_hash_desc,417			       &hash_info, inum, &search, BTREE_ITER_intent);418	int ret = bkey_err(k);419	if (ret)420		return bch2_err_matches(ret, ENOENT) ? 0 : ret;421 422	struct bkey_s_c_xattr xattr = bkey_s_c_to_xattr(k);423 424	acl = bch2_acl_from_disk(trans, xattr_val(xattr.v),425			le16_to_cpu(xattr.v->x_val_len));426	ret = PTR_ERR_OR_ZERO(acl);427	if (ret)428		goto err;429 430	ret = allocate_dropping_locks_errcode(trans, __posix_acl_chmod(&acl, _gfp, mode));431	if (ret)432		goto err;433 434	struct bkey_i_xattr *new = bch2_acl_to_xattr(trans, acl, ACL_TYPE_ACCESS);435	ret = PTR_ERR_OR_ZERO(new);436	if (ret)437		goto err;438 439	new->k.p = iter.pos;440	ret = bch2_trans_update(trans, &iter, &new->k_i, 0);441	*new_acl = acl;442	acl = NULL;443err:444	bch2_trans_iter_exit(trans, &iter);445	if (!IS_ERR_OR_NULL(acl))446		kfree(acl);447	return ret;448}449 450#endif /* CONFIG_BCACHEFS_POSIX_ACL */451