diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-28 11:38:04 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-28 11:38:04 -0500 |
commit | bf3d846b783327359ddc4bd4f52627b36abb4d1d (patch) | |
tree | c6b8fddbf04a2962dfcf9f487af25033f11b10b9 /fs/posix_acl.c | |
parent | 54c0a4b46150db1571d955d598cd342c9f1d9657 (diff) | |
parent | f6500801522c61782d4990fa1ad96154cb397cd4 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs updates from Al Viro:
"Assorted stuff; the biggest pile here is Christoph's ACL series. Plus
assorted cleanups and fixes all over the place...
There will be another pile later this week"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (43 commits)
__dentry_path() fixes
vfs: Remove second variable named error in __dentry_path
vfs: Is mounted should be testing mnt_ns for NULL or error.
Fix race when checking i_size on direct i/o read
hfsplus: remove can_set_xattr
nfsd: use get_acl and ->set_acl
fs: remove generic_acl
nfs: use generic posix ACL infrastructure for v3 Posix ACLs
gfs2: use generic posix ACL infrastructure
jfs: use generic posix ACL infrastructure
xfs: use generic posix ACL infrastructure
reiserfs: use generic posix ACL infrastructure
ocfs2: use generic posix ACL infrastructure
jffs2: use generic posix ACL infrastructure
hfsplus: use generic posix ACL infrastructure
f2fs: use generic posix ACL infrastructure
ext2/3/4: use generic posix ACL infrastructure
btrfs: use generic posix ACL infrastructure
fs: make posix_acl_create more useful
fs: make posix_acl_chmod more useful
...
Diffstat (limited to 'fs/posix_acl.c')
-rw-r--r-- | fs/posix_acl.c | 428 |
1 files changed, 416 insertions, 12 deletions
diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 551e61ba15b6..38bae5a0ea25 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c | |||
@@ -1,10 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * linux/fs/posix_acl.c | 2 | * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org> |
3 | * | 3 | * |
4 | * Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org> | 4 | * Fixes from William Schumacher incorporated on 15 March 2001. |
5 | * | 5 | * (Reported by Charles Bertsch, <CBertsch@microtest.com>). |
6 | * Fixes from William Schumacher incorporated on 15 March 2001. | ||
7 | * (Reported by Charles Bertsch, <CBertsch@microtest.com>). | ||
8 | */ | 6 | */ |
9 | 7 | ||
10 | /* | 8 | /* |
@@ -18,9 +16,10 @@ | |||
18 | #include <linux/fs.h> | 16 | #include <linux/fs.h> |
19 | #include <linux/sched.h> | 17 | #include <linux/sched.h> |
20 | #include <linux/posix_acl.h> | 18 | #include <linux/posix_acl.h> |
19 | #include <linux/posix_acl_xattr.h> | ||
20 | #include <linux/xattr.h> | ||
21 | #include <linux/export.h> | 21 | #include <linux/export.h> |
22 | 22 | #include <linux/user_namespace.h> | |
23 | #include <linux/errno.h> | ||
24 | 23 | ||
25 | struct posix_acl **acl_by_type(struct inode *inode, int type) | 24 | struct posix_acl **acl_by_type(struct inode *inode, int type) |
26 | { | 25 | { |
@@ -97,6 +96,33 @@ void forget_all_cached_acls(struct inode *inode) | |||
97 | } | 96 | } |
98 | EXPORT_SYMBOL(forget_all_cached_acls); | 97 | EXPORT_SYMBOL(forget_all_cached_acls); |
99 | 98 | ||
99 | struct posix_acl *get_acl(struct inode *inode, int type) | ||
100 | { | ||
101 | struct posix_acl *acl; | ||
102 | |||
103 | acl = get_cached_acl(inode, type); | ||
104 | if (acl != ACL_NOT_CACHED) | ||
105 | return acl; | ||
106 | |||
107 | if (!IS_POSIXACL(inode)) | ||
108 | return NULL; | ||
109 | |||
110 | /* | ||
111 | * A filesystem can force a ACL callback by just never filling the | ||
112 | * ACL cache. But normally you'd fill the cache either at inode | ||
113 | * instantiation time, or on the first ->get_acl call. | ||
114 | * | ||
115 | * If the filesystem doesn't have a get_acl() function at all, we'll | ||
116 | * just create the negative cache entry. | ||
117 | */ | ||
118 | if (!inode->i_op->get_acl) { | ||
119 | set_cached_acl(inode, type, NULL); | ||
120 | return NULL; | ||
121 | } | ||
122 | return inode->i_op->get_acl(inode, type); | ||
123 | } | ||
124 | EXPORT_SYMBOL(get_acl); | ||
125 | |||
100 | /* | 126 | /* |
101 | * Init a fresh posix_acl | 127 | * Init a fresh posix_acl |
102 | */ | 128 | */ |
@@ -402,7 +428,7 @@ static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p) | |||
402 | /* | 428 | /* |
403 | * Modify the ACL for the chmod syscall. | 429 | * Modify the ACL for the chmod syscall. |
404 | */ | 430 | */ |
405 | static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) | 431 | static int __posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) |
406 | { | 432 | { |
407 | struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; | 433 | struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; |
408 | struct posix_acl_entry *pa, *pe; | 434 | struct posix_acl_entry *pa, *pe; |
@@ -448,7 +474,7 @@ static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) | |||
448 | } | 474 | } |
449 | 475 | ||
450 | int | 476 | int |
451 | posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) | 477 | __posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) |
452 | { | 478 | { |
453 | struct posix_acl *clone = posix_acl_clone(*acl, gfp); | 479 | struct posix_acl *clone = posix_acl_clone(*acl, gfp); |
454 | int err = -ENOMEM; | 480 | int err = -ENOMEM; |
@@ -463,15 +489,15 @@ posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p) | |||
463 | *acl = clone; | 489 | *acl = clone; |
464 | return err; | 490 | return err; |
465 | } | 491 | } |
466 | EXPORT_SYMBOL(posix_acl_create); | 492 | EXPORT_SYMBOL(__posix_acl_create); |
467 | 493 | ||
468 | int | 494 | int |
469 | posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) | 495 | __posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) |
470 | { | 496 | { |
471 | struct posix_acl *clone = posix_acl_clone(*acl, gfp); | 497 | struct posix_acl *clone = posix_acl_clone(*acl, gfp); |
472 | int err = -ENOMEM; | 498 | int err = -ENOMEM; |
473 | if (clone) { | 499 | if (clone) { |
474 | err = posix_acl_chmod_masq(clone, mode); | 500 | err = __posix_acl_chmod_masq(clone, mode); |
475 | if (err) { | 501 | if (err) { |
476 | posix_acl_release(clone); | 502 | posix_acl_release(clone); |
477 | clone = NULL; | 503 | clone = NULL; |
@@ -481,4 +507,382 @@ posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode) | |||
481 | *acl = clone; | 507 | *acl = clone; |
482 | return err; | 508 | return err; |
483 | } | 509 | } |
510 | EXPORT_SYMBOL(__posix_acl_chmod); | ||
511 | |||
512 | int | ||
513 | posix_acl_chmod(struct inode *inode, umode_t mode) | ||
514 | { | ||
515 | struct posix_acl *acl; | ||
516 | int ret = 0; | ||
517 | |||
518 | if (!IS_POSIXACL(inode)) | ||
519 | return 0; | ||
520 | if (!inode->i_op->set_acl) | ||
521 | return -EOPNOTSUPP; | ||
522 | |||
523 | acl = get_acl(inode, ACL_TYPE_ACCESS); | ||
524 | if (IS_ERR_OR_NULL(acl)) | ||
525 | return PTR_ERR(acl); | ||
526 | |||
527 | ret = __posix_acl_chmod(&acl, GFP_KERNEL, mode); | ||
528 | if (ret) | ||
529 | return ret; | ||
530 | ret = inode->i_op->set_acl(inode, acl, ACL_TYPE_ACCESS); | ||
531 | posix_acl_release(acl); | ||
532 | return ret; | ||
533 | } | ||
484 | EXPORT_SYMBOL(posix_acl_chmod); | 534 | EXPORT_SYMBOL(posix_acl_chmod); |
535 | |||
536 | int | ||
537 | posix_acl_create(struct inode *dir, umode_t *mode, | ||
538 | struct posix_acl **default_acl, struct posix_acl **acl) | ||
539 | { | ||
540 | struct posix_acl *p; | ||
541 | int ret; | ||
542 | |||
543 | if (S_ISLNK(*mode) || !IS_POSIXACL(dir)) | ||
544 | goto no_acl; | ||
545 | |||
546 | p = get_acl(dir, ACL_TYPE_DEFAULT); | ||
547 | if (IS_ERR(p)) | ||
548 | return PTR_ERR(p); | ||
549 | |||
550 | if (!p) { | ||
551 | *mode &= ~current_umask(); | ||
552 | goto no_acl; | ||
553 | } | ||
554 | |||
555 | *acl = posix_acl_clone(p, GFP_NOFS); | ||
556 | if (!*acl) | ||
557 | return -ENOMEM; | ||
558 | |||
559 | ret = posix_acl_create_masq(*acl, mode); | ||
560 | if (ret < 0) { | ||
561 | posix_acl_release(*acl); | ||
562 | return -ENOMEM; | ||
563 | } | ||
564 | |||
565 | if (ret == 0) { | ||
566 | posix_acl_release(*acl); | ||
567 | *acl = NULL; | ||
568 | } | ||
569 | |||
570 | if (!S_ISDIR(*mode)) { | ||
571 | posix_acl_release(p); | ||
572 | *default_acl = NULL; | ||
573 | } else { | ||
574 | *default_acl = p; | ||
575 | } | ||
576 | return 0; | ||
577 | |||
578 | no_acl: | ||
579 | *default_acl = NULL; | ||
580 | *acl = NULL; | ||
581 | return 0; | ||
582 | } | ||
583 | EXPORT_SYMBOL_GPL(posix_acl_create); | ||
584 | |||
585 | /* | ||
586 | * Fix up the uids and gids in posix acl extended attributes in place. | ||
587 | */ | ||
588 | static void posix_acl_fix_xattr_userns( | ||
589 | struct user_namespace *to, struct user_namespace *from, | ||
590 | void *value, size_t size) | ||
591 | { | ||
592 | posix_acl_xattr_header *header = (posix_acl_xattr_header *)value; | ||
593 | posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end; | ||
594 | int count; | ||
595 | kuid_t uid; | ||
596 | kgid_t gid; | ||
597 | |||
598 | if (!value) | ||
599 | return; | ||
600 | if (size < sizeof(posix_acl_xattr_header)) | ||
601 | return; | ||
602 | if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) | ||
603 | return; | ||
604 | |||
605 | count = posix_acl_xattr_count(size); | ||
606 | if (count < 0) | ||
607 | return; | ||
608 | if (count == 0) | ||
609 | return; | ||
610 | |||
611 | for (end = entry + count; entry != end; entry++) { | ||
612 | switch(le16_to_cpu(entry->e_tag)) { | ||
613 | case ACL_USER: | ||
614 | uid = make_kuid(from, le32_to_cpu(entry->e_id)); | ||
615 | entry->e_id = cpu_to_le32(from_kuid(to, uid)); | ||
616 | break; | ||
617 | case ACL_GROUP: | ||
618 | gid = make_kgid(from, le32_to_cpu(entry->e_id)); | ||
619 | entry->e_id = cpu_to_le32(from_kgid(to, gid)); | ||
620 | break; | ||
621 | default: | ||
622 | break; | ||
623 | } | ||
624 | } | ||
625 | } | ||
626 | |||
627 | void posix_acl_fix_xattr_from_user(void *value, size_t size) | ||
628 | { | ||
629 | struct user_namespace *user_ns = current_user_ns(); | ||
630 | if (user_ns == &init_user_ns) | ||
631 | return; | ||
632 | posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size); | ||
633 | } | ||
634 | |||
635 | void posix_acl_fix_xattr_to_user(void *value, size_t size) | ||
636 | { | ||
637 | struct user_namespace *user_ns = current_user_ns(); | ||
638 | if (user_ns == &init_user_ns) | ||
639 | return; | ||
640 | posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size); | ||
641 | } | ||
642 | |||
643 | /* | ||
644 | * Convert from extended attribute to in-memory representation. | ||
645 | */ | ||
646 | struct posix_acl * | ||
647 | posix_acl_from_xattr(struct user_namespace *user_ns, | ||
648 | const void *value, size_t size) | ||
649 | { | ||
650 | posix_acl_xattr_header *header = (posix_acl_xattr_header *)value; | ||
651 | posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end; | ||
652 | int count; | ||
653 | struct posix_acl *acl; | ||
654 | struct posix_acl_entry *acl_e; | ||
655 | |||
656 | if (!value) | ||
657 | return NULL; | ||
658 | if (size < sizeof(posix_acl_xattr_header)) | ||
659 | return ERR_PTR(-EINVAL); | ||
660 | if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) | ||
661 | return ERR_PTR(-EOPNOTSUPP); | ||
662 | |||
663 | count = posix_acl_xattr_count(size); | ||
664 | if (count < 0) | ||
665 | return ERR_PTR(-EINVAL); | ||
666 | if (count == 0) | ||
667 | return NULL; | ||
668 | |||
669 | acl = posix_acl_alloc(count, GFP_NOFS); | ||
670 | if (!acl) | ||
671 | return ERR_PTR(-ENOMEM); | ||
672 | acl_e = acl->a_entries; | ||
673 | |||
674 | for (end = entry + count; entry != end; acl_e++, entry++) { | ||
675 | acl_e->e_tag = le16_to_cpu(entry->e_tag); | ||
676 | acl_e->e_perm = le16_to_cpu(entry->e_perm); | ||
677 | |||
678 | switch(acl_e->e_tag) { | ||
679 | case ACL_USER_OBJ: | ||
680 | case ACL_GROUP_OBJ: | ||
681 | case ACL_MASK: | ||
682 | case ACL_OTHER: | ||
683 | break; | ||
684 | |||
685 | case ACL_USER: | ||
686 | acl_e->e_uid = | ||
687 | make_kuid(user_ns, | ||
688 | le32_to_cpu(entry->e_id)); | ||
689 | if (!uid_valid(acl_e->e_uid)) | ||
690 | goto fail; | ||
691 | break; | ||
692 | case ACL_GROUP: | ||
693 | acl_e->e_gid = | ||
694 | make_kgid(user_ns, | ||
695 | le32_to_cpu(entry->e_id)); | ||
696 | if (!gid_valid(acl_e->e_gid)) | ||
697 | goto fail; | ||
698 | break; | ||
699 | |||
700 | default: | ||
701 | goto fail; | ||
702 | } | ||
703 | } | ||
704 | return acl; | ||
705 | |||
706 | fail: | ||
707 | posix_acl_release(acl); | ||
708 | return ERR_PTR(-EINVAL); | ||
709 | } | ||
710 | EXPORT_SYMBOL (posix_acl_from_xattr); | ||
711 | |||
712 | /* | ||
713 | * Convert from in-memory to extended attribute representation. | ||
714 | */ | ||
715 | int | ||
716 | posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl, | ||
717 | void *buffer, size_t size) | ||
718 | { | ||
719 | posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer; | ||
720 | posix_acl_xattr_entry *ext_entry = ext_acl->a_entries; | ||
721 | int real_size, n; | ||
722 | |||
723 | real_size = posix_acl_xattr_size(acl->a_count); | ||
724 | if (!buffer) | ||
725 | return real_size; | ||
726 | if (real_size > size) | ||
727 | return -ERANGE; | ||
728 | |||
729 | ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION); | ||
730 | |||
731 | for (n=0; n < acl->a_count; n++, ext_entry++) { | ||
732 | const struct posix_acl_entry *acl_e = &acl->a_entries[n]; | ||
733 | ext_entry->e_tag = cpu_to_le16(acl_e->e_tag); | ||
734 | ext_entry->e_perm = cpu_to_le16(acl_e->e_perm); | ||
735 | switch(acl_e->e_tag) { | ||
736 | case ACL_USER: | ||
737 | ext_entry->e_id = | ||
738 | cpu_to_le32(from_kuid(user_ns, acl_e->e_uid)); | ||
739 | break; | ||
740 | case ACL_GROUP: | ||
741 | ext_entry->e_id = | ||
742 | cpu_to_le32(from_kgid(user_ns, acl_e->e_gid)); | ||
743 | break; | ||
744 | default: | ||
745 | ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID); | ||
746 | break; | ||
747 | } | ||
748 | } | ||
749 | return real_size; | ||
750 | } | ||
751 | EXPORT_SYMBOL (posix_acl_to_xattr); | ||
752 | |||
753 | static int | ||
754 | posix_acl_xattr_get(struct dentry *dentry, const char *name, | ||
755 | void *value, size_t size, int type) | ||
756 | { | ||
757 | struct posix_acl *acl; | ||
758 | int error; | ||
759 | |||
760 | if (!IS_POSIXACL(dentry->d_inode)) | ||
761 | return -EOPNOTSUPP; | ||
762 | if (S_ISLNK(dentry->d_inode->i_mode)) | ||
763 | return -EOPNOTSUPP; | ||
764 | |||
765 | acl = get_acl(dentry->d_inode, type); | ||
766 | if (IS_ERR(acl)) | ||
767 | return PTR_ERR(acl); | ||
768 | if (acl == NULL) | ||
769 | return -ENODATA; | ||
770 | |||
771 | error = posix_acl_to_xattr(&init_user_ns, acl, value, size); | ||
772 | posix_acl_release(acl); | ||
773 | |||
774 | return error; | ||
775 | } | ||
776 | |||
777 | static int | ||
778 | posix_acl_xattr_set(struct dentry *dentry, const char *name, | ||
779 | const void *value, size_t size, int flags, int type) | ||
780 | { | ||
781 | struct inode *inode = dentry->d_inode; | ||
782 | struct posix_acl *acl = NULL; | ||
783 | int ret; | ||
784 | |||
785 | if (!IS_POSIXACL(inode)) | ||
786 | return -EOPNOTSUPP; | ||
787 | if (!inode->i_op->set_acl) | ||
788 | return -EOPNOTSUPP; | ||
789 | |||
790 | if (type == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) | ||
791 | return value ? -EACCES : 0; | ||
792 | if (!inode_owner_or_capable(inode)) | ||
793 | return -EPERM; | ||
794 | |||
795 | if (value) { | ||
796 | acl = posix_acl_from_xattr(&init_user_ns, value, size); | ||
797 | if (IS_ERR(acl)) | ||
798 | return PTR_ERR(acl); | ||
799 | |||
800 | if (acl) { | ||
801 | ret = posix_acl_valid(acl); | ||
802 | if (ret) | ||
803 | goto out; | ||
804 | } | ||
805 | } | ||
806 | |||
807 | ret = inode->i_op->set_acl(inode, acl, type); | ||
808 | out: | ||
809 | posix_acl_release(acl); | ||
810 | return ret; | ||
811 | } | ||
812 | |||
813 | static size_t | ||
814 | posix_acl_xattr_list(struct dentry *dentry, char *list, size_t list_size, | ||
815 | const char *name, size_t name_len, int type) | ||
816 | { | ||
817 | const char *xname; | ||
818 | size_t size; | ||
819 | |||
820 | if (!IS_POSIXACL(dentry->d_inode)) | ||
821 | return -EOPNOTSUPP; | ||
822 | if (S_ISLNK(dentry->d_inode->i_mode)) | ||
823 | return -EOPNOTSUPP; | ||
824 | |||
825 | if (type == ACL_TYPE_ACCESS) | ||
826 | xname = POSIX_ACL_XATTR_ACCESS; | ||
827 | else | ||
828 | xname = POSIX_ACL_XATTR_DEFAULT; | ||
829 | |||
830 | size = strlen(xname) + 1; | ||
831 | if (list && size <= list_size) | ||
832 | memcpy(list, xname, size); | ||
833 | return size; | ||
834 | } | ||
835 | |||
836 | const struct xattr_handler posix_acl_access_xattr_handler = { | ||
837 | .prefix = POSIX_ACL_XATTR_ACCESS, | ||
838 | .flags = ACL_TYPE_ACCESS, | ||
839 | .list = posix_acl_xattr_list, | ||
840 | .get = posix_acl_xattr_get, | ||
841 | .set = posix_acl_xattr_set, | ||
842 | }; | ||
843 | EXPORT_SYMBOL_GPL(posix_acl_access_xattr_handler); | ||
844 | |||
845 | const struct xattr_handler posix_acl_default_xattr_handler = { | ||
846 | .prefix = POSIX_ACL_XATTR_DEFAULT, | ||
847 | .flags = ACL_TYPE_DEFAULT, | ||
848 | .list = posix_acl_xattr_list, | ||
849 | .get = posix_acl_xattr_get, | ||
850 | .set = posix_acl_xattr_set, | ||
851 | }; | ||
852 | EXPORT_SYMBOL_GPL(posix_acl_default_xattr_handler); | ||
853 | |||
854 | int simple_set_acl(struct inode *inode, struct posix_acl *acl, int type) | ||
855 | { | ||
856 | int error; | ||
857 | |||
858 | if (type == ACL_TYPE_ACCESS) { | ||
859 | error = posix_acl_equiv_mode(acl, &inode->i_mode); | ||
860 | if (error < 0) | ||
861 | return 0; | ||
862 | if (error == 0) | ||
863 | acl = NULL; | ||
864 | } | ||
865 | |||
866 | inode->i_ctime = CURRENT_TIME; | ||
867 | set_cached_acl(inode, type, acl); | ||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | int simple_acl_create(struct inode *dir, struct inode *inode) | ||
872 | { | ||
873 | struct posix_acl *default_acl, *acl; | ||
874 | int error; | ||
875 | |||
876 | error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl); | ||
877 | if (error) | ||
878 | return error; | ||
879 | |||
880 | set_cached_acl(inode, ACL_TYPE_DEFAULT, default_acl); | ||
881 | set_cached_acl(inode, ACL_TYPE_ACCESS, acl); | ||
882 | |||
883 | if (default_acl) | ||
884 | posix_acl_release(default_acl); | ||
885 | if (acl) | ||
886 | posix_acl_release(acl); | ||
887 | return 0; | ||
888 | } | ||