]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/acl.c
Update bcachefs sources to 9abf628c70 bcachefs: Fix a spurious error in fsck
[bcachefs-tools-debian] / libbcachefs / acl.c
1 #ifdef CONFIG_BCACHEFS_POSIX_ACL
2
3 #include "bcachefs.h"
4
5 #include <linux/fs.h>
6 #include <linux/posix_acl.h>
7 #include <linux/posix_acl_xattr.h>
8 #include <linux/sched.h>
9 #include <linux/slab.h>
10
11 #include "acl.h"
12 #include "fs.h"
13 #include "xattr.h"
14
15 /*
16  * Convert from filesystem to in-memory representation.
17  */
18 static struct posix_acl *bch2_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(bch_acl_header))
27                 return ERR_PTR(-EINVAL);
28         if (((bch_acl_header *)value)->a_version !=
29             cpu_to_le32(BCH_ACL_VERSION))
30                 return ERR_PTR(-EINVAL);
31         value = (char *)value + sizeof(bch_acl_header);
32         count = bch2_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_KERNEL);
38         if (!acl)
39                 return ERR_PTR(-ENOMEM);
40         for (n = 0; n < count; n++) {
41                 bch_acl_entry *entry =
42                         (bch_acl_entry *)value;
43                 if ((char *)value + sizeof(bch_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                 switch (acl->a_entries[n].e_tag) {
48                 case ACL_USER_OBJ:
49                 case ACL_GROUP_OBJ:
50                 case ACL_MASK:
51                 case ACL_OTHER:
52                         value = (char *)value +
53                                 sizeof(bch_acl_entry_short);
54                         break;
55
56                 case ACL_USER:
57                         value = (char *)value + sizeof(bch_acl_entry);
58                         if ((char *)value > end)
59                                 goto fail;
60                         acl->a_entries[n].e_uid =
61                                 make_kuid(&init_user_ns,
62                                           le32_to_cpu(entry->e_id));
63                         break;
64                 case ACL_GROUP:
65                         value = (char *)value + sizeof(bch_acl_entry);
66                         if ((char *)value > end)
67                                 goto fail;
68                         acl->a_entries[n].e_gid =
69                                 make_kgid(&init_user_ns,
70                                           le32_to_cpu(entry->e_id));
71                         break;
72
73                 default:
74                         goto fail;
75                 }
76         }
77         if (value != end)
78                 goto fail;
79         return acl;
80
81 fail:
82         posix_acl_release(acl);
83         return ERR_PTR(-EINVAL);
84 }
85
86 /*
87  * Convert from in-memory to filesystem representation.
88  */
89 static void *bch2_acl_to_disk(const struct posix_acl *acl, size_t *size)
90 {
91         bch_acl_header *ext_acl;
92         char *e;
93         size_t n;
94
95         *size = bch2_acl_size(acl->a_count);
96         ext_acl = kmalloc(sizeof(bch_acl_header) + acl->a_count *
97                         sizeof(bch_acl_entry), GFP_KERNEL);
98         if (!ext_acl)
99                 return ERR_PTR(-ENOMEM);
100         ext_acl->a_version = cpu_to_le32(BCH_ACL_VERSION);
101         e = (char *)ext_acl + sizeof(bch_acl_header);
102         for (n = 0; n < acl->a_count; n++) {
103                 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
104                 bch_acl_entry *entry = (bch_acl_entry *)e;
105
106                 entry->e_tag = cpu_to_le16(acl_e->e_tag);
107                 entry->e_perm = cpu_to_le16(acl_e->e_perm);
108                 switch (acl_e->e_tag) {
109                 case ACL_USER:
110                         entry->e_id = cpu_to_le32(
111                                 from_kuid(&init_user_ns, acl_e->e_uid));
112                         e += sizeof(bch_acl_entry);
113                         break;
114                 case ACL_GROUP:
115                         entry->e_id = cpu_to_le32(
116                                 from_kgid(&init_user_ns, acl_e->e_gid));
117                         e += sizeof(bch_acl_entry);
118                         break;
119
120                 case ACL_USER_OBJ:
121                 case ACL_GROUP_OBJ:
122                 case ACL_MASK:
123                 case ACL_OTHER:
124                         e += sizeof(bch_acl_entry_short);
125                         break;
126
127                 default:
128                         goto fail;
129                 }
130         }
131         return (char *)ext_acl;
132
133 fail:
134         kfree(ext_acl);
135         return ERR_PTR(-EINVAL);
136 }
137
138 struct posix_acl *bch2_get_acl(struct inode *vinode, int type)
139 {
140         struct bch_inode_info *inode = to_bch_ei(vinode);
141         struct bch_fs *c = inode->v.i_sb->s_fs_info;
142         int name_index;
143         char *value = NULL;
144         struct posix_acl *acl;
145         int ret;
146
147         switch (type) {
148         case ACL_TYPE_ACCESS:
149                 name_index = BCH_XATTR_INDEX_POSIX_ACL_ACCESS;
150                 break;
151         case ACL_TYPE_DEFAULT:
152                 name_index = BCH_XATTR_INDEX_POSIX_ACL_DEFAULT;
153                 break;
154         default:
155                 BUG();
156         }
157         ret = bch2_xattr_get(c, inode, "", NULL, 0, name_index);
158         if (ret > 0) {
159                 value = kmalloc(ret, GFP_KERNEL);
160                 if (!value)
161                         return ERR_PTR(-ENOMEM);
162                 ret = bch2_xattr_get(c, inode, "", value,
163                                     ret, name_index);
164         }
165         if (ret > 0)
166                 acl = bch2_acl_from_disk(value, ret);
167         else if (ret == -ENODATA || ret == -ENOSYS)
168                 acl = NULL;
169         else
170                 acl = ERR_PTR(ret);
171         kfree(value);
172
173         if (!IS_ERR(acl))
174                 set_cached_acl(&inode->v, type, acl);
175
176         return acl;
177 }
178
179 int __bch2_set_acl(struct inode *vinode, struct posix_acl *acl, int type)
180 {
181         struct bch_inode_info *inode = to_bch_ei(vinode);
182         struct bch_fs *c = inode->v.i_sb->s_fs_info;
183         int name_index;
184         void *value = NULL;
185         size_t size = 0;
186         int ret;
187
188         switch (type) {
189         case ACL_TYPE_ACCESS:
190                 name_index = BCH_XATTR_INDEX_POSIX_ACL_ACCESS;
191                 break;
192         case ACL_TYPE_DEFAULT:
193                 name_index = BCH_XATTR_INDEX_POSIX_ACL_DEFAULT;
194                 if (!S_ISDIR(inode->v.i_mode))
195                         return acl ? -EACCES : 0;
196                 break;
197
198         default:
199                 return -EINVAL;
200         }
201
202         if (acl) {
203                 value = bch2_acl_to_disk(acl, &size);
204                 if (IS_ERR(value))
205                         return (int)PTR_ERR(value);
206         }
207
208         ret = bch2_xattr_set(c, inode, "", value, size, 0, name_index);
209         kfree(value);
210
211         if (ret == -ERANGE)
212                 ret = -E2BIG;
213
214         if (!ret)
215                 set_cached_acl(&inode->v, type, acl);
216
217         return ret;
218 }
219
220 int bch2_set_acl(struct inode *vinode, struct posix_acl *acl, int type)
221 {
222         struct bch_inode_info *inode = to_bch_ei(vinode);
223         struct bch_fs *c = inode->v.i_sb->s_fs_info;
224         umode_t mode = inode->v.i_mode;
225         int ret;
226
227         if (type == ACL_TYPE_ACCESS && acl) {
228                 ret = posix_acl_update_mode(&inode->v, &mode, &acl);
229                 if (ret)
230                         return ret;
231         }
232
233         ret = __bch2_set_acl(vinode, acl, type);
234         if (ret)
235                 return ret;
236
237         if (mode != inode->v.i_mode) {
238                 mutex_lock(&inode->ei_update_lock);
239                 inode->v.i_mode = mode;
240                 inode->v.i_ctime = current_time(&inode->v);
241
242                 ret = bch2_write_inode(c, inode);
243                 mutex_unlock(&inode->ei_update_lock);
244         }
245
246         return ret;
247 }
248
249 #endif /* CONFIG_BCACHEFS_POSIX_ACL */