diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 135 |
1 files changed, 81 insertions, 54 deletions
diff --git a/fs/namei.c b/fs/namei.c index 967c3db92724..527119afb6a5 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -552,6 +552,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd | |||
552 | return result; | 552 | return result; |
553 | } | 553 | } |
554 | 554 | ||
555 | static __always_inline void set_root(struct nameidata *nd) | ||
556 | { | ||
557 | if (!nd->root.mnt) { | ||
558 | struct fs_struct *fs = current->fs; | ||
559 | read_lock(&fs->lock); | ||
560 | nd->root = fs->root; | ||
561 | path_get(&nd->root); | ||
562 | read_unlock(&fs->lock); | ||
563 | } | ||
564 | } | ||
565 | |||
555 | static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) | 566 | static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) |
556 | { | 567 | { |
557 | int res = 0; | 568 | int res = 0; |
@@ -560,14 +571,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l | |||
560 | goto fail; | 571 | goto fail; |
561 | 572 | ||
562 | if (*link == '/') { | 573 | if (*link == '/') { |
563 | struct fs_struct *fs = current->fs; | 574 | set_root(nd); |
564 | |||
565 | path_put(&nd->path); | 575 | path_put(&nd->path); |
566 | 576 | nd->path = nd->root; | |
567 | read_lock(&fs->lock); | 577 | path_get(&nd->root); |
568 | nd->path = fs->root; | ||
569 | path_get(&fs->root); | ||
570 | read_unlock(&fs->lock); | ||
571 | } | 578 | } |
572 | 579 | ||
573 | res = link_path_walk(link, nd); | 580 | res = link_path_walk(link, nd); |
@@ -668,23 +675,23 @@ loop: | |||
668 | return err; | 675 | return err; |
669 | } | 676 | } |
670 | 677 | ||
671 | int follow_up(struct vfsmount **mnt, struct dentry **dentry) | 678 | int follow_up(struct path *path) |
672 | { | 679 | { |
673 | struct vfsmount *parent; | 680 | struct vfsmount *parent; |
674 | struct dentry *mountpoint; | 681 | struct dentry *mountpoint; |
675 | spin_lock(&vfsmount_lock); | 682 | spin_lock(&vfsmount_lock); |
676 | parent=(*mnt)->mnt_parent; | 683 | parent = path->mnt->mnt_parent; |
677 | if (parent == *mnt) { | 684 | if (parent == path->mnt) { |
678 | spin_unlock(&vfsmount_lock); | 685 | spin_unlock(&vfsmount_lock); |
679 | return 0; | 686 | return 0; |
680 | } | 687 | } |
681 | mntget(parent); | 688 | mntget(parent); |
682 | mountpoint=dget((*mnt)->mnt_mountpoint); | 689 | mountpoint = dget(path->mnt->mnt_mountpoint); |
683 | spin_unlock(&vfsmount_lock); | 690 | spin_unlock(&vfsmount_lock); |
684 | dput(*dentry); | 691 | dput(path->dentry); |
685 | *dentry = mountpoint; | 692 | path->dentry = mountpoint; |
686 | mntput(*mnt); | 693 | mntput(path->mnt); |
687 | *mnt = parent; | 694 | path->mnt = parent; |
688 | return 1; | 695 | return 1; |
689 | } | 696 | } |
690 | 697 | ||
@@ -695,7 +702,7 @@ static int __follow_mount(struct path *path) | |||
695 | { | 702 | { |
696 | int res = 0; | 703 | int res = 0; |
697 | while (d_mountpoint(path->dentry)) { | 704 | while (d_mountpoint(path->dentry)) { |
698 | struct vfsmount *mounted = lookup_mnt(path->mnt, path->dentry); | 705 | struct vfsmount *mounted = lookup_mnt(path); |
699 | if (!mounted) | 706 | if (!mounted) |
700 | break; | 707 | break; |
701 | dput(path->dentry); | 708 | dput(path->dentry); |
@@ -708,32 +715,32 @@ static int __follow_mount(struct path *path) | |||
708 | return res; | 715 | return res; |
709 | } | 716 | } |
710 | 717 | ||
711 | static void follow_mount(struct vfsmount **mnt, struct dentry **dentry) | 718 | static void follow_mount(struct path *path) |
712 | { | 719 | { |
713 | while (d_mountpoint(*dentry)) { | 720 | while (d_mountpoint(path->dentry)) { |
714 | struct vfsmount *mounted = lookup_mnt(*mnt, *dentry); | 721 | struct vfsmount *mounted = lookup_mnt(path); |
715 | if (!mounted) | 722 | if (!mounted) |
716 | break; | 723 | break; |
717 | dput(*dentry); | 724 | dput(path->dentry); |
718 | mntput(*mnt); | 725 | mntput(path->mnt); |
719 | *mnt = mounted; | 726 | path->mnt = mounted; |
720 | *dentry = dget(mounted->mnt_root); | 727 | path->dentry = dget(mounted->mnt_root); |
721 | } | 728 | } |
722 | } | 729 | } |
723 | 730 | ||
724 | /* no need for dcache_lock, as serialization is taken care in | 731 | /* no need for dcache_lock, as serialization is taken care in |
725 | * namespace.c | 732 | * namespace.c |
726 | */ | 733 | */ |
727 | int follow_down(struct vfsmount **mnt, struct dentry **dentry) | 734 | int follow_down(struct path *path) |
728 | { | 735 | { |
729 | struct vfsmount *mounted; | 736 | struct vfsmount *mounted; |
730 | 737 | ||
731 | mounted = lookup_mnt(*mnt, *dentry); | 738 | mounted = lookup_mnt(path); |
732 | if (mounted) { | 739 | if (mounted) { |
733 | dput(*dentry); | 740 | dput(path->dentry); |
734 | mntput(*mnt); | 741 | mntput(path->mnt); |
735 | *mnt = mounted; | 742 | path->mnt = mounted; |
736 | *dentry = dget(mounted->mnt_root); | 743 | path->dentry = dget(mounted->mnt_root); |
737 | return 1; | 744 | return 1; |
738 | } | 745 | } |
739 | return 0; | 746 | return 0; |
@@ -741,19 +748,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) | |||
741 | 748 | ||
742 | static __always_inline void follow_dotdot(struct nameidata *nd) | 749 | static __always_inline void follow_dotdot(struct nameidata *nd) |
743 | { | 750 | { |
744 | struct fs_struct *fs = current->fs; | 751 | set_root(nd); |
745 | 752 | ||
746 | while(1) { | 753 | while(1) { |
747 | struct vfsmount *parent; | 754 | struct vfsmount *parent; |
748 | struct dentry *old = nd->path.dentry; | 755 | struct dentry *old = nd->path.dentry; |
749 | 756 | ||
750 | read_lock(&fs->lock); | 757 | if (nd->path.dentry == nd->root.dentry && |
751 | if (nd->path.dentry == fs->root.dentry && | 758 | nd->path.mnt == nd->root.mnt) { |
752 | nd->path.mnt == fs->root.mnt) { | ||
753 | read_unlock(&fs->lock); | ||
754 | break; | 759 | break; |
755 | } | 760 | } |
756 | read_unlock(&fs->lock); | ||
757 | spin_lock(&dcache_lock); | 761 | spin_lock(&dcache_lock); |
758 | if (nd->path.dentry != nd->path.mnt->mnt_root) { | 762 | if (nd->path.dentry != nd->path.mnt->mnt_root) { |
759 | nd->path.dentry = dget(nd->path.dentry->d_parent); | 763 | nd->path.dentry = dget(nd->path.dentry->d_parent); |
@@ -775,7 +779,7 @@ static __always_inline void follow_dotdot(struct nameidata *nd) | |||
775 | mntput(nd->path.mnt); | 779 | mntput(nd->path.mnt); |
776 | nd->path.mnt = parent; | 780 | nd->path.mnt = parent; |
777 | } | 781 | } |
778 | follow_mount(&nd->path.mnt, &nd->path.dentry); | 782 | follow_mount(&nd->path); |
779 | } | 783 | } |
780 | 784 | ||
781 | /* | 785 | /* |
@@ -853,7 +857,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd) | |||
853 | err = inode_permission(nd->path.dentry->d_inode, | 857 | err = inode_permission(nd->path.dentry->d_inode, |
854 | MAY_EXEC); | 858 | MAY_EXEC); |
855 | if (!err) | 859 | if (!err) |
856 | err = ima_path_check(&nd->path, MAY_EXEC); | 860 | err = ima_path_check(&nd->path, MAY_EXEC, |
861 | IMA_COUNT_UPDATE); | ||
857 | if (err) | 862 | if (err) |
858 | break; | 863 | break; |
859 | 864 | ||
@@ -1016,25 +1021,23 @@ static int path_walk(const char *name, struct nameidata *nd) | |||
1016 | return link_path_walk(name, nd); | 1021 | return link_path_walk(name, nd); |
1017 | } | 1022 | } |
1018 | 1023 | ||
1019 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ | 1024 | static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd) |
1020 | static int do_path_lookup(int dfd, const char *name, | ||
1021 | unsigned int flags, struct nameidata *nd) | ||
1022 | { | 1025 | { |
1023 | int retval = 0; | 1026 | int retval = 0; |
1024 | int fput_needed; | 1027 | int fput_needed; |
1025 | struct file *file; | 1028 | struct file *file; |
1026 | struct fs_struct *fs = current->fs; | ||
1027 | 1029 | ||
1028 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ | 1030 | nd->last_type = LAST_ROOT; /* if there are only slashes... */ |
1029 | nd->flags = flags; | 1031 | nd->flags = flags; |
1030 | nd->depth = 0; | 1032 | nd->depth = 0; |
1033 | nd->root.mnt = NULL; | ||
1031 | 1034 | ||
1032 | if (*name=='/') { | 1035 | if (*name=='/') { |
1033 | read_lock(&fs->lock); | 1036 | set_root(nd); |
1034 | nd->path = fs->root; | 1037 | nd->path = nd->root; |
1035 | path_get(&fs->root); | 1038 | path_get(&nd->root); |
1036 | read_unlock(&fs->lock); | ||
1037 | } else if (dfd == AT_FDCWD) { | 1039 | } else if (dfd == AT_FDCWD) { |
1040 | struct fs_struct *fs = current->fs; | ||
1038 | read_lock(&fs->lock); | 1041 | read_lock(&fs->lock); |
1039 | nd->path = fs->pwd; | 1042 | nd->path = fs->pwd; |
1040 | path_get(&fs->pwd); | 1043 | path_get(&fs->pwd); |
@@ -1062,17 +1065,29 @@ static int do_path_lookup(int dfd, const char *name, | |||
1062 | 1065 | ||
1063 | fput_light(file, fput_needed); | 1066 | fput_light(file, fput_needed); |
1064 | } | 1067 | } |
1068 | return 0; | ||
1065 | 1069 | ||
1066 | retval = path_walk(name, nd); | 1070 | fput_fail: |
1071 | fput_light(file, fput_needed); | ||
1072 | out_fail: | ||
1073 | return retval; | ||
1074 | } | ||
1075 | |||
1076 | /* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ | ||
1077 | static int do_path_lookup(int dfd, const char *name, | ||
1078 | unsigned int flags, struct nameidata *nd) | ||
1079 | { | ||
1080 | int retval = path_init(dfd, name, flags, nd); | ||
1081 | if (!retval) | ||
1082 | retval = path_walk(name, nd); | ||
1067 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && | 1083 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && |
1068 | nd->path.dentry->d_inode)) | 1084 | nd->path.dentry->d_inode)) |
1069 | audit_inode(name, nd->path.dentry); | 1085 | audit_inode(name, nd->path.dentry); |
1070 | out_fail: | 1086 | if (nd->root.mnt) { |
1087 | path_put(&nd->root); | ||
1088 | nd->root.mnt = NULL; | ||
1089 | } | ||
1071 | return retval; | 1090 | return retval; |
1072 | |||
1073 | fput_fail: | ||
1074 | fput_light(file, fput_needed); | ||
1075 | goto out_fail; | ||
1076 | } | 1091 | } |
1077 | 1092 | ||
1078 | int path_lookup(const char *name, unsigned int flags, | 1093 | int path_lookup(const char *name, unsigned int flags, |
@@ -1112,14 +1127,18 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, | |||
1112 | nd->path.dentry = dentry; | 1127 | nd->path.dentry = dentry; |
1113 | nd->path.mnt = mnt; | 1128 | nd->path.mnt = mnt; |
1114 | path_get(&nd->path); | 1129 | path_get(&nd->path); |
1130 | nd->root = nd->path; | ||
1131 | path_get(&nd->root); | ||
1115 | 1132 | ||
1116 | retval = path_walk(name, nd); | 1133 | retval = path_walk(name, nd); |
1117 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && | 1134 | if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && |
1118 | nd->path.dentry->d_inode)) | 1135 | nd->path.dentry->d_inode)) |
1119 | audit_inode(name, nd->path.dentry); | 1136 | audit_inode(name, nd->path.dentry); |
1120 | 1137 | ||
1121 | return retval; | 1138 | path_put(&nd->root); |
1139 | nd->root.mnt = NULL; | ||
1122 | 1140 | ||
1141 | return retval; | ||
1123 | } | 1142 | } |
1124 | 1143 | ||
1125 | /** | 1144 | /** |
@@ -1515,7 +1534,8 @@ int may_open(struct path *path, int acc_mode, int flag) | |||
1515 | return error; | 1534 | return error; |
1516 | 1535 | ||
1517 | error = ima_path_check(path, | 1536 | error = ima_path_check(path, |
1518 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC)); | 1537 | acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC), |
1538 | IMA_COUNT_UPDATE); | ||
1519 | if (error) | 1539 | if (error) |
1520 | return error; | 1540 | return error; |
1521 | /* | 1541 | /* |
@@ -1674,9 +1694,14 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
1674 | /* | 1694 | /* |
1675 | * Create - we need to know the parent. | 1695 | * Create - we need to know the parent. |
1676 | */ | 1696 | */ |
1677 | error = do_path_lookup(dfd, pathname, LOOKUP_PARENT, &nd); | 1697 | error = path_init(dfd, pathname, LOOKUP_PARENT, &nd); |
1678 | if (error) | 1698 | if (error) |
1679 | return ERR_PTR(error); | 1699 | return ERR_PTR(error); |
1700 | error = path_walk(pathname, &nd); | ||
1701 | if (error) | ||
1702 | return ERR_PTR(error); | ||
1703 | if (unlikely(!audit_dummy_context())) | ||
1704 | audit_inode(pathname, nd.path.dentry); | ||
1680 | 1705 | ||
1681 | /* | 1706 | /* |
1682 | * We have the parent and last component. First of all, check | 1707 | * We have the parent and last component. First of all, check |
@@ -1804,6 +1829,8 @@ exit: | |||
1804 | if (!IS_ERR(nd.intent.open.file)) | 1829 | if (!IS_ERR(nd.intent.open.file)) |
1805 | release_open_intent(&nd); | 1830 | release_open_intent(&nd); |
1806 | exit_parent: | 1831 | exit_parent: |
1832 | if (nd.root.mnt) | ||
1833 | path_put(&nd.root); | ||
1807 | path_put(&nd.path); | 1834 | path_put(&nd.path); |
1808 | return ERR_PTR(error); | 1835 | return ERR_PTR(error); |
1809 | 1836 | ||