diff options
Diffstat (limited to 'fs/xattr.c')
-rw-r--r-- | fs/xattr.c | 245 |
1 files changed, 213 insertions, 32 deletions
diff --git a/fs/xattr.c b/fs/xattr.c index 4d45b7189e7e..1780f062dbaf 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/fsnotify.h> | 20 | #include <linux/fsnotify.h> |
21 | #include <linux/audit.h> | 21 | #include <linux/audit.h> |
22 | #include <linux/vmalloc.h> | 22 | #include <linux/vmalloc.h> |
23 | #include <linux/posix_acl_xattr.h> | ||
23 | 24 | ||
24 | #include <asm/uaccess.h> | 25 | #include <asm/uaccess.h> |
25 | 26 | ||
@@ -295,11 +296,13 @@ vfs_removexattr(struct dentry *dentry, const char *name) | |||
295 | if (error) | 296 | if (error) |
296 | return error; | 297 | return error; |
297 | 298 | ||
299 | mutex_lock(&inode->i_mutex); | ||
298 | error = security_inode_removexattr(dentry, name); | 300 | error = security_inode_removexattr(dentry, name); |
299 | if (error) | 301 | if (error) { |
302 | mutex_unlock(&inode->i_mutex); | ||
300 | return error; | 303 | return error; |
304 | } | ||
301 | 305 | ||
302 | mutex_lock(&inode->i_mutex); | ||
303 | error = inode->i_op->removexattr(dentry, name); | 306 | error = inode->i_op->removexattr(dentry, name); |
304 | mutex_unlock(&inode->i_mutex); | 307 | mutex_unlock(&inode->i_mutex); |
305 | 308 | ||
@@ -347,6 +350,9 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value, | |||
347 | error = -EFAULT; | 350 | error = -EFAULT; |
348 | goto out; | 351 | goto out; |
349 | } | 352 | } |
353 | if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) || | ||
354 | (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)) | ||
355 | posix_acl_fix_xattr_from_user(kvalue, size); | ||
350 | } | 356 | } |
351 | 357 | ||
352 | error = vfs_setxattr(d, kname, kvalue, size, flags); | 358 | error = vfs_setxattr(d, kname, kvalue, size, flags); |
@@ -399,22 +405,20 @@ SYSCALL_DEFINE5(lsetxattr, const char __user *, pathname, | |||
399 | SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, | 405 | SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name, |
400 | const void __user *,value, size_t, size, int, flags) | 406 | const void __user *,value, size_t, size, int, flags) |
401 | { | 407 | { |
402 | int fput_needed; | 408 | struct fd f = fdget(fd); |
403 | struct file *f; | ||
404 | struct dentry *dentry; | 409 | struct dentry *dentry; |
405 | int error = -EBADF; | 410 | int error = -EBADF; |
406 | 411 | ||
407 | f = fget_light(fd, &fput_needed); | 412 | if (!f.file) |
408 | if (!f) | ||
409 | return error; | 413 | return error; |
410 | dentry = f->f_path.dentry; | 414 | dentry = f.file->f_path.dentry; |
411 | audit_inode(NULL, dentry); | 415 | audit_inode(NULL, dentry); |
412 | error = mnt_want_write_file(f); | 416 | error = mnt_want_write_file(f.file); |
413 | if (!error) { | 417 | if (!error) { |
414 | error = setxattr(dentry, name, value, size, flags); | 418 | error = setxattr(dentry, name, value, size, flags); |
415 | mnt_drop_write_file(f); | 419 | mnt_drop_write_file(f.file); |
416 | } | 420 | } |
417 | fput_light(f, fput_needed); | 421 | fdput(f); |
418 | return error; | 422 | return error; |
419 | } | 423 | } |
420 | 424 | ||
@@ -450,6 +454,9 @@ getxattr(struct dentry *d, const char __user *name, void __user *value, | |||
450 | 454 | ||
451 | error = vfs_getxattr(d, kname, kvalue, size); | 455 | error = vfs_getxattr(d, kname, kvalue, size); |
452 | if (error > 0) { | 456 | if (error > 0) { |
457 | if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) || | ||
458 | (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)) | ||
459 | posix_acl_fix_xattr_to_user(kvalue, size); | ||
453 | if (size && copy_to_user(value, kvalue, error)) | 460 | if (size && copy_to_user(value, kvalue, error)) |
454 | error = -EFAULT; | 461 | error = -EFAULT; |
455 | } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { | 462 | } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) { |
@@ -495,16 +502,14 @@ SYSCALL_DEFINE4(lgetxattr, const char __user *, pathname, | |||
495 | SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, | 502 | SYSCALL_DEFINE4(fgetxattr, int, fd, const char __user *, name, |
496 | void __user *, value, size_t, size) | 503 | void __user *, value, size_t, size) |
497 | { | 504 | { |
498 | int fput_needed; | 505 | struct fd f = fdget(fd); |
499 | struct file *f; | ||
500 | ssize_t error = -EBADF; | 506 | ssize_t error = -EBADF; |
501 | 507 | ||
502 | f = fget_light(fd, &fput_needed); | 508 | if (!f.file) |
503 | if (!f) | ||
504 | return error; | 509 | return error; |
505 | audit_inode(NULL, f->f_path.dentry); | 510 | audit_inode(NULL, f.file->f_path.dentry); |
506 | error = getxattr(f->f_path.dentry, name, value, size); | 511 | error = getxattr(f.file->f_path.dentry, name, value, size); |
507 | fput_light(f, fput_needed); | 512 | fdput(f); |
508 | return error; | 513 | return error; |
509 | } | 514 | } |
510 | 515 | ||
@@ -576,16 +581,14 @@ SYSCALL_DEFINE3(llistxattr, const char __user *, pathname, char __user *, list, | |||
576 | 581 | ||
577 | SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) | 582 | SYSCALL_DEFINE3(flistxattr, int, fd, char __user *, list, size_t, size) |
578 | { | 583 | { |
579 | int fput_needed; | 584 | struct fd f = fdget(fd); |
580 | struct file *f; | ||
581 | ssize_t error = -EBADF; | 585 | ssize_t error = -EBADF; |
582 | 586 | ||
583 | f = fget_light(fd, &fput_needed); | 587 | if (!f.file) |
584 | if (!f) | ||
585 | return error; | 588 | return error; |
586 | audit_inode(NULL, f->f_path.dentry); | 589 | audit_inode(NULL, f.file->f_path.dentry); |
587 | error = listxattr(f->f_path.dentry, list, size); | 590 | error = listxattr(f.file->f_path.dentry, list, size); |
588 | fput_light(f, fput_needed); | 591 | fdput(f); |
589 | return error; | 592 | return error; |
590 | } | 593 | } |
591 | 594 | ||
@@ -645,22 +648,20 @@ SYSCALL_DEFINE2(lremovexattr, const char __user *, pathname, | |||
645 | 648 | ||
646 | SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) | 649 | SYSCALL_DEFINE2(fremovexattr, int, fd, const char __user *, name) |
647 | { | 650 | { |
648 | int fput_needed; | 651 | struct fd f = fdget(fd); |
649 | struct file *f; | ||
650 | struct dentry *dentry; | 652 | struct dentry *dentry; |
651 | int error = -EBADF; | 653 | int error = -EBADF; |
652 | 654 | ||
653 | f = fget_light(fd, &fput_needed); | 655 | if (!f.file) |
654 | if (!f) | ||
655 | return error; | 656 | return error; |
656 | dentry = f->f_path.dentry; | 657 | dentry = f.file->f_path.dentry; |
657 | audit_inode(NULL, dentry); | 658 | audit_inode(NULL, dentry); |
658 | error = mnt_want_write_file(f); | 659 | error = mnt_want_write_file(f.file); |
659 | if (!error) { | 660 | if (!error) { |
660 | error = removexattr(dentry, name); | 661 | error = removexattr(dentry, name); |
661 | mnt_drop_write_file(f); | 662 | mnt_drop_write_file(f.file); |
662 | } | 663 | } |
663 | fput_light(f, fput_needed); | 664 | fdput(f); |
664 | return error; | 665 | return error; |
665 | } | 666 | } |
666 | 667 | ||
@@ -791,3 +792,183 @@ EXPORT_SYMBOL(generic_getxattr); | |||
791 | EXPORT_SYMBOL(generic_listxattr); | 792 | EXPORT_SYMBOL(generic_listxattr); |
792 | EXPORT_SYMBOL(generic_setxattr); | 793 | EXPORT_SYMBOL(generic_setxattr); |
793 | EXPORT_SYMBOL(generic_removexattr); | 794 | EXPORT_SYMBOL(generic_removexattr); |
795 | |||
796 | /* | ||
797 | * Allocate new xattr and copy in the value; but leave the name to callers. | ||
798 | */ | ||
799 | struct simple_xattr *simple_xattr_alloc(const void *value, size_t size) | ||
800 | { | ||
801 | struct simple_xattr *new_xattr; | ||
802 | size_t len; | ||
803 | |||
804 | /* wrap around? */ | ||
805 | len = sizeof(*new_xattr) + size; | ||
806 | if (len <= sizeof(*new_xattr)) | ||
807 | return NULL; | ||
808 | |||
809 | new_xattr = kmalloc(len, GFP_KERNEL); | ||
810 | if (!new_xattr) | ||
811 | return NULL; | ||
812 | |||
813 | new_xattr->size = size; | ||
814 | memcpy(new_xattr->value, value, size); | ||
815 | return new_xattr; | ||
816 | } | ||
817 | |||
818 | /* | ||
819 | * xattr GET operation for in-memory/pseudo filesystems | ||
820 | */ | ||
821 | int simple_xattr_get(struct simple_xattrs *xattrs, const char *name, | ||
822 | void *buffer, size_t size) | ||
823 | { | ||
824 | struct simple_xattr *xattr; | ||
825 | int ret = -ENODATA; | ||
826 | |||
827 | spin_lock(&xattrs->lock); | ||
828 | list_for_each_entry(xattr, &xattrs->head, list) { | ||
829 | if (strcmp(name, xattr->name)) | ||
830 | continue; | ||
831 | |||
832 | ret = xattr->size; | ||
833 | if (buffer) { | ||
834 | if (size < xattr->size) | ||
835 | ret = -ERANGE; | ||
836 | else | ||
837 | memcpy(buffer, xattr->value, xattr->size); | ||
838 | } | ||
839 | break; | ||
840 | } | ||
841 | spin_unlock(&xattrs->lock); | ||
842 | return ret; | ||
843 | } | ||
844 | |||
845 | static int __simple_xattr_set(struct simple_xattrs *xattrs, const char *name, | ||
846 | const void *value, size_t size, int flags) | ||
847 | { | ||
848 | struct simple_xattr *xattr; | ||
849 | struct simple_xattr *uninitialized_var(new_xattr); | ||
850 | int err = 0; | ||
851 | |||
852 | /* value == NULL means remove */ | ||
853 | if (value) { | ||
854 | new_xattr = simple_xattr_alloc(value, size); | ||
855 | if (!new_xattr) | ||
856 | return -ENOMEM; | ||
857 | |||
858 | new_xattr->name = kstrdup(name, GFP_KERNEL); | ||
859 | if (!new_xattr->name) { | ||
860 | kfree(new_xattr); | ||
861 | return -ENOMEM; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | spin_lock(&xattrs->lock); | ||
866 | list_for_each_entry(xattr, &xattrs->head, list) { | ||
867 | if (!strcmp(name, xattr->name)) { | ||
868 | if (flags & XATTR_CREATE) { | ||
869 | xattr = new_xattr; | ||
870 | err = -EEXIST; | ||
871 | } else if (new_xattr) { | ||
872 | list_replace(&xattr->list, &new_xattr->list); | ||
873 | } else { | ||
874 | list_del(&xattr->list); | ||
875 | } | ||
876 | goto out; | ||
877 | } | ||
878 | } | ||
879 | if (flags & XATTR_REPLACE) { | ||
880 | xattr = new_xattr; | ||
881 | err = -ENODATA; | ||
882 | } else { | ||
883 | list_add(&new_xattr->list, &xattrs->head); | ||
884 | xattr = NULL; | ||
885 | } | ||
886 | out: | ||
887 | spin_unlock(&xattrs->lock); | ||
888 | if (xattr) { | ||
889 | kfree(xattr->name); | ||
890 | kfree(xattr); | ||
891 | } | ||
892 | return err; | ||
893 | |||
894 | } | ||
895 | |||
896 | /** | ||
897 | * simple_xattr_set - xattr SET operation for in-memory/pseudo filesystems | ||
898 | * @xattrs: target simple_xattr list | ||
899 | * @name: name of the new extended attribute | ||
900 | * @value: value of the new xattr. If %NULL, will remove the attribute | ||
901 | * @size: size of the new xattr | ||
902 | * @flags: %XATTR_{CREATE|REPLACE} | ||
903 | * | ||
904 | * %XATTR_CREATE is set, the xattr shouldn't exist already; otherwise fails | ||
905 | * with -EEXIST. If %XATTR_REPLACE is set, the xattr should exist; | ||
906 | * otherwise, fails with -ENODATA. | ||
907 | * | ||
908 | * Returns 0 on success, -errno on failure. | ||
909 | */ | ||
910 | int simple_xattr_set(struct simple_xattrs *xattrs, const char *name, | ||
911 | const void *value, size_t size, int flags) | ||
912 | { | ||
913 | if (size == 0) | ||
914 | value = ""; /* empty EA, do not remove */ | ||
915 | return __simple_xattr_set(xattrs, name, value, size, flags); | ||
916 | } | ||
917 | |||
918 | /* | ||
919 | * xattr REMOVE operation for in-memory/pseudo filesystems | ||
920 | */ | ||
921 | int simple_xattr_remove(struct simple_xattrs *xattrs, const char *name) | ||
922 | { | ||
923 | return __simple_xattr_set(xattrs, name, NULL, 0, XATTR_REPLACE); | ||
924 | } | ||
925 | |||
926 | static bool xattr_is_trusted(const char *name) | ||
927 | { | ||
928 | return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); | ||
929 | } | ||
930 | |||
931 | /* | ||
932 | * xattr LIST operation for in-memory/pseudo filesystems | ||
933 | */ | ||
934 | ssize_t simple_xattr_list(struct simple_xattrs *xattrs, char *buffer, | ||
935 | size_t size) | ||
936 | { | ||
937 | bool trusted = capable(CAP_SYS_ADMIN); | ||
938 | struct simple_xattr *xattr; | ||
939 | size_t used = 0; | ||
940 | |||
941 | spin_lock(&xattrs->lock); | ||
942 | list_for_each_entry(xattr, &xattrs->head, list) { | ||
943 | size_t len; | ||
944 | |||
945 | /* skip "trusted." attributes for unprivileged callers */ | ||
946 | if (!trusted && xattr_is_trusted(xattr->name)) | ||
947 | continue; | ||
948 | |||
949 | len = strlen(xattr->name) + 1; | ||
950 | used += len; | ||
951 | if (buffer) { | ||
952 | if (size < used) { | ||
953 | used = -ERANGE; | ||
954 | break; | ||
955 | } | ||
956 | memcpy(buffer, xattr->name, len); | ||
957 | buffer += len; | ||
958 | } | ||
959 | } | ||
960 | spin_unlock(&xattrs->lock); | ||
961 | |||
962 | return used; | ||
963 | } | ||
964 | |||
965 | /* | ||
966 | * Adds an extended attribute to the list | ||
967 | */ | ||
968 | void simple_xattr_list_add(struct simple_xattrs *xattrs, | ||
969 | struct simple_xattr *new_xattr) | ||
970 | { | ||
971 | spin_lock(&xattrs->lock); | ||
972 | list_add(&new_xattr->list, &xattrs->head); | ||
973 | spin_unlock(&xattrs->lock); | ||
974 | } | ||