Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / fs / ext3 / acl.c
1 /*
2  * linux/fs/ext3/acl.c
3  *
4  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
5  */
6
7 #include "ext3.h"
8 #include "xattr.h"
9 #include "acl.h"
10
11 /*
12  * Convert from filesystem to in-memory representation.
13  */
14 static struct posix_acl *
15 ext3_acl_from_disk(const void *value, size_t size)
16 {
17         const char *end = (char *)value + size;
18         int n, count;
19         struct posix_acl *acl;
20
21         if (!value)
22                 return NULL;
23         if (size < sizeof(ext3_acl_header))
24                  return ERR_PTR(-EINVAL);
25         if (((ext3_acl_header *)value)->a_version !=
26             cpu_to_le32(EXT3_ACL_VERSION))
27                 return ERR_PTR(-EINVAL);
28         value = (char *)value + sizeof(ext3_acl_header);
29         count = ext3_acl_count(size);
30         if (count < 0)
31                 return ERR_PTR(-EINVAL);
32         if (count == 0)
33                 return NULL;
34         acl = posix_acl_alloc(count, GFP_NOFS);
35         if (!acl)
36                 return ERR_PTR(-ENOMEM);
37         for (n=0; n < count; n++) {
38                 ext3_acl_entry *entry =
39                         (ext3_acl_entry *)value;
40                 if ((char *)value + sizeof(ext3_acl_entry_short) > end)
41                         goto fail;
42                 acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
43                 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
44                 switch(acl->a_entries[n].e_tag) {
45                         case ACL_USER_OBJ:
46                         case ACL_GROUP_OBJ:
47                         case ACL_MASK:
48                         case ACL_OTHER:
49                                 value = (char *)value +
50                                         sizeof(ext3_acl_entry_short);
51                                 break;
52
53                         case ACL_USER:
54                                 value = (char *)value + sizeof(ext3_acl_entry);
55                                 if ((char *)value > end)
56                                         goto fail;
57                                 acl->a_entries[n].e_uid =
58                                         make_kuid(&init_user_ns,
59                                                   le32_to_cpu(entry->e_id));
60                                 break;
61                         case ACL_GROUP:
62                                 value = (char *)value + sizeof(ext3_acl_entry);
63                                 if ((char *)value > end)
64                                         goto fail;
65                                 acl->a_entries[n].e_gid =
66                                         make_kgid(&init_user_ns,
67                                                   le32_to_cpu(entry->e_id));
68                                 break;
69
70                         default:
71                                 goto fail;
72                 }
73         }
74         if (value != end)
75                 goto fail;
76         return acl;
77
78 fail:
79         posix_acl_release(acl);
80         return ERR_PTR(-EINVAL);
81 }
82
83 /*
84  * Convert from in-memory to filesystem representation.
85  */
86 static void *
87 ext3_acl_to_disk(const struct posix_acl *acl, size_t *size)
88 {
89         ext3_acl_header *ext_acl;
90         char *e;
91         size_t n;
92
93         *size = ext3_acl_size(acl->a_count);
94         ext_acl = kmalloc(sizeof(ext3_acl_header) + acl->a_count *
95                         sizeof(ext3_acl_entry), GFP_NOFS);
96         if (!ext_acl)
97                 return ERR_PTR(-ENOMEM);
98         ext_acl->a_version = cpu_to_le32(EXT3_ACL_VERSION);
99         e = (char *)ext_acl + sizeof(ext3_acl_header);
100         for (n=0; n < acl->a_count; n++) {
101                 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
102                 ext3_acl_entry *entry = (ext3_acl_entry *)e;
103                 entry->e_tag  = cpu_to_le16(acl_e->e_tag);
104                 entry->e_perm = cpu_to_le16(acl_e->e_perm);
105                 switch(acl_e->e_tag) {
106                         case ACL_USER:
107                                 entry->e_id = cpu_to_le32(
108                                         from_kuid(&init_user_ns, acl_e->e_uid));
109                                 e += sizeof(ext3_acl_entry);
110                                 break;
111                         case ACL_GROUP:
112                                 entry->e_id = cpu_to_le32(
113                                         from_kgid(&init_user_ns, acl_e->e_gid));
114                                 e += sizeof(ext3_acl_entry);
115                                 break;
116
117                         case ACL_USER_OBJ:
118                         case ACL_GROUP_OBJ:
119                         case ACL_MASK:
120                         case ACL_OTHER:
121                                 e += sizeof(ext3_acl_entry_short);
122                                 break;
123
124                         default:
125                                 goto fail;
126                 }
127         }
128         return (char *)ext_acl;
129
130 fail:
131         kfree(ext_acl);
132         return ERR_PTR(-EINVAL);
133 }
134
135 /*
136  * Inode operation get_posix_acl().
137  *
138  * inode->i_mutex: don't care
139  */
140 struct posix_acl *
141 ext3_get_acl(struct inode *inode, int type)
142 {
143         int name_index;
144         char *value = NULL;
145         struct posix_acl *acl;
146         int retval;
147
148         switch (type) {
149         case ACL_TYPE_ACCESS:
150                 name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
151                 break;
152         case ACL_TYPE_DEFAULT:
153                 name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
154                 break;
155         default:
156                 BUG();
157         }
158
159         retval = ext3_xattr_get(inode, name_index, "", NULL, 0);
160         if (retval > 0) {
161                 value = kmalloc(retval, GFP_NOFS);
162                 if (!value)
163                         return ERR_PTR(-ENOMEM);
164                 retval = ext3_xattr_get(inode, name_index, "", value, retval);
165         }
166         if (retval > 0)
167                 acl = ext3_acl_from_disk(value, retval);
168         else if (retval == -ENODATA || retval == -ENOSYS)
169                 acl = NULL;
170         else
171                 acl = ERR_PTR(retval);
172         kfree(value);
173
174         if (!IS_ERR(acl))
175                 set_cached_acl(inode, type, acl);
176
177         return acl;
178 }
179
180 /*
181  * Set the access or default ACL of an inode.
182  *
183  * inode->i_mutex: down unless called from ext3_new_inode
184  */
185 static int
186 __ext3_set_acl(handle_t *handle, struct inode *inode, int type,
187              struct posix_acl *acl)
188 {
189         int name_index;
190         void *value = NULL;
191         size_t size = 0;
192         int error;
193
194         switch(type) {
195                 case ACL_TYPE_ACCESS:
196                         name_index = EXT3_XATTR_INDEX_POSIX_ACL_ACCESS;
197                         if (acl) {
198                                 error = posix_acl_equiv_mode(acl, &inode->i_mode);
199                                 if (error < 0)
200                                         return error;
201                                 else {
202                                         inode->i_ctime = CURRENT_TIME_SEC;
203                                         ext3_mark_inode_dirty(handle, inode);
204                                         if (error == 0)
205                                                 acl = NULL;
206                                 }
207                         }
208                         break;
209
210                 case ACL_TYPE_DEFAULT:
211                         name_index = EXT3_XATTR_INDEX_POSIX_ACL_DEFAULT;
212                         if (!S_ISDIR(inode->i_mode))
213                                 return acl ? -EACCES : 0;
214                         break;
215
216                 default:
217                         return -EINVAL;
218         }
219         if (acl) {
220                 value = ext3_acl_to_disk(acl, &size);
221                 if (IS_ERR(value))
222                         return (int)PTR_ERR(value);
223         }
224
225         error = ext3_xattr_set_handle(handle, inode, name_index, "",
226                                       value, size, 0);
227
228         kfree(value);
229
230         if (!error)
231                 set_cached_acl(inode, type, acl);
232
233         return error;
234 }
235
236 int
237 ext3_set_acl(struct inode *inode, struct posix_acl *acl, int type)
238 {
239         handle_t *handle;
240         int error, retries = 0;
241
242 retry:
243         handle = ext3_journal_start(inode, EXT3_DATA_TRANS_BLOCKS(inode->i_sb));
244         if (IS_ERR(handle))
245                 return PTR_ERR(handle);
246         error = __ext3_set_acl(handle, inode, type, acl);
247         ext3_journal_stop(handle);
248         if (error == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
249                 goto retry;
250         return error;
251 }
252
253 /*
254  * Initialize the ACLs of a new inode. Called from ext3_new_inode.
255  *
256  * dir->i_mutex: down
257  * inode->i_mutex: up (access to inode is still exclusive)
258  */
259 int
260 ext3_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
261 {
262         struct posix_acl *default_acl, *acl;
263         int error;
264
265         error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
266         if (error)
267                 return error;
268
269         if (default_acl) {
270                 error = __ext3_set_acl(handle, inode, ACL_TYPE_DEFAULT,
271                                        default_acl);
272                 posix_acl_release(default_acl);
273         }
274         if (acl) {
275                 if (!error)
276                         error = __ext3_set_acl(handle, inode, ACL_TYPE_ACCESS,
277                                                acl);
278                 posix_acl_release(acl);
279         }
280         return error;
281 }