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