drm/radeon: add new RS880 pci id
[linux-2.6.git] / fs / ocfs2 / acl.c
1 /* -*- mode: c; c-basic-offset: 8; -*-
2  * vim: noexpandtab sw=8 ts=8 sts=0:
3  *
4  * acl.c
5  *
6  * Copyright (C) 2004, 2008 Oracle.  All rights reserved.
7  *
8  * CREDITS:
9  * Lots of code in this file is copy from linux/fs/ext3/acl.c.
10  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public
14  * License version 2 as published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  */
21
22 #include <linux/init.h>
23 #include <linux/module.h>
24 #include <linux/string.h>
25
26 #define MLOG_MASK_PREFIX ML_INODE
27 #include <cluster/masklog.h>
28
29 #include "ocfs2.h"
30 #include "alloc.h"
31 #include "dlmglue.h"
32 #include "file.h"
33 #include "ocfs2_fs.h"
34
35 #include "xattr.h"
36 #include "acl.h"
37
38 /*
39  * Convert from xattr value to acl struct.
40  */
41 static struct posix_acl *ocfs2_acl_from_xattr(const void *value, size_t size)
42 {
43         int n, count;
44         struct posix_acl *acl;
45
46         if (!value)
47                 return NULL;
48         if (size < sizeof(struct posix_acl_entry))
49                 return ERR_PTR(-EINVAL);
50
51         count = size / sizeof(struct posix_acl_entry);
52         if (count < 0)
53                 return ERR_PTR(-EINVAL);
54         if (count == 0)
55                 return NULL;
56
57         acl = posix_acl_alloc(count, GFP_NOFS);
58         if (!acl)
59                 return ERR_PTR(-ENOMEM);
60         for (n = 0; n < count; n++) {
61                 struct ocfs2_acl_entry *entry =
62                         (struct ocfs2_acl_entry *)value;
63
64                 acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
65                 acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
66                 acl->a_entries[n].e_id   = le32_to_cpu(entry->e_id);
67                 value += sizeof(struct posix_acl_entry);
68
69         }
70         return acl;
71 }
72
73 /*
74  * Convert acl struct to xattr value.
75  */
76 static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)
77 {
78         struct ocfs2_acl_entry *entry = NULL;
79         char *ocfs2_acl;
80         size_t n;
81
82         *size = acl->a_count * sizeof(struct posix_acl_entry);
83
84         ocfs2_acl = kmalloc(*size, GFP_NOFS);
85         if (!ocfs2_acl)
86                 return ERR_PTR(-ENOMEM);
87
88         entry = (struct ocfs2_acl_entry *)ocfs2_acl;
89         for (n = 0; n < acl->a_count; n++, entry++) {
90                 entry->e_tag  = cpu_to_le16(acl->a_entries[n].e_tag);
91                 entry->e_perm = cpu_to_le16(acl->a_entries[n].e_perm);
92                 entry->e_id   = cpu_to_le32(acl->a_entries[n].e_id);
93         }
94         return ocfs2_acl;
95 }
96
97 static struct posix_acl *ocfs2_get_acl_nolock(struct inode *inode,
98                                               int type,
99                                               struct buffer_head *di_bh)
100 {
101         int name_index;
102         char *value = NULL;
103         struct posix_acl *acl;
104         int retval;
105
106         switch (type) {
107         case ACL_TYPE_ACCESS:
108                 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
109                 break;
110         case ACL_TYPE_DEFAULT:
111                 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
112                 break;
113         default:
114                 return ERR_PTR(-EINVAL);
115         }
116
117         retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index, "", NULL, 0);
118         if (retval > 0) {
119                 value = kmalloc(retval, GFP_NOFS);
120                 if (!value)
121                         return ERR_PTR(-ENOMEM);
122                 retval = ocfs2_xattr_get_nolock(inode, di_bh, name_index,
123                                                 "", value, retval);
124         }
125
126         if (retval > 0)
127                 acl = ocfs2_acl_from_xattr(value, retval);
128         else if (retval == -ENODATA || retval == 0)
129                 acl = NULL;
130         else
131                 acl = ERR_PTR(retval);
132
133         kfree(value);
134
135         return acl;
136 }
137
138
139 /*
140  * Get posix acl.
141  */
142 static struct posix_acl *ocfs2_get_acl(struct inode *inode, int type)
143 {
144         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
145         struct buffer_head *di_bh = NULL;
146         struct posix_acl *acl;
147         int ret;
148
149         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
150                 return NULL;
151
152         ret = ocfs2_inode_lock(inode, &di_bh, 0);
153         if (ret < 0) {
154                 mlog_errno(ret);
155                 acl = ERR_PTR(ret);
156                 return acl;
157         }
158
159         acl = ocfs2_get_acl_nolock(inode, type, di_bh);
160
161         ocfs2_inode_unlock(inode, 0);
162
163         brelse(di_bh);
164
165         return acl;
166 }
167
168 /*
169  * Set the access or default ACL of an inode.
170  */
171 static int ocfs2_set_acl(handle_t *handle,
172                          struct inode *inode,
173                          struct buffer_head *di_bh,
174                          int type,
175                          struct posix_acl *acl,
176                          struct ocfs2_alloc_context *meta_ac,
177                          struct ocfs2_alloc_context *data_ac)
178 {
179         int name_index;
180         void *value = NULL;
181         size_t size = 0;
182         int ret;
183
184         if (S_ISLNK(inode->i_mode))
185                 return -EOPNOTSUPP;
186
187         switch (type) {
188         case ACL_TYPE_ACCESS:
189                 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_ACCESS;
190                 if (acl) {
191                         mode_t mode = inode->i_mode;
192                         ret = posix_acl_equiv_mode(acl, &mode);
193                         if (ret < 0)
194                                 return ret;
195                         else {
196                                 inode->i_mode = mode;
197                                 if (ret == 0)
198                                         acl = NULL;
199                         }
200                 }
201                 break;
202         case ACL_TYPE_DEFAULT:
203                 name_index = OCFS2_XATTR_INDEX_POSIX_ACL_DEFAULT;
204                 if (!S_ISDIR(inode->i_mode))
205                         return acl ? -EACCES : 0;
206                 break;
207         default:
208                 return -EINVAL;
209         }
210
211         if (acl) {
212                 value = ocfs2_acl_to_xattr(acl, &size);
213                 if (IS_ERR(value))
214                         return (int)PTR_ERR(value);
215         }
216
217         if (handle)
218                 ret = ocfs2_xattr_set_handle(handle, inode, di_bh, name_index,
219                                              "", value, size, 0,
220                                              meta_ac, data_ac);
221         else
222                 ret = ocfs2_xattr_set(inode, name_index, "", value, size, 0);
223
224         kfree(value);
225
226         return ret;
227 }
228
229 int ocfs2_check_acl(struct inode *inode, int mask)
230 {
231         struct posix_acl *acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS);
232
233         if (IS_ERR(acl))
234                 return PTR_ERR(acl);
235         if (acl) {
236                 int ret = posix_acl_permission(inode, acl, mask);
237                 posix_acl_release(acl);
238                 return ret;
239         }
240
241         return -EAGAIN;
242 }
243
244 int ocfs2_acl_chmod(struct inode *inode)
245 {
246         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
247         struct posix_acl *acl, *clone;
248         int ret;
249
250         if (S_ISLNK(inode->i_mode))
251                 return -EOPNOTSUPP;
252
253         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
254                 return 0;
255
256         acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS);
257         if (IS_ERR(acl) || !acl)
258                 return PTR_ERR(acl);
259         clone = posix_acl_clone(acl, GFP_KERNEL);
260         posix_acl_release(acl);
261         if (!clone)
262                 return -ENOMEM;
263         ret = posix_acl_chmod_masq(clone, inode->i_mode);
264         if (!ret)
265                 ret = ocfs2_set_acl(NULL, inode, NULL, ACL_TYPE_ACCESS,
266                                     clone, NULL, NULL);
267         posix_acl_release(clone);
268         return ret;
269 }
270
271 /*
272  * Initialize the ACLs of a new inode. If parent directory has default ACL,
273  * then clone to new inode. Called from ocfs2_mknod.
274  */
275 int ocfs2_init_acl(handle_t *handle,
276                    struct inode *inode,
277                    struct inode *dir,
278                    struct buffer_head *di_bh,
279                    struct buffer_head *dir_bh,
280                    struct ocfs2_alloc_context *meta_ac,
281                    struct ocfs2_alloc_context *data_ac)
282 {
283         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
284         struct posix_acl *acl = NULL;
285         int ret = 0;
286
287         if (!S_ISLNK(inode->i_mode)) {
288                 if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
289                         acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
290                                                    dir_bh);
291                         if (IS_ERR(acl))
292                                 return PTR_ERR(acl);
293                 }
294                 if (!acl)
295                         inode->i_mode &= ~current_umask();
296         }
297         if ((osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) && acl) {
298                 struct posix_acl *clone;
299                 mode_t mode;
300
301                 if (S_ISDIR(inode->i_mode)) {
302                         ret = ocfs2_set_acl(handle, inode, di_bh,
303                                             ACL_TYPE_DEFAULT, acl,
304                                             meta_ac, data_ac);
305                         if (ret)
306                                 goto cleanup;
307                 }
308                 clone = posix_acl_clone(acl, GFP_NOFS);
309                 ret = -ENOMEM;
310                 if (!clone)
311                         goto cleanup;
312
313                 mode = inode->i_mode;
314                 ret = posix_acl_create_masq(clone, &mode);
315                 if (ret >= 0) {
316                         inode->i_mode = mode;
317                         if (ret > 0) {
318                                 ret = ocfs2_set_acl(handle, inode,
319                                                     di_bh, ACL_TYPE_ACCESS,
320                                                     clone, meta_ac, data_ac);
321                         }
322                 }
323                 posix_acl_release(clone);
324         }
325 cleanup:
326         posix_acl_release(acl);
327         return ret;
328 }
329
330 static size_t ocfs2_xattr_list_acl_access(struct dentry *dentry,
331                                           char *list,
332                                           size_t list_len,
333                                           const char *name,
334                                           size_t name_len,
335                                           int type)
336 {
337         struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
338         const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
339
340         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
341                 return 0;
342
343         if (list && size <= list_len)
344                 memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
345         return size;
346 }
347
348 static size_t ocfs2_xattr_list_acl_default(struct dentry *dentry,
349                                            char *list,
350                                            size_t list_len,
351                                            const char *name,
352                                            size_t name_len,
353                                            int type)
354 {
355         struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
356         const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
357
358         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
359                 return 0;
360
361         if (list && size <= list_len)
362                 memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
363         return size;
364 }
365
366 static int ocfs2_xattr_get_acl(struct dentry *dentry, const char *name,
367                 void *buffer, size_t size, int type)
368 {
369         struct ocfs2_super *osb = OCFS2_SB(dentry->d_sb);
370         struct posix_acl *acl;
371         int ret;
372
373         if (strcmp(name, "") != 0)
374                 return -EINVAL;
375         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
376                 return -EOPNOTSUPP;
377
378         acl = ocfs2_get_acl(dentry->d_inode, type);
379         if (IS_ERR(acl))
380                 return PTR_ERR(acl);
381         if (acl == NULL)
382                 return -ENODATA;
383         ret = posix_acl_to_xattr(acl, buffer, size);
384         posix_acl_release(acl);
385
386         return ret;
387 }
388
389 static int ocfs2_xattr_set_acl(struct dentry *dentry, const char *name,
390                 const void *value, size_t size, int flags, int type)
391 {
392         struct inode *inode = dentry->d_inode;
393         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
394         struct posix_acl *acl;
395         int ret = 0;
396
397         if (strcmp(name, "") != 0)
398                 return -EINVAL;
399         if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL))
400                 return -EOPNOTSUPP;
401
402         if (!is_owner_or_cap(inode))
403                 return -EPERM;
404
405         if (value) {
406                 acl = posix_acl_from_xattr(value, size);
407                 if (IS_ERR(acl))
408                         return PTR_ERR(acl);
409                 else if (acl) {
410                         ret = posix_acl_valid(acl);
411                         if (ret)
412                                 goto cleanup;
413                 }
414         } else
415                 acl = NULL;
416
417         ret = ocfs2_set_acl(NULL, inode, NULL, type, acl, NULL, NULL);
418
419 cleanup:
420         posix_acl_release(acl);
421         return ret;
422 }
423
424 struct xattr_handler ocfs2_xattr_acl_access_handler = {
425         .prefix = POSIX_ACL_XATTR_ACCESS,
426         .flags  = ACL_TYPE_ACCESS,
427         .list   = ocfs2_xattr_list_acl_access,
428         .get    = ocfs2_xattr_get_acl,
429         .set    = ocfs2_xattr_set_acl,
430 };
431
432 struct xattr_handler ocfs2_xattr_acl_default_handler = {
433         .prefix = POSIX_ACL_XATTR_DEFAULT,
434         .flags  = ACL_TYPE_DEFAULT,
435         .list   = ocfs2_xattr_list_acl_default,
436         .get    = ocfs2_xattr_get_acl,
437         .set    = ocfs2_xattr_set_acl,
438 };