diff options
-rw-r--r-- | Documentation/filesystems/Locking | 2 | ||||
-rw-r--r-- | Documentation/filesystems/vfs.txt | 14 | ||||
-rw-r--r-- | fs/dcache.c | 5 | ||||
-rw-r--r-- | fs/namei.c | 226 | ||||
-rw-r--r-- | include/linux/dcache.h | 7 | ||||
-rw-r--r-- | include/linux/fs.h | 2 |
6 files changed, 198 insertions, 58 deletions
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 977d8919cc69..5f0c52a07386 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking | |||
@@ -19,6 +19,7 @@ prototypes: | |||
19 | void (*d_release)(struct dentry *); | 19 | void (*d_release)(struct dentry *); |
20 | void (*d_iput)(struct dentry *, struct inode *); | 20 | void (*d_iput)(struct dentry *, struct inode *); |
21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); | 21 | char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); |
22 | struct vfsmount *(*d_automount)(struct path *path); | ||
22 | 23 | ||
23 | locking rules: | 24 | locking rules: |
24 | rename_lock ->d_lock may block rcu-walk | 25 | rename_lock ->d_lock may block rcu-walk |
@@ -29,6 +30,7 @@ d_delete: no yes no no | |||
29 | d_release: no no yes no | 30 | d_release: no no yes no |
30 | d_iput: no no yes no | 31 | d_iput: no no yes no |
31 | d_dname: no no no no | 32 | d_dname: no no no no |
33 | d_automount: no no yes no | ||
32 | 34 | ||
33 | --------------------------- inode_operations --------------------------- | 35 | --------------------------- inode_operations --------------------------- |
34 | prototypes: | 36 | prototypes: |
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index cae6d27c9f5b..726a4f6fa3c9 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt | |||
@@ -864,6 +864,7 @@ struct dentry_operations { | |||
864 | void (*d_release)(struct dentry *); | 864 | void (*d_release)(struct dentry *); |
865 | void (*d_iput)(struct dentry *, struct inode *); | 865 | void (*d_iput)(struct dentry *, struct inode *); |
866 | char *(*d_dname)(struct dentry *, char *, int); | 866 | char *(*d_dname)(struct dentry *, char *, int); |
867 | struct vfsmount *(*d_automount)(struct path *); | ||
867 | }; | 868 | }; |
868 | 869 | ||
869 | d_revalidate: called when the VFS needs to revalidate a dentry. This | 870 | d_revalidate: called when the VFS needs to revalidate a dentry. This |
@@ -930,6 +931,19 @@ struct dentry_operations { | |||
930 | at the end of the buffer, and returns a pointer to the first char. | 931 | at the end of the buffer, and returns a pointer to the first char. |
931 | dynamic_dname() helper function is provided to take care of this. | 932 | dynamic_dname() helper function is provided to take care of this. |
932 | 933 | ||
934 | d_automount: called when an automount dentry is to be traversed (optional). | ||
935 | This should create a new VFS mount record, mount it on the directory | ||
936 | and return the record to the caller. The caller is supplied with a | ||
937 | path parameter giving the automount directory to describe the automount | ||
938 | target and the parent VFS mount record to provide inheritable mount | ||
939 | parameters. NULL should be returned if someone else managed to make | ||
940 | the automount first. If the automount failed, then an error code | ||
941 | should be returned. | ||
942 | |||
943 | This function is only used if DCACHE_NEED_AUTOMOUNT is set on the | ||
944 | dentry. This is set by __d_instantiate() if S_AUTOMOUNT is set on the | ||
945 | inode being added. | ||
946 | |||
933 | Example : | 947 | Example : |
934 | 948 | ||
935 | static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) | 949 | static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) |
diff --git a/fs/dcache.c b/fs/dcache.c index 0c6d5c549d84..51f7bb6463af 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1380,8 +1380,11 @@ EXPORT_SYMBOL(d_set_d_op); | |||
1380 | static void __d_instantiate(struct dentry *dentry, struct inode *inode) | 1380 | static void __d_instantiate(struct dentry *dentry, struct inode *inode) |
1381 | { | 1381 | { |
1382 | spin_lock(&dentry->d_lock); | 1382 | spin_lock(&dentry->d_lock); |
1383 | if (inode) | 1383 | if (inode) { |
1384 | if (unlikely(IS_AUTOMOUNT(inode))) | ||
1385 | dentry->d_flags |= DCACHE_NEED_AUTOMOUNT; | ||
1384 | list_add(&dentry->d_alias, &inode->i_dentry); | 1386 | list_add(&dentry->d_alias, &inode->i_dentry); |
1387 | } | ||
1385 | dentry->d_inode = inode; | 1388 | dentry->d_inode = inode; |
1386 | dentry_rcuwalk_barrier(dentry); | 1389 | dentry_rcuwalk_barrier(dentry); |
1387 | spin_unlock(&dentry->d_lock); | 1390 | spin_unlock(&dentry->d_lock); |
diff --git a/fs/namei.c b/fs/namei.c index 529e917ad2fc..16109da68bbf 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -896,51 +896,120 @@ int follow_up(struct path *path) | |||
896 | } | 896 | } |
897 | 897 | ||
898 | /* | 898 | /* |
899 | * serialization is taken care of in namespace.c | 899 | * Perform an automount |
900 | * - return -EISDIR to tell follow_managed() to stop and return the path we | ||
901 | * were called with. | ||
900 | */ | 902 | */ |
901 | static void __follow_mount_rcu(struct nameidata *nd, struct path *path, | 903 | static int follow_automount(struct path *path, unsigned flags, |
902 | struct inode **inode) | 904 | bool *need_mntput) |
903 | { | 905 | { |
904 | while (d_mountpoint(path->dentry)) { | 906 | struct vfsmount *mnt; |
905 | struct vfsmount *mounted; | 907 | |
906 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); | 908 | if (!path->dentry->d_op || !path->dentry->d_op->d_automount) |
907 | if (!mounted) | 909 | return -EREMOTE; |
908 | return; | 910 | |
909 | path->mnt = mounted; | 911 | /* We want to mount if someone is trying to open/create a file of any |
910 | path->dentry = mounted->mnt_root; | 912 | * type under the mountpoint, wants to traverse through the mountpoint |
911 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); | 913 | * or wants to open the mounted directory. |
912 | *inode = path->dentry->d_inode; | 914 | * |
915 | * We don't want to mount if someone's just doing a stat and they've | ||
916 | * set AT_SYMLINK_NOFOLLOW - unless they're stat'ing a directory and | ||
917 | * appended a '/' to the name. | ||
918 | */ | ||
919 | if (!(flags & LOOKUP_FOLLOW) && | ||
920 | !(flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY | | ||
921 | LOOKUP_OPEN | LOOKUP_CREATE))) | ||
922 | return -EISDIR; | ||
923 | |||
924 | current->total_link_count++; | ||
925 | if (current->total_link_count >= 40) | ||
926 | return -ELOOP; | ||
927 | |||
928 | mnt = path->dentry->d_op->d_automount(path); | ||
929 | if (IS_ERR(mnt)) { | ||
930 | /* | ||
931 | * The filesystem is allowed to return -EISDIR here to indicate | ||
932 | * it doesn't want to automount. For instance, autofs would do | ||
933 | * this so that its userspace daemon can mount on this dentry. | ||
934 | * | ||
935 | * However, we can only permit this if it's a terminal point in | ||
936 | * the path being looked up; if it wasn't then the remainder of | ||
937 | * the path is inaccessible and we should say so. | ||
938 | */ | ||
939 | if (PTR_ERR(mnt) == -EISDIR && (flags & LOOKUP_CONTINUE)) | ||
940 | return -EREMOTE; | ||
941 | return PTR_ERR(mnt); | ||
913 | } | 942 | } |
914 | } | 943 | if (!mnt) /* mount collision */ |
944 | return 0; | ||
915 | 945 | ||
916 | static int __follow_mount(struct path *path) | 946 | if (mnt->mnt_sb == path->mnt->mnt_sb && |
917 | { | 947 | mnt->mnt_root == path->dentry) { |
918 | int res = 0; | 948 | mntput(mnt); |
919 | while (d_mountpoint(path->dentry)) { | 949 | return -ELOOP; |
920 | struct vfsmount *mounted = lookup_mnt(path); | ||
921 | if (!mounted) | ||
922 | break; | ||
923 | dput(path->dentry); | ||
924 | if (res) | ||
925 | mntput(path->mnt); | ||
926 | path->mnt = mounted; | ||
927 | path->dentry = dget(mounted->mnt_root); | ||
928 | res = 1; | ||
929 | } | 950 | } |
930 | return res; | 951 | |
952 | dput(path->dentry); | ||
953 | if (*need_mntput) | ||
954 | mntput(path->mnt); | ||
955 | path->mnt = mnt; | ||
956 | path->dentry = dget(mnt->mnt_root); | ||
957 | *need_mntput = true; | ||
958 | return 0; | ||
931 | } | 959 | } |
932 | 960 | ||
933 | static void follow_mount(struct path *path) | 961 | /* |
962 | * Handle a dentry that is managed in some way. | ||
963 | * - Flagged as mountpoint | ||
964 | * - Flagged as automount point | ||
965 | * | ||
966 | * This may only be called in refwalk mode. | ||
967 | * | ||
968 | * Serialization is taken care of in namespace.c | ||
969 | */ | ||
970 | static int follow_managed(struct path *path, unsigned flags) | ||
934 | { | 971 | { |
935 | while (d_mountpoint(path->dentry)) { | 972 | unsigned managed; |
936 | struct vfsmount *mounted = lookup_mnt(path); | 973 | bool need_mntput = false; |
937 | if (!mounted) | 974 | int ret; |
938 | break; | 975 | |
939 | dput(path->dentry); | 976 | /* Given that we're not holding a lock here, we retain the value in a |
940 | mntput(path->mnt); | 977 | * local variable for each dentry as we look at it so that we don't see |
941 | path->mnt = mounted; | 978 | * the components of that value change under us */ |
942 | path->dentry = dget(mounted->mnt_root); | 979 | while (managed = ACCESS_ONCE(path->dentry->d_flags), |
980 | managed &= DCACHE_MANAGED_DENTRY, | ||
981 | unlikely(managed != 0)) { | ||
982 | /* Transit to a mounted filesystem. */ | ||
983 | if (managed & DCACHE_MOUNTED) { | ||
984 | struct vfsmount *mounted = lookup_mnt(path); | ||
985 | if (mounted) { | ||
986 | dput(path->dentry); | ||
987 | if (need_mntput) | ||
988 | mntput(path->mnt); | ||
989 | path->mnt = mounted; | ||
990 | path->dentry = dget(mounted->mnt_root); | ||
991 | need_mntput = true; | ||
992 | continue; | ||
993 | } | ||
994 | |||
995 | /* Something is mounted on this dentry in another | ||
996 | * namespace and/or whatever was mounted there in this | ||
997 | * namespace got unmounted before we managed to get the | ||
998 | * vfsmount_lock */ | ||
999 | } | ||
1000 | |||
1001 | /* Handle an automount point */ | ||
1002 | if (managed & DCACHE_NEED_AUTOMOUNT) { | ||
1003 | ret = follow_automount(path, flags, &need_mntput); | ||
1004 | if (ret < 0) | ||
1005 | return ret == -EISDIR ? 0 : ret; | ||
1006 | continue; | ||
1007 | } | ||
1008 | |||
1009 | /* We didn't change the current path point */ | ||
1010 | break; | ||
943 | } | 1011 | } |
1012 | return 0; | ||
944 | } | 1013 | } |
945 | 1014 | ||
946 | int follow_down(struct path *path) | 1015 | int follow_down(struct path *path) |
@@ -958,13 +1027,37 @@ int follow_down(struct path *path) | |||
958 | return 0; | 1027 | return 0; |
959 | } | 1028 | } |
960 | 1029 | ||
1030 | /* | ||
1031 | * Skip to top of mountpoint pile in rcuwalk mode. We abort the rcu-walk if we | ||
1032 | * meet an automount point and we're not walking to "..". True is returned to | ||
1033 | * continue, false to abort. | ||
1034 | */ | ||
1035 | static bool __follow_mount_rcu(struct nameidata *nd, struct path *path, | ||
1036 | struct inode **inode, bool reverse_transit) | ||
1037 | { | ||
1038 | while (d_mountpoint(path->dentry)) { | ||
1039 | struct vfsmount *mounted; | ||
1040 | mounted = __lookup_mnt(path->mnt, path->dentry, 1); | ||
1041 | if (!mounted) | ||
1042 | break; | ||
1043 | path->mnt = mounted; | ||
1044 | path->dentry = mounted->mnt_root; | ||
1045 | nd->seq = read_seqcount_begin(&path->dentry->d_seq); | ||
1046 | *inode = path->dentry->d_inode; | ||
1047 | } | ||
1048 | |||
1049 | if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) | ||
1050 | return reverse_transit; | ||
1051 | return true; | ||
1052 | } | ||
1053 | |||
961 | static int follow_dotdot_rcu(struct nameidata *nd) | 1054 | static int follow_dotdot_rcu(struct nameidata *nd) |
962 | { | 1055 | { |
963 | struct inode *inode = nd->inode; | 1056 | struct inode *inode = nd->inode; |
964 | 1057 | ||
965 | set_root_rcu(nd); | 1058 | set_root_rcu(nd); |
966 | 1059 | ||
967 | while(1) { | 1060 | while (1) { |
968 | if (nd->path.dentry == nd->root.dentry && | 1061 | if (nd->path.dentry == nd->root.dentry && |
969 | nd->path.mnt == nd->root.mnt) { | 1062 | nd->path.mnt == nd->root.mnt) { |
970 | break; | 1063 | break; |
@@ -987,12 +1080,28 @@ static int follow_dotdot_rcu(struct nameidata *nd) | |||
987 | nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); | 1080 | nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq); |
988 | inode = nd->path.dentry->d_inode; | 1081 | inode = nd->path.dentry->d_inode; |
989 | } | 1082 | } |
990 | __follow_mount_rcu(nd, &nd->path, &inode); | 1083 | __follow_mount_rcu(nd, &nd->path, &inode, true); |
991 | nd->inode = inode; | 1084 | nd->inode = inode; |
992 | 1085 | ||
993 | return 0; | 1086 | return 0; |
994 | } | 1087 | } |
995 | 1088 | ||
1089 | /* | ||
1090 | * Skip to top of mountpoint pile in refwalk mode for follow_dotdot() | ||
1091 | */ | ||
1092 | static void follow_mount(struct path *path) | ||
1093 | { | ||
1094 | while (d_mountpoint(path->dentry)) { | ||
1095 | struct vfsmount *mounted = lookup_mnt(path); | ||
1096 | if (!mounted) | ||
1097 | break; | ||
1098 | dput(path->dentry); | ||
1099 | mntput(path->mnt); | ||
1100 | path->mnt = mounted; | ||
1101 | path->dentry = dget(mounted->mnt_root); | ||
1102 | } | ||
1103 | } | ||
1104 | |||
996 | static void follow_dotdot(struct nameidata *nd) | 1105 | static void follow_dotdot(struct nameidata *nd) |
997 | { | 1106 | { |
998 | set_root(nd); | 1107 | set_root(nd); |
@@ -1057,12 +1166,14 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, | |||
1057 | struct vfsmount *mnt = nd->path.mnt; | 1166 | struct vfsmount *mnt = nd->path.mnt; |
1058 | struct dentry *dentry, *parent = nd->path.dentry; | 1167 | struct dentry *dentry, *parent = nd->path.dentry; |
1059 | struct inode *dir; | 1168 | struct inode *dir; |
1169 | int err; | ||
1170 | |||
1060 | /* | 1171 | /* |
1061 | * See if the low-level filesystem might want | 1172 | * See if the low-level filesystem might want |
1062 | * to use its own hash.. | 1173 | * to use its own hash.. |
1063 | */ | 1174 | */ |
1064 | if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { | 1175 | if (unlikely(parent->d_flags & DCACHE_OP_HASH)) { |
1065 | int err = parent->d_op->d_hash(parent, nd->inode, name); | 1176 | err = parent->d_op->d_hash(parent, nd->inode, name); |
1066 | if (err < 0) | 1177 | if (err < 0) |
1067 | return err; | 1178 | return err; |
1068 | } | 1179 | } |
@@ -1092,20 +1203,25 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, | |||
1092 | done2: | 1203 | done2: |
1093 | path->mnt = mnt; | 1204 | path->mnt = mnt; |
1094 | path->dentry = dentry; | 1205 | path->dentry = dentry; |
1095 | __follow_mount_rcu(nd, path, inode); | 1206 | if (likely(__follow_mount_rcu(nd, path, inode, false))) |
1096 | } else { | 1207 | return 0; |
1097 | dentry = __d_lookup(parent, name); | 1208 | if (nameidata_drop_rcu(nd)) |
1098 | if (!dentry) | 1209 | return -ECHILD; |
1099 | goto need_lookup; | 1210 | /* fallthru */ |
1211 | } | ||
1212 | dentry = __d_lookup(parent, name); | ||
1213 | if (!dentry) | ||
1214 | goto need_lookup; | ||
1100 | found: | 1215 | found: |
1101 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) | 1216 | if (dentry->d_flags & DCACHE_OP_REVALIDATE) |
1102 | goto need_revalidate; | 1217 | goto need_revalidate; |
1103 | done: | 1218 | done: |
1104 | path->mnt = mnt; | 1219 | path->mnt = mnt; |
1105 | path->dentry = dentry; | 1220 | path->dentry = dentry; |
1106 | __follow_mount(path); | 1221 | err = follow_managed(path, nd->flags); |
1107 | *inode = path->dentry->d_inode; | 1222 | if (unlikely(err < 0)) |
1108 | } | 1223 | return err; |
1224 | *inode = path->dentry->d_inode; | ||
1109 | return 0; | 1225 | return 0; |
1110 | 1226 | ||
1111 | need_lookup: | 1227 | need_lookup: |
@@ -2203,11 +2319,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2203 | if (open_flag & O_EXCL) | 2319 | if (open_flag & O_EXCL) |
2204 | goto exit_dput; | 2320 | goto exit_dput; |
2205 | 2321 | ||
2206 | if (__follow_mount(path)) { | 2322 | error = follow_managed(path, nd->flags); |
2207 | error = -ELOOP; | 2323 | if (error < 0) |
2208 | if (open_flag & O_NOFOLLOW) | 2324 | goto exit_dput; |
2209 | goto exit_dput; | ||
2210 | } | ||
2211 | 2325 | ||
2212 | error = -ENOENT; | 2326 | error = -ENOENT; |
2213 | if (!path->dentry->d_inode) | 2327 | if (!path->dentry->d_inode) |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 59fcd24b1468..ee6c26d142c3 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -167,6 +167,7 @@ struct dentry_operations { | |||
167 | void (*d_release)(struct dentry *); | 167 | void (*d_release)(struct dentry *); |
168 | void (*d_iput)(struct dentry *, struct inode *); | 168 | void (*d_iput)(struct dentry *, struct inode *); |
169 | char *(*d_dname)(struct dentry *, char *, int); | 169 | char *(*d_dname)(struct dentry *, char *, int); |
170 | struct vfsmount *(*d_automount)(struct path *); | ||
170 | } ____cacheline_aligned; | 171 | } ____cacheline_aligned; |
171 | 172 | ||
172 | /* | 173 | /* |
@@ -205,13 +206,17 @@ struct dentry_operations { | |||
205 | 206 | ||
206 | #define DCACHE_CANT_MOUNT 0x0100 | 207 | #define DCACHE_CANT_MOUNT 0x0100 |
207 | #define DCACHE_GENOCIDE 0x0200 | 208 | #define DCACHE_GENOCIDE 0x0200 |
208 | #define DCACHE_MOUNTED 0x0400 /* is a mountpoint */ | ||
209 | 209 | ||
210 | #define DCACHE_OP_HASH 0x1000 | 210 | #define DCACHE_OP_HASH 0x1000 |
211 | #define DCACHE_OP_COMPARE 0x2000 | 211 | #define DCACHE_OP_COMPARE 0x2000 |
212 | #define DCACHE_OP_REVALIDATE 0x4000 | 212 | #define DCACHE_OP_REVALIDATE 0x4000 |
213 | #define DCACHE_OP_DELETE 0x8000 | 213 | #define DCACHE_OP_DELETE 0x8000 |
214 | 214 | ||
215 | #define DCACHE_MOUNTED 0x10000 /* is a mountpoint */ | ||
216 | #define DCACHE_NEED_AUTOMOUNT 0x20000 /* handle automount on this dir */ | ||
217 | #define DCACHE_MANAGED_DENTRY \ | ||
218 | (DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT) | ||
219 | |||
215 | extern seqlock_t rename_lock; | 220 | extern seqlock_t rename_lock; |
216 | 221 | ||
217 | static inline int dname_external(struct dentry *dentry) | 222 | static inline int dname_external(struct dentry *dentry) |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 3984f2358d1f..4c00ed53be8f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -242,6 +242,7 @@ struct inodes_stat_t { | |||
242 | #define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */ | 242 | #define S_SWAPFILE 256 /* Do not truncate: swapon got its bmaps */ |
243 | #define S_PRIVATE 512 /* Inode is fs-internal */ | 243 | #define S_PRIVATE 512 /* Inode is fs-internal */ |
244 | #define S_IMA 1024 /* Inode has an associated IMA struct */ | 244 | #define S_IMA 1024 /* Inode has an associated IMA struct */ |
245 | #define S_AUTOMOUNT 2048 /* Automount/referral quasi-directory */ | ||
245 | 246 | ||
246 | /* | 247 | /* |
247 | * Note that nosuid etc flags are inode-specific: setting some file-system | 248 | * Note that nosuid etc flags are inode-specific: setting some file-system |
@@ -277,6 +278,7 @@ struct inodes_stat_t { | |||
277 | #define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE) | 278 | #define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE) |
278 | #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) | 279 | #define IS_PRIVATE(inode) ((inode)->i_flags & S_PRIVATE) |
279 | #define IS_IMA(inode) ((inode)->i_flags & S_IMA) | 280 | #define IS_IMA(inode) ((inode)->i_flags & S_IMA) |
281 | #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) | ||
280 | 282 | ||
281 | /* the read-only stuff doesn't really belong here, but any other place is | 283 | /* the read-only stuff doesn't really belong here, but any other place is |
282 | probably as bad and I don't want to create yet another include file. */ | 284 | probably as bad and I don't want to create yet another include file. */ |