]> Pileus Git - ~andy/linux/blob - fs/hfsplus/posix_acl.c
fs: make posix_acl_chmod more useful
[~andy/linux] / fs / hfsplus / posix_acl.c
1 /*
2  * linux/fs/hfsplus/posix_acl.c
3  *
4  * Vyacheslav Dubeyko <slava@dubeyko.com>
5  *
6  * Handler for Posix Access Control Lists (ACLs) support.
7  */
8
9 #include "hfsplus_fs.h"
10 #include "xattr.h"
11 #include "acl.h"
12
13 struct posix_acl *hfsplus_get_posix_acl(struct inode *inode, int type)
14 {
15         struct posix_acl *acl;
16         char *xattr_name;
17         char *value = NULL;
18         ssize_t size;
19
20         acl = get_cached_acl(inode, type);
21         if (acl != ACL_NOT_CACHED)
22                 return acl;
23
24         switch (type) {
25         case ACL_TYPE_ACCESS:
26                 xattr_name = POSIX_ACL_XATTR_ACCESS;
27                 break;
28         case ACL_TYPE_DEFAULT:
29                 xattr_name = POSIX_ACL_XATTR_DEFAULT;
30                 break;
31         default:
32                 return ERR_PTR(-EINVAL);
33         }
34
35         size = __hfsplus_getxattr(inode, xattr_name, NULL, 0);
36
37         if (size > 0) {
38                 value = (char *)hfsplus_alloc_attr_entry();
39                 if (unlikely(!value))
40                         return ERR_PTR(-ENOMEM);
41                 size = __hfsplus_getxattr(inode, xattr_name, value, size);
42         }
43
44         if (size > 0)
45                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
46         else if (size == -ENODATA)
47                 acl = NULL;
48         else
49                 acl = ERR_PTR(size);
50
51         hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
52
53         if (!IS_ERR(acl))
54                 set_cached_acl(inode, type, acl);
55
56         return acl;
57 }
58
59 static int hfsplus_set_posix_acl(struct inode *inode,
60                                         int type,
61                                         struct posix_acl *acl)
62 {
63         int err;
64         char *xattr_name;
65         size_t size = 0;
66         char *value = NULL;
67
68         if (S_ISLNK(inode->i_mode))
69                 return -EOPNOTSUPP;
70
71         switch (type) {
72         case ACL_TYPE_ACCESS:
73                 xattr_name = POSIX_ACL_XATTR_ACCESS;
74                 if (acl) {
75                         err = posix_acl_equiv_mode(acl, &inode->i_mode);
76                         if (err < 0)
77                                 return err;
78                 }
79                 err = 0;
80                 break;
81
82         case ACL_TYPE_DEFAULT:
83                 xattr_name = POSIX_ACL_XATTR_DEFAULT;
84                 if (!S_ISDIR(inode->i_mode))
85                         return acl ? -EACCES : 0;
86                 break;
87
88         default:
89                 return -EINVAL;
90         }
91
92         if (acl) {
93                 size = posix_acl_xattr_size(acl->a_count);
94                 if (unlikely(size > HFSPLUS_MAX_INLINE_DATA_SIZE))
95                         return -ENOMEM;
96                 value = (char *)hfsplus_alloc_attr_entry();
97                 if (unlikely(!value))
98                         return -ENOMEM;
99                 err = posix_acl_to_xattr(&init_user_ns, acl, value, size);
100                 if (unlikely(err < 0))
101                         goto end_set_acl;
102         }
103
104         err = __hfsplus_setxattr(inode, xattr_name, value, size, 0);
105
106 end_set_acl:
107         hfsplus_destroy_attr_entry((hfsplus_attr_entry *)value);
108
109         if (!err)
110                 set_cached_acl(inode, type, acl);
111
112         return err;
113 }
114
115 int hfsplus_init_posix_acl(struct inode *inode, struct inode *dir)
116 {
117         int err = 0;
118         struct posix_acl *acl = NULL;
119
120         hfs_dbg(ACL_MOD,
121                 "[%s]: ino %lu, dir->ino %lu\n",
122                 __func__, inode->i_ino, dir->i_ino);
123
124         if (S_ISLNK(inode->i_mode))
125                 return 0;
126
127         acl = hfsplus_get_posix_acl(dir, ACL_TYPE_DEFAULT);
128         if (IS_ERR(acl))
129                 return PTR_ERR(acl);
130
131         if (acl) {
132                 if (S_ISDIR(inode->i_mode)) {
133                         err = hfsplus_set_posix_acl(inode,
134                                                         ACL_TYPE_DEFAULT,
135                                                         acl);
136                         if (unlikely(err))
137                                 goto init_acl_cleanup;
138                 }
139
140                 err = posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
141                 if (unlikely(err < 0))
142                         return err;
143
144                 if (err > 0)
145                         err = hfsplus_set_posix_acl(inode,
146                                                         ACL_TYPE_ACCESS,
147                                                         acl);
148         } else
149                 inode->i_mode &= ~current_umask();
150
151 init_acl_cleanup:
152         posix_acl_release(acl);
153         return err;
154 }
155
156 int hfsplus_posix_acl_chmod(struct inode *inode)
157 {
158         int err;
159         struct posix_acl *acl;
160
161         hfs_dbg(ACL_MOD, "[%s]: ino %lu\n", __func__, inode->i_ino);
162
163         if (S_ISLNK(inode->i_mode))
164                 return -EOPNOTSUPP;
165
166         acl = hfsplus_get_posix_acl(inode, ACL_TYPE_ACCESS);
167         if (IS_ERR(acl) || !acl)
168                 return PTR_ERR(acl);
169
170         err = __posix_acl_chmod(&acl, GFP_KERNEL, inode->i_mode);
171         if (unlikely(err))
172                 return err;
173
174         err = hfsplus_set_posix_acl(inode, ACL_TYPE_ACCESS, acl);
175         posix_acl_release(acl);
176         return err;
177 }
178
179 static int hfsplus_xattr_get_posix_acl(struct dentry *dentry,
180                                         const char *name,
181                                         void *buffer,
182                                         size_t size,
183                                         int type)
184 {
185         int err = 0;
186         struct posix_acl *acl;
187
188         hfs_dbg(ACL_MOD,
189                 "[%s]: ino %lu, buffer %p, size %zu, type %#x\n",
190                 __func__, dentry->d_inode->i_ino, buffer, size, type);
191
192         if (strcmp(name, "") != 0)
193                 return -EINVAL;
194
195         acl = hfsplus_get_posix_acl(dentry->d_inode, type);
196         if (IS_ERR(acl))
197                 return PTR_ERR(acl);
198         if (acl == NULL)
199                 return -ENODATA;
200
201         err = posix_acl_to_xattr(&init_user_ns, acl, buffer, size);
202         posix_acl_release(acl);
203
204         return err;
205 }
206
207 static int hfsplus_xattr_set_posix_acl(struct dentry *dentry,
208                                         const char *name,
209                                         const void *value,
210                                         size_t size,
211                                         int flags,
212                                         int type)
213 {
214         int err = 0;
215         struct inode *inode = dentry->d_inode;
216         struct posix_acl *acl = NULL;
217
218         hfs_dbg(ACL_MOD,
219                 "[%s]: ino %lu, value %p, size %zu, flags %#x, type %#x\n",
220                 __func__, inode->i_ino, value, size, flags, type);
221
222         if (strcmp(name, "") != 0)
223                 return -EINVAL;
224
225         if (!inode_owner_or_capable(inode))
226                 return -EPERM;
227
228         if (value) {
229                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
230                 if (IS_ERR(acl))
231                         return PTR_ERR(acl);
232                 else if (acl) {
233                         err = posix_acl_valid(acl);
234                         if (err)
235                                 goto end_xattr_set_acl;
236                 }
237         }
238
239         err = hfsplus_set_posix_acl(inode, type, acl);
240
241 end_xattr_set_acl:
242         posix_acl_release(acl);
243         return err;
244 }
245
246 static size_t hfsplus_xattr_list_posix_acl(struct dentry *dentry,
247                                                 char *list,
248                                                 size_t list_size,
249                                                 const char *name,
250                                                 size_t name_len,
251                                                 int type)
252 {
253         /*
254          * This method is not used.
255          * It is used hfsplus_listxattr() instead of generic_listxattr().
256          */
257         return -EOPNOTSUPP;
258 }
259
260 const struct xattr_handler hfsplus_xattr_acl_access_handler = {
261         .prefix = POSIX_ACL_XATTR_ACCESS,
262         .flags  = ACL_TYPE_ACCESS,
263         .list   = hfsplus_xattr_list_posix_acl,
264         .get    = hfsplus_xattr_get_posix_acl,
265         .set    = hfsplus_xattr_set_posix_acl,
266 };
267
268 const struct xattr_handler hfsplus_xattr_acl_default_handler = {
269         .prefix = POSIX_ACL_XATTR_DEFAULT,
270         .flags  = ACL_TYPE_DEFAULT,
271         .list   = hfsplus_xattr_list_posix_acl,
272         .get    = hfsplus_xattr_get_posix_acl,
273         .set    = hfsplus_xattr_set_posix_acl,
274 };