c8b77aa24a1dbaac372c5107b1540a2dcde70ccf
[cascardo/linux.git] / fs / cifs / xattr.c
1 /*
2  *   fs/cifs/xattr.c
3  *
4  *   Copyright (c) International Business Machines  Corp., 2003, 2007
5  *   Author(s): Steve French (sfrench@us.ibm.com)
6  *
7  *   This library is free software; you can redistribute it and/or modify
8  *   it under the terms of the GNU Lesser General Public License as published
9  *   by the Free Software Foundation; either version 2.1 of the License, or
10  *   (at your option) any later version.
11  *
12  *   This library is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
15  *   the GNU Lesser General Public License for more details.
16  *
17  *   You should have received a copy of the GNU Lesser General Public License
18  *   along with this library; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21
22 #include <linux/fs.h>
23 #include <linux/posix_acl_xattr.h>
24 #include <linux/slab.h>
25 #include <linux/xattr.h>
26 #include "cifsfs.h"
27 #include "cifspdu.h"
28 #include "cifsglob.h"
29 #include "cifsproto.h"
30 #include "cifs_debug.h"
31 #include "cifs_fs_sb.h"
32 #include "cifs_unicode.h"
33
34 #define MAX_EA_VALUE_SIZE 65535
35 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
36
37 /* BB need to add server (Samba e.g) support for security and trusted prefix */
38
39 enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
40
41 static int cifs_xattr_set(const struct xattr_handler *handler,
42                           struct dentry *dentry, const char *name,
43                           const void *value, size_t size, int flags)
44 {
45         int rc = -EOPNOTSUPP;
46         unsigned int xid;
47         struct super_block *sb = dentry->d_sb;
48         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
49         struct tcon_link *tlink;
50         struct cifs_tcon *pTcon;
51         char *full_path;
52
53         tlink = cifs_sb_tlink(cifs_sb);
54         if (IS_ERR(tlink))
55                 return PTR_ERR(tlink);
56         pTcon = tlink_tcon(tlink);
57
58         xid = get_xid();
59
60         full_path = build_path_from_dentry(dentry);
61         if (full_path == NULL) {
62                 rc = -ENOMEM;
63                 goto out;
64         }
65         /* return dos attributes as pseudo xattr */
66         /* return alt name if available as pseudo attr */
67
68         /* if proc/fs/cifs/streamstoxattr is set then
69                 search server for EAs or streams to
70                 returns as xattrs */
71         if (size > MAX_EA_VALUE_SIZE) {
72                 cifs_dbg(FYI, "size of EA value too large\n");
73                 rc = -EOPNOTSUPP;
74                 goto out;
75         }
76
77         switch (handler->flags) {
78         case XATTR_USER:
79                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
80                         goto out;
81
82                 if (pTcon->ses->server->ops->set_EA)
83                         rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
84                                 full_path, name, value, (__u16)size,
85                                 cifs_sb->local_nls, cifs_remap(cifs_sb));
86                 break;
87
88         case XATTR_CIFS_ACL: {
89 #ifdef CONFIG_CIFS_ACL
90                 struct cifs_ntsd *pacl;
91
92                 if (!value)
93                         goto out;
94                 pacl = kmalloc(size, GFP_KERNEL);
95                 if (!pacl) {
96                         rc = -ENOMEM;
97                 } else {
98                         memcpy(pacl, value, size);
99                         if (value &&
100                             pTcon->ses->server->ops->set_acl)
101                                 rc = pTcon->ses->server->ops->set_acl(pacl,
102                                                 size, d_inode(dentry),
103                                                 full_path, CIFS_ACL_DACL);
104                         else
105                                 rc = -EOPNOTSUPP;
106                         if (rc == 0) /* force revalidate of the inode */
107                                 CIFS_I(d_inode(dentry))->time = 0;
108                         kfree(pacl);
109                 }
110 #endif /* CONFIG_CIFS_ACL */
111                 break;
112         }
113
114         case XATTR_ACL_ACCESS:
115 #ifdef CONFIG_CIFS_POSIX
116                 if (!value)
117                         goto out;
118                 if (sb->s_flags & MS_POSIXACL)
119                         rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
120                                 value, (const int)size,
121                                 ACL_TYPE_ACCESS, cifs_sb->local_nls,
122                                 cifs_remap(cifs_sb));
123 #endif  /* CONFIG_CIFS_POSIX */
124                 break;
125
126         case XATTR_ACL_DEFAULT:
127 #ifdef CONFIG_CIFS_POSIX
128                 if (!value)
129                         goto out;
130                 if (sb->s_flags & MS_POSIXACL)
131                         rc = CIFSSMBSetPosixACL(xid, pTcon, full_path,
132                                 value, (const int)size,
133                                 ACL_TYPE_DEFAULT, cifs_sb->local_nls,
134                                 cifs_remap(cifs_sb));
135 #endif  /* CONFIG_CIFS_POSIX */
136                 break;
137         }
138
139 out:
140         kfree(full_path);
141         free_xid(xid);
142         cifs_put_tlink(tlink);
143         return rc;
144 }
145
146 static int cifs_xattr_get(const struct xattr_handler *handler,
147                           struct dentry *dentry, struct inode *inode,
148                           const char *name, void *value, size_t size)
149 {
150         ssize_t rc = -EOPNOTSUPP;
151         unsigned int xid;
152         struct super_block *sb = dentry->d_sb;
153         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
154         struct tcon_link *tlink;
155         struct cifs_tcon *pTcon;
156         char *full_path;
157
158         tlink = cifs_sb_tlink(cifs_sb);
159         if (IS_ERR(tlink))
160                 return PTR_ERR(tlink);
161         pTcon = tlink_tcon(tlink);
162
163         xid = get_xid();
164
165         full_path = build_path_from_dentry(dentry);
166         if (full_path == NULL) {
167                 rc = -ENOMEM;
168                 goto out;
169         }
170         /* return dos attributes as pseudo xattr */
171         /* return alt name if available as pseudo attr */
172         switch (handler->flags) {
173         case XATTR_USER:
174                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
175                         goto out;
176
177                 if (pTcon->ses->server->ops->query_all_EAs)
178                         rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
179                                 full_path, name, value, size,
180                                 cifs_sb->local_nls, cifs_remap(cifs_sb));
181                 break;
182
183         case XATTR_CIFS_ACL: {
184 #ifdef CONFIG_CIFS_ACL
185                 u32 acllen;
186                 struct cifs_ntsd *pacl;
187
188                 if (pTcon->ses->server->ops->get_acl == NULL)
189                         goto out; /* rc already EOPNOTSUPP */
190
191                 pacl = pTcon->ses->server->ops->get_acl(cifs_sb,
192                                 inode, full_path, &acllen);
193                 if (IS_ERR(pacl)) {
194                         rc = PTR_ERR(pacl);
195                         cifs_dbg(VFS, "%s: error %zd getting sec desc\n",
196                                  __func__, rc);
197                 } else {
198                         if (value) {
199                                 if (acllen > size)
200                                         acllen = -ERANGE;
201                                 else
202                                         memcpy(value, pacl, acllen);
203                         }
204                         rc = acllen;
205                         kfree(pacl);
206                 }
207 #endif  /* CONFIG_CIFS_ACL */
208                 break;
209         }
210
211         case XATTR_ACL_ACCESS:
212 #ifdef CONFIG_CIFS_POSIX
213                 if (sb->s_flags & MS_POSIXACL)
214                         rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
215                                 value, size, ACL_TYPE_ACCESS,
216                                 cifs_sb->local_nls,
217                                 cifs_remap(cifs_sb));
218 #endif  /* CONFIG_CIFS_POSIX */
219                 break;
220
221         case XATTR_ACL_DEFAULT:
222 #ifdef CONFIG_CIFS_POSIX
223                 if (sb->s_flags & MS_POSIXACL)
224                         rc = CIFSSMBGetPosixACL(xid, pTcon, full_path,
225                                 value, size, ACL_TYPE_DEFAULT,
226                                 cifs_sb->local_nls,
227                                 cifs_remap(cifs_sb));
228 #endif  /* CONFIG_CIFS_POSIX */
229                 break;
230         }
231
232         /* We could add an additional check for streams ie
233             if proc/fs/cifs/streamstoxattr is set then
234                 search server for EAs or streams to
235                 returns as xattrs */
236
237         if (rc == -EINVAL)
238                 rc = -EOPNOTSUPP;
239
240 out:
241         kfree(full_path);
242         free_xid(xid);
243         cifs_put_tlink(tlink);
244         return rc;
245 }
246
247 ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
248 {
249         ssize_t rc = -EOPNOTSUPP;
250         unsigned int xid;
251         struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
252         struct tcon_link *tlink;
253         struct cifs_tcon *pTcon;
254         char *full_path;
255
256         if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
257                 return -EOPNOTSUPP;
258
259         tlink = cifs_sb_tlink(cifs_sb);
260         if (IS_ERR(tlink))
261                 return PTR_ERR(tlink);
262         pTcon = tlink_tcon(tlink);
263
264         xid = get_xid();
265
266         full_path = build_path_from_dentry(direntry);
267         if (full_path == NULL) {
268                 rc = -ENOMEM;
269                 goto list_ea_exit;
270         }
271         /* return dos attributes as pseudo xattr */
272         /* return alt name if available as pseudo attr */
273
274         /* if proc/fs/cifs/streamstoxattr is set then
275                 search server for EAs or streams to
276                 returns as xattrs */
277
278         if (pTcon->ses->server->ops->query_all_EAs)
279                 rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
280                                 full_path, NULL, data, buf_size,
281                                 cifs_sb->local_nls, cifs_remap(cifs_sb));
282 list_ea_exit:
283         kfree(full_path);
284         free_xid(xid);
285         cifs_put_tlink(tlink);
286         return rc;
287 }
288
289 static const struct xattr_handler cifs_user_xattr_handler = {
290         .prefix = XATTR_USER_PREFIX,
291         .flags = XATTR_USER,
292         .get = cifs_xattr_get,
293         .set = cifs_xattr_set,
294 };
295
296 /* os2.* attributes are treated like user.* attributes */
297 static const struct xattr_handler cifs_os2_xattr_handler = {
298         .prefix = XATTR_OS2_PREFIX,
299         .flags = XATTR_USER,
300         .get = cifs_xattr_get,
301         .set = cifs_xattr_set,
302 };
303
304 static const struct xattr_handler cifs_cifs_acl_xattr_handler = {
305         .name = CIFS_XATTR_CIFS_ACL,
306         .flags = XATTR_CIFS_ACL,
307         .get = cifs_xattr_get,
308         .set = cifs_xattr_set,
309 };
310
311 static const struct xattr_handler cifs_posix_acl_access_xattr_handler = {
312         .name = XATTR_NAME_POSIX_ACL_ACCESS,
313         .flags = XATTR_ACL_ACCESS,
314         .get = cifs_xattr_get,
315         .set = cifs_xattr_set,
316 };
317
318 static const struct xattr_handler cifs_posix_acl_default_xattr_handler = {
319         .name = XATTR_NAME_POSIX_ACL_DEFAULT,
320         .flags = XATTR_ACL_DEFAULT,
321         .get = cifs_xattr_get,
322         .set = cifs_xattr_set,
323 };
324
325 const struct xattr_handler *cifs_xattr_handlers[] = {
326         &cifs_user_xattr_handler,
327         &cifs_os2_xattr_handler,
328         &cifs_cifs_acl_xattr_handler,
329         &cifs_posix_acl_access_xattr_handler,
330         &cifs_posix_acl_default_xattr_handler,
331         NULL
332 };