xref: /kernel/linux/linux-5.10/fs/btrfs/acl.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2007 Red Hat.  All rights reserved.
4 */
5
6#include <linux/fs.h>
7#include <linux/string.h>
8#include <linux/xattr.h>
9#include <linux/posix_acl_xattr.h>
10#include <linux/posix_acl.h>
11#include <linux/sched.h>
12#include <linux/sched/mm.h>
13#include <linux/slab.h>
14
15#include "ctree.h"
16#include "btrfs_inode.h"
17#include "xattr.h"
18
19struct posix_acl *btrfs_get_acl(struct inode *inode, int type)
20{
21	int size;
22	const char *name;
23	char *value = NULL;
24	struct posix_acl *acl;
25
26	switch (type) {
27	case ACL_TYPE_ACCESS:
28		name = XATTR_NAME_POSIX_ACL_ACCESS;
29		break;
30	case ACL_TYPE_DEFAULT:
31		name = XATTR_NAME_POSIX_ACL_DEFAULT;
32		break;
33	default:
34		return ERR_PTR(-EINVAL);
35	}
36
37	size = btrfs_getxattr(inode, name, NULL, 0);
38	if (size > 0) {
39		value = kzalloc(size, GFP_KERNEL);
40		if (!value)
41			return ERR_PTR(-ENOMEM);
42		size = btrfs_getxattr(inode, name, value, size);
43	}
44	if (size > 0)
45		acl = posix_acl_from_xattr(&init_user_ns, value, size);
46	else if (size == -ENODATA || size == 0)
47		acl = NULL;
48	else
49		acl = ERR_PTR(size);
50	kfree(value);
51
52	return acl;
53}
54
55static int __btrfs_set_acl(struct btrfs_trans_handle *trans,
56			 struct inode *inode, struct posix_acl *acl, int type)
57{
58	int ret, size = 0;
59	const char *name;
60	char *value = NULL;
61
62	switch (type) {
63	case ACL_TYPE_ACCESS:
64		name = XATTR_NAME_POSIX_ACL_ACCESS;
65		break;
66	case ACL_TYPE_DEFAULT:
67		if (!S_ISDIR(inode->i_mode))
68			return acl ? -EINVAL : 0;
69		name = XATTR_NAME_POSIX_ACL_DEFAULT;
70		break;
71	default:
72		return -EINVAL;
73	}
74
75	if (acl) {
76		unsigned int nofs_flag;
77
78		size = posix_acl_xattr_size(acl->a_count);
79		/*
80		 * We're holding a transaction handle, so use a NOFS memory
81		 * allocation context to avoid deadlock if reclaim happens.
82		 */
83		nofs_flag = memalloc_nofs_save();
84		value = kmalloc(size, GFP_KERNEL);
85		memalloc_nofs_restore(nofs_flag);
86		if (!value) {
87			ret = -ENOMEM;
88			goto out;
89		}
90
91		ret = posix_acl_to_xattr(&init_user_ns, acl, value, size);
92		if (ret < 0)
93			goto out;
94	}
95
96	if (trans)
97		ret = btrfs_setxattr(trans, inode, name, value, size, 0);
98	else
99		ret = btrfs_setxattr_trans(inode, name, value, size, 0);
100
101out:
102	kfree(value);
103
104	if (!ret)
105		set_cached_acl(inode, type, acl);
106
107	return ret;
108}
109
110int btrfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
111{
112	int ret;
113	umode_t old_mode = inode->i_mode;
114
115	if (type == ACL_TYPE_ACCESS && acl) {
116		ret = posix_acl_update_mode(inode, &inode->i_mode, &acl);
117		if (ret)
118			return ret;
119	}
120	ret = __btrfs_set_acl(NULL, inode, acl, type);
121	if (ret)
122		inode->i_mode = old_mode;
123	return ret;
124}
125
126int btrfs_init_acl(struct btrfs_trans_handle *trans,
127		   struct inode *inode, struct inode *dir)
128{
129	struct posix_acl *default_acl, *acl;
130	int ret = 0;
131
132	/* this happens with subvols */
133	if (!dir)
134		return 0;
135
136	ret = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
137	if (ret)
138		return ret;
139
140	if (default_acl) {
141		ret = __btrfs_set_acl(trans, inode, default_acl,
142				      ACL_TYPE_DEFAULT);
143		posix_acl_release(default_acl);
144	}
145
146	if (acl) {
147		if (!ret)
148			ret = __btrfs_set_acl(trans, inode, acl,
149					      ACL_TYPE_ACCESS);
150		posix_acl_release(acl);
151	}
152
153	if (!default_acl && !acl)
154		cache_no_acl(inode);
155	return ret;
156}
157