]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | File: fs/xattr.c | |
3 | ||
4 | Extended attribute handling. | |
5 | ||
6 | Copyright (C) 2001 by Andreas Gruenbacher <[email protected]> | |
7 | Copyright (C) 2001 SGI - Silicon Graphics, Inc <[email protected]> | |
8 | Copyright (c) 2004 Red Hat, Inc., James Morris <[email protected]> | |
9 | */ | |
10 | #include <linux/fs.h> | |
11 | #include <linux/slab.h> | |
1da177e4 LT |
12 | #include <linux/file.h> |
13 | #include <linux/xattr.h> | |
14 | #include <linux/namei.h> | |
15 | #include <linux/security.h> | |
16 | #include <linux/syscalls.h> | |
17 | #include <linux/module.h> | |
0eeca283 | 18 | #include <linux/fsnotify.h> |
73241ccc | 19 | #include <linux/audit.h> |
1da177e4 LT |
20 | #include <asm/uaccess.h> |
21 | ||
5be196e5 | 22 | |
e0ad7b07 | 23 | /* |
24 | * Check permissions for extended attribute access. This is a bit complicated | |
25 | * because different namespaces have very different rules. | |
26 | */ | |
27 | static int | |
28 | xattr_permission(struct inode *inode, const char *name, int mask) | |
29 | { | |
30 | /* | |
31 | * We can never set or remove an extended attribute on a read-only | |
32 | * filesystem or on an immutable / append-only inode. | |
33 | */ | |
34 | if (mask & MAY_WRITE) { | |
35 | if (IS_RDONLY(inode)) | |
36 | return -EROFS; | |
37 | if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) | |
38 | return -EPERM; | |
39 | } | |
40 | ||
41 | /* | |
42 | * No restriction for security.* and system.* from the VFS. Decision | |
43 | * on these is left to the underlying filesystem / security module. | |
44 | */ | |
45 | if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) || | |
46 | !strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) | |
47 | return 0; | |
48 | ||
49 | /* | |
f1f2d871 | 50 | * The trusted.* namespace can only be accessed by a privileged user. |
e0ad7b07 | 51 | */ |
52 | if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) | |
53 | return (capable(CAP_SYS_ADMIN) ? 0 : -EPERM); | |
54 | ||
f1f2d871 AG |
55 | /* In user.* namespace, only regular files and directories can have |
56 | * extended attributes. For sticky directories, only the owner and | |
57 | * privileged user can write attributes. | |
58 | */ | |
e0ad7b07 | 59 | if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) { |
f1f2d871 AG |
60 | if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) |
61 | return -EPERM; | |
62 | if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) && | |
3bd858ab | 63 | (mask & MAY_WRITE) && !is_owner_or_cap(inode)) |
e0ad7b07 | 64 | return -EPERM; |
65 | } | |
66 | ||
67 | return permission(inode, mask, NULL); | |
68 | } | |
69 | ||
5be196e5 CH |
70 | int |
71 | vfs_setxattr(struct dentry *dentry, char *name, void *value, | |
72 | size_t size, int flags) | |
73 | { | |
74 | struct inode *inode = dentry->d_inode; | |
75 | int error; | |
76 | ||
e0ad7b07 | 77 | error = xattr_permission(inode, name, MAY_WRITE); |
78 | if (error) | |
79 | return error; | |
80 | ||
5be196e5 CH |
81 | mutex_lock(&inode->i_mutex); |
82 | error = security_inode_setxattr(dentry, name, value, size, flags); | |
83 | if (error) | |
84 | goto out; | |
85 | error = -EOPNOTSUPP; | |
86 | if (inode->i_op->setxattr) { | |
87 | error = inode->i_op->setxattr(dentry, name, value, size, flags); | |
88 | if (!error) { | |
89 | fsnotify_xattr(dentry); | |
90 | security_inode_post_setxattr(dentry, name, value, | |
91 | size, flags); | |
92 | } | |
93 | } else if (!strncmp(name, XATTR_SECURITY_PREFIX, | |
e0ad7b07 | 94 | XATTR_SECURITY_PREFIX_LEN)) { |
95 | const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; | |
5be196e5 CH |
96 | error = security_inode_setsecurity(inode, suffix, value, |
97 | size, flags); | |
98 | if (!error) | |
99 | fsnotify_xattr(dentry); | |
100 | } | |
101 | out: | |
102 | mutex_unlock(&inode->i_mutex); | |
103 | return error; | |
104 | } | |
105 | EXPORT_SYMBOL_GPL(vfs_setxattr); | |
106 | ||
42492594 DQ |
107 | ssize_t |
108 | xattr_getsecurity(struct inode *inode, const char *name, void *value, | |
109 | size_t size) | |
110 | { | |
111 | void *buffer = NULL; | |
112 | ssize_t len; | |
113 | ||
114 | if (!value || !size) { | |
115 | len = security_inode_getsecurity(inode, name, &buffer, false); | |
116 | goto out_noalloc; | |
117 | } | |
118 | ||
119 | len = security_inode_getsecurity(inode, name, &buffer, true); | |
120 | if (len < 0) | |
121 | return len; | |
122 | if (size < len) { | |
123 | len = -ERANGE; | |
124 | goto out; | |
125 | } | |
126 | memcpy(value, buffer, len); | |
127 | out: | |
128 | security_release_secctx(buffer, len); | |
129 | out_noalloc: | |
130 | return len; | |
131 | } | |
132 | EXPORT_SYMBOL_GPL(xattr_getsecurity); | |
133 | ||
5be196e5 CH |
134 | ssize_t |
135 | vfs_getxattr(struct dentry *dentry, char *name, void *value, size_t size) | |
136 | { | |
137 | struct inode *inode = dentry->d_inode; | |
138 | int error; | |
139 | ||
e0ad7b07 | 140 | error = xattr_permission(inode, name, MAY_READ); |
141 | if (error) | |
142 | return error; | |
143 | ||
5be196e5 CH |
144 | error = security_inode_getxattr(dentry, name); |
145 | if (error) | |
146 | return error; | |
147 | ||
5be196e5 | 148 | if (!strncmp(name, XATTR_SECURITY_PREFIX, |
e0ad7b07 | 149 | XATTR_SECURITY_PREFIX_LEN)) { |
150 | const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; | |
42492594 | 151 | int ret = xattr_getsecurity(inode, suffix, value, size); |
5be196e5 CH |
152 | /* |
153 | * Only overwrite the return value if a security module | |
154 | * is actually active. | |
155 | */ | |
4bea5805 DQ |
156 | if (ret == -EOPNOTSUPP) |
157 | goto nolsm; | |
158 | return ret; | |
5be196e5 | 159 | } |
4bea5805 DQ |
160 | nolsm: |
161 | if (inode->i_op->getxattr) | |
162 | error = inode->i_op->getxattr(dentry, name, value, size); | |
163 | else | |
164 | error = -EOPNOTSUPP; | |
5be196e5 CH |
165 | |
166 | return error; | |
167 | } | |
168 | EXPORT_SYMBOL_GPL(vfs_getxattr); | |
169 | ||
659564c8 BN |
170 | ssize_t |
171 | vfs_listxattr(struct dentry *d, char *list, size_t size) | |
172 | { | |
173 | ssize_t error; | |
174 | ||
175 | error = security_inode_listxattr(d); | |
176 | if (error) | |
177 | return error; | |
178 | error = -EOPNOTSUPP; | |
179 | if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { | |
180 | error = d->d_inode->i_op->listxattr(d, list, size); | |
181 | } else { | |
182 | error = security_inode_listsecurity(d->d_inode, list, size); | |
183 | if (size && error > size) | |
184 | error = -ERANGE; | |
185 | } | |
186 | return error; | |
187 | } | |
188 | EXPORT_SYMBOL_GPL(vfs_listxattr); | |
189 | ||
5be196e5 CH |
190 | int |
191 | vfs_removexattr(struct dentry *dentry, char *name) | |
192 | { | |
193 | struct inode *inode = dentry->d_inode; | |
194 | int error; | |
195 | ||
196 | if (!inode->i_op->removexattr) | |
197 | return -EOPNOTSUPP; | |
198 | ||
e0ad7b07 | 199 | error = xattr_permission(inode, name, MAY_WRITE); |
200 | if (error) | |
201 | return error; | |
202 | ||
5be196e5 CH |
203 | error = security_inode_removexattr(dentry, name); |
204 | if (error) | |
205 | return error; | |
206 | ||
207 | mutex_lock(&inode->i_mutex); | |
208 | error = inode->i_op->removexattr(dentry, name); | |
209 | mutex_unlock(&inode->i_mutex); | |
210 | ||
211 | if (!error) | |
212 | fsnotify_xattr(dentry); | |
213 | return error; | |
214 | } | |
215 | EXPORT_SYMBOL_GPL(vfs_removexattr); | |
216 | ||
217 | ||
1da177e4 LT |
218 | /* |
219 | * Extended attribute SET operations | |
220 | */ | |
221 | static long | |
222 | setxattr(struct dentry *d, char __user *name, void __user *value, | |
223 | size_t size, int flags) | |
224 | { | |
225 | int error; | |
226 | void *kvalue = NULL; | |
227 | char kname[XATTR_NAME_MAX + 1]; | |
228 | ||
229 | if (flags & ~(XATTR_CREATE|XATTR_REPLACE)) | |
230 | return -EINVAL; | |
231 | ||
232 | error = strncpy_from_user(kname, name, sizeof(kname)); | |
233 | if (error == 0 || error == sizeof(kname)) | |
234 | error = -ERANGE; | |
235 | if (error < 0) | |
236 | return error; | |
237 | ||
238 | if (size) { | |
239 | if (size > XATTR_SIZE_MAX) | |
240 | return -E2BIG; | |
241 | kvalue = kmalloc(size, GFP_KERNEL); | |
242 | if (!kvalue) | |
243 | return -ENOMEM; | |
244 | if (copy_from_user(kvalue, value, size)) { | |
245 | kfree(kvalue); | |
246 | return -EFAULT; | |
247 | } | |
248 | } | |
249 | ||
5be196e5 | 250 | error = vfs_setxattr(d, kname, kvalue, size, flags); |
f99d49ad | 251 | kfree(kvalue); |
1da177e4 LT |
252 | return error; |
253 | } | |
254 | ||
255 | asmlinkage long | |
256 | sys_setxattr(char __user *path, char __user *name, void __user *value, | |
257 | size_t size, int flags) | |
258 | { | |
259 | struct nameidata nd; | |
260 | int error; | |
261 | ||
262 | error = user_path_walk(path, &nd); | |
263 | if (error) | |
264 | return error; | |
4ac91378 | 265 | error = setxattr(nd.path.dentry, name, value, size, flags); |
1d957f9b | 266 | path_put(&nd.path); |
1da177e4 LT |
267 | return error; |
268 | } | |
269 | ||
270 | asmlinkage long | |
271 | sys_lsetxattr(char __user *path, char __user *name, void __user *value, | |
272 | size_t size, int flags) | |
273 | { | |
274 | struct nameidata nd; | |
275 | int error; | |
276 | ||
277 | error = user_path_walk_link(path, &nd); | |
278 | if (error) | |
279 | return error; | |
4ac91378 | 280 | error = setxattr(nd.path.dentry, name, value, size, flags); |
1d957f9b | 281 | path_put(&nd.path); |
1da177e4 LT |
282 | return error; |
283 | } | |
284 | ||
285 | asmlinkage long | |
286 | sys_fsetxattr(int fd, char __user *name, void __user *value, | |
287 | size_t size, int flags) | |
288 | { | |
289 | struct file *f; | |
73241ccc | 290 | struct dentry *dentry; |
1da177e4 LT |
291 | int error = -EBADF; |
292 | ||
293 | f = fget(fd); | |
294 | if (!f) | |
295 | return error; | |
0f7fc9e4 | 296 | dentry = f->f_path.dentry; |
5a190ae6 | 297 | audit_inode(NULL, dentry); |
73241ccc | 298 | error = setxattr(dentry, name, value, size, flags); |
1da177e4 LT |
299 | fput(f); |
300 | return error; | |
301 | } | |
302 | ||
303 | /* | |
304 | * Extended attribute GET operations | |
305 | */ | |
306 | static ssize_t | |
307 | getxattr(struct dentry *d, char __user *name, void __user *value, size_t size) | |
308 | { | |
309 | ssize_t error; | |
310 | void *kvalue = NULL; | |
311 | char kname[XATTR_NAME_MAX + 1]; | |
312 | ||
313 | error = strncpy_from_user(kname, name, sizeof(kname)); | |
314 | if (error == 0 || error == sizeof(kname)) | |
315 | error = -ERANGE; | |
316 | if (error < 0) | |
317 | return error; | |
318 | ||
319 | if (size) { | |
320 | if (size > XATTR_SIZE_MAX) | |
321 | size = XATTR_SIZE_MAX; | |
d381d8a9 | 322 | kvalue = kzalloc(size, GFP_KERNEL); |
1da177e4 LT |
323 | if (!kvalue) |
324 | return -ENOMEM; | |
325 | } | |
326 | ||
5be196e5 | 327 | error = vfs_getxattr(d, kname, kvalue, size); |
f549d6c1 SS |
328 | if (error > 0) { |
329 | if (size && copy_to_user(value, kvalue, error)) | |
330 | error = -EFAULT; | |
331 | } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { | |
332 | /* The file system tried to returned a value bigger | |
333 | than XATTR_SIZE_MAX bytes. Not possible. */ | |
334 | error = -E2BIG; | |
1da177e4 | 335 | } |
f99d49ad | 336 | kfree(kvalue); |
1da177e4 LT |
337 | return error; |
338 | } | |
339 | ||
340 | asmlinkage ssize_t | |
341 | sys_getxattr(char __user *path, char __user *name, void __user *value, | |
342 | size_t size) | |
343 | { | |
344 | struct nameidata nd; | |
345 | ssize_t error; | |
346 | ||
347 | error = user_path_walk(path, &nd); | |
348 | if (error) | |
349 | return error; | |
4ac91378 | 350 | error = getxattr(nd.path.dentry, name, value, size); |
1d957f9b | 351 | path_put(&nd.path); |
1da177e4 LT |
352 | return error; |
353 | } | |
354 | ||
355 | asmlinkage ssize_t | |
356 | sys_lgetxattr(char __user *path, char __user *name, void __user *value, | |
357 | size_t size) | |
358 | { | |
359 | struct nameidata nd; | |
360 | ssize_t error; | |
361 | ||
362 | error = user_path_walk_link(path, &nd); | |
363 | if (error) | |
364 | return error; | |
4ac91378 | 365 | error = getxattr(nd.path.dentry, name, value, size); |
1d957f9b | 366 | path_put(&nd.path); |
1da177e4 LT |
367 | return error; |
368 | } | |
369 | ||
370 | asmlinkage ssize_t | |
371 | sys_fgetxattr(int fd, char __user *name, void __user *value, size_t size) | |
372 | { | |
373 | struct file *f; | |
374 | ssize_t error = -EBADF; | |
375 | ||
376 | f = fget(fd); | |
377 | if (!f) | |
378 | return error; | |
5a190ae6 | 379 | audit_inode(NULL, f->f_path.dentry); |
0f7fc9e4 | 380 | error = getxattr(f->f_path.dentry, name, value, size); |
1da177e4 LT |
381 | fput(f); |
382 | return error; | |
383 | } | |
384 | ||
385 | /* | |
386 | * Extended attribute LIST operations | |
387 | */ | |
388 | static ssize_t | |
389 | listxattr(struct dentry *d, char __user *list, size_t size) | |
390 | { | |
391 | ssize_t error; | |
392 | char *klist = NULL; | |
393 | ||
394 | if (size) { | |
395 | if (size > XATTR_LIST_MAX) | |
396 | size = XATTR_LIST_MAX; | |
397 | klist = kmalloc(size, GFP_KERNEL); | |
398 | if (!klist) | |
399 | return -ENOMEM; | |
400 | } | |
401 | ||
659564c8 | 402 | error = vfs_listxattr(d, klist, size); |
f549d6c1 SS |
403 | if (error > 0) { |
404 | if (size && copy_to_user(list, klist, error)) | |
405 | error = -EFAULT; | |
406 | } else if (error == -ERANGE && size >= XATTR_LIST_MAX) { | |
407 | /* The file system tried to returned a list bigger | |
408 | than XATTR_LIST_MAX bytes. Not possible. */ | |
409 | error = -E2BIG; | |
1da177e4 | 410 | } |
f99d49ad | 411 | kfree(klist); |
1da177e4 LT |
412 | return error; |
413 | } | |
414 | ||
415 | asmlinkage ssize_t | |
416 | sys_listxattr(char __user *path, char __user *list, size_t size) | |
417 | { | |
418 | struct nameidata nd; | |
419 | ssize_t error; | |
420 | ||
421 | error = user_path_walk(path, &nd); | |
422 | if (error) | |
423 | return error; | |
4ac91378 | 424 | error = listxattr(nd.path.dentry, list, size); |
1d957f9b | 425 | path_put(&nd.path); |
1da177e4 LT |
426 | return error; |
427 | } | |
428 | ||
429 | asmlinkage ssize_t | |
430 | sys_llistxattr(char __user *path, char __user *list, size_t size) | |
431 | { | |
432 | struct nameidata nd; | |
433 | ssize_t error; | |
434 | ||
435 | error = user_path_walk_link(path, &nd); | |
436 | if (error) | |
437 | return error; | |
4ac91378 | 438 | error = listxattr(nd.path.dentry, list, size); |
1d957f9b | 439 | path_put(&nd.path); |
1da177e4 LT |
440 | return error; |
441 | } | |
442 | ||
443 | asmlinkage ssize_t | |
444 | sys_flistxattr(int fd, char __user *list, size_t size) | |
445 | { | |
446 | struct file *f; | |
447 | ssize_t error = -EBADF; | |
448 | ||
449 | f = fget(fd); | |
450 | if (!f) | |
451 | return error; | |
5a190ae6 | 452 | audit_inode(NULL, f->f_path.dentry); |
0f7fc9e4 | 453 | error = listxattr(f->f_path.dentry, list, size); |
1da177e4 LT |
454 | fput(f); |
455 | return error; | |
456 | } | |
457 | ||
458 | /* | |
459 | * Extended attribute REMOVE operations | |
460 | */ | |
461 | static long | |
462 | removexattr(struct dentry *d, char __user *name) | |
463 | { | |
464 | int error; | |
465 | char kname[XATTR_NAME_MAX + 1]; | |
466 | ||
467 | error = strncpy_from_user(kname, name, sizeof(kname)); | |
468 | if (error == 0 || error == sizeof(kname)) | |
469 | error = -ERANGE; | |
470 | if (error < 0) | |
471 | return error; | |
472 | ||
5be196e5 | 473 | return vfs_removexattr(d, kname); |
1da177e4 LT |
474 | } |
475 | ||
476 | asmlinkage long | |
477 | sys_removexattr(char __user *path, char __user *name) | |
478 | { | |
479 | struct nameidata nd; | |
480 | int error; | |
481 | ||
482 | error = user_path_walk(path, &nd); | |
483 | if (error) | |
484 | return error; | |
4ac91378 | 485 | error = removexattr(nd.path.dentry, name); |
1d957f9b | 486 | path_put(&nd.path); |
1da177e4 LT |
487 | return error; |
488 | } | |
489 | ||
490 | asmlinkage long | |
491 | sys_lremovexattr(char __user *path, char __user *name) | |
492 | { | |
493 | struct nameidata nd; | |
494 | int error; | |
495 | ||
496 | error = user_path_walk_link(path, &nd); | |
497 | if (error) | |
498 | return error; | |
4ac91378 | 499 | error = removexattr(nd.path.dentry, name); |
1d957f9b | 500 | path_put(&nd.path); |
1da177e4 LT |
501 | return error; |
502 | } | |
503 | ||
504 | asmlinkage long | |
505 | sys_fremovexattr(int fd, char __user *name) | |
506 | { | |
507 | struct file *f; | |
73241ccc | 508 | struct dentry *dentry; |
1da177e4 LT |
509 | int error = -EBADF; |
510 | ||
511 | f = fget(fd); | |
512 | if (!f) | |
513 | return error; | |
0f7fc9e4 | 514 | dentry = f->f_path.dentry; |
5a190ae6 | 515 | audit_inode(NULL, dentry); |
73241ccc | 516 | error = removexattr(dentry, name); |
1da177e4 LT |
517 | fput(f); |
518 | return error; | |
519 | } | |
520 | ||
521 | ||
522 | static const char * | |
523 | strcmp_prefix(const char *a, const char *a_prefix) | |
524 | { | |
525 | while (*a_prefix && *a == *a_prefix) { | |
526 | a++; | |
527 | a_prefix++; | |
528 | } | |
529 | return *a_prefix ? NULL : a; | |
530 | } | |
531 | ||
532 | /* | |
533 | * In order to implement different sets of xattr operations for each xattr | |
534 | * prefix with the generic xattr API, a filesystem should create a | |
535 | * null-terminated array of struct xattr_handler (one for each prefix) and | |
536 | * hang a pointer to it off of the s_xattr field of the superblock. | |
537 | * | |
538 | * The generic_fooxattr() functions will use this list to dispatch xattr | |
539 | * operations to the correct xattr_handler. | |
540 | */ | |
541 | #define for_each_xattr_handler(handlers, handler) \ | |
542 | for ((handler) = *(handlers)++; \ | |
543 | (handler) != NULL; \ | |
544 | (handler) = *(handlers)++) | |
545 | ||
546 | /* | |
547 | * Find the xattr_handler with the matching prefix. | |
548 | */ | |
549 | static struct xattr_handler * | |
550 | xattr_resolve_name(struct xattr_handler **handlers, const char **name) | |
551 | { | |
552 | struct xattr_handler *handler; | |
553 | ||
554 | if (!*name) | |
555 | return NULL; | |
556 | ||
557 | for_each_xattr_handler(handlers, handler) { | |
558 | const char *n = strcmp_prefix(*name, handler->prefix); | |
559 | if (n) { | |
560 | *name = n; | |
561 | break; | |
562 | } | |
563 | } | |
564 | return handler; | |
565 | } | |
566 | ||
567 | /* | |
568 | * Find the handler for the prefix and dispatch its get() operation. | |
569 | */ | |
570 | ssize_t | |
571 | generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) | |
572 | { | |
573 | struct xattr_handler *handler; | |
574 | struct inode *inode = dentry->d_inode; | |
575 | ||
576 | handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); | |
577 | if (!handler) | |
578 | return -EOPNOTSUPP; | |
579 | return handler->get(inode, name, buffer, size); | |
580 | } | |
581 | ||
582 | /* | |
583 | * Combine the results of the list() operation from every xattr_handler in the | |
584 | * list. | |
585 | */ | |
586 | ssize_t | |
587 | generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) | |
588 | { | |
589 | struct inode *inode = dentry->d_inode; | |
590 | struct xattr_handler *handler, **handlers = inode->i_sb->s_xattr; | |
591 | unsigned int size = 0; | |
592 | ||
593 | if (!buffer) { | |
594 | for_each_xattr_handler(handlers, handler) | |
595 | size += handler->list(inode, NULL, 0, NULL, 0); | |
596 | } else { | |
597 | char *buf = buffer; | |
598 | ||
599 | for_each_xattr_handler(handlers, handler) { | |
600 | size = handler->list(inode, buf, buffer_size, NULL, 0); | |
601 | if (size > buffer_size) | |
602 | return -ERANGE; | |
603 | buf += size; | |
604 | buffer_size -= size; | |
605 | } | |
606 | size = buf - buffer; | |
607 | } | |
608 | return size; | |
609 | } | |
610 | ||
611 | /* | |
612 | * Find the handler for the prefix and dispatch its set() operation. | |
613 | */ | |
614 | int | |
615 | generic_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) | |
616 | { | |
617 | struct xattr_handler *handler; | |
618 | struct inode *inode = dentry->d_inode; | |
619 | ||
620 | if (size == 0) | |
621 | value = ""; /* empty EA, do not remove */ | |
622 | handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); | |
623 | if (!handler) | |
624 | return -EOPNOTSUPP; | |
625 | return handler->set(inode, name, value, size, flags); | |
626 | } | |
627 | ||
628 | /* | |
629 | * Find the handler for the prefix and dispatch its set() operation to remove | |
630 | * any associated extended attribute. | |
631 | */ | |
632 | int | |
633 | generic_removexattr(struct dentry *dentry, const char *name) | |
634 | { | |
635 | struct xattr_handler *handler; | |
636 | struct inode *inode = dentry->d_inode; | |
637 | ||
638 | handler = xattr_resolve_name(inode->i_sb->s_xattr, &name); | |
639 | if (!handler) | |
640 | return -EOPNOTSUPP; | |
641 | return handler->set(inode, name, NULL, 0, XATTR_REPLACE); | |
642 | } | |
643 | ||
644 | EXPORT_SYMBOL(generic_getxattr); | |
645 | EXPORT_SYMBOL(generic_listxattr); | |
646 | EXPORT_SYMBOL(generic_setxattr); | |
647 | EXPORT_SYMBOL(generic_removexattr); |