diff options
-rw-r--r-- | fs/namei.c | 71 | ||||
-rw-r--r-- | fs/nfsd/nfs3xdr.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 8 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 23 | ||||
-rw-r--r-- | include/linux/namei.h | 1 |
5 files changed, 86 insertions, 19 deletions
diff --git a/fs/namei.c b/fs/namei.c index 45c702edce3c..1067f7a0287a 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2272,6 +2272,8 @@ EXPORT_SYMBOL(vfs_path_lookup); | |||
2272 | * | 2272 | * |
2273 | * Note that this routine is purely a helper for filesystem usage and should | 2273 | * Note that this routine is purely a helper for filesystem usage and should |
2274 | * not be called by generic code. | 2274 | * not be called by generic code. |
2275 | * | ||
2276 | * The caller must hold base->i_mutex. | ||
2275 | */ | 2277 | */ |
2276 | struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) | 2278 | struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) |
2277 | { | 2279 | { |
@@ -2315,6 +2317,75 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) | |||
2315 | } | 2317 | } |
2316 | EXPORT_SYMBOL(lookup_one_len); | 2318 | EXPORT_SYMBOL(lookup_one_len); |
2317 | 2319 | ||
2320 | /** | ||
2321 | * lookup_one_len_unlocked - filesystem helper to lookup single pathname component | ||
2322 | * @name: pathname component to lookup | ||
2323 | * @base: base directory to lookup from | ||
2324 | * @len: maximum length @len should be interpreted to | ||
2325 | * | ||
2326 | * Note that this routine is purely a helper for filesystem usage and should | ||
2327 | * not be called by generic code. | ||
2328 | * | ||
2329 | * Unlike lookup_one_len, it should be called without the parent | ||
2330 | * i_mutex held, and will take the i_mutex itself if necessary. | ||
2331 | */ | ||
2332 | struct dentry *lookup_one_len_unlocked(const char *name, | ||
2333 | struct dentry *base, int len) | ||
2334 | { | ||
2335 | struct qstr this; | ||
2336 | unsigned int c; | ||
2337 | int err; | ||
2338 | struct dentry *ret; | ||
2339 | |||
2340 | this.name = name; | ||
2341 | this.len = len; | ||
2342 | this.hash = full_name_hash(name, len); | ||
2343 | if (!len) | ||
2344 | return ERR_PTR(-EACCES); | ||
2345 | |||
2346 | if (unlikely(name[0] == '.')) { | ||
2347 | if (len < 2 || (len == 2 && name[1] == '.')) | ||
2348 | return ERR_PTR(-EACCES); | ||
2349 | } | ||
2350 | |||
2351 | while (len--) { | ||
2352 | c = *(const unsigned char *)name++; | ||
2353 | if (c == '/' || c == '\0') | ||
2354 | return ERR_PTR(-EACCES); | ||
2355 | } | ||
2356 | /* | ||
2357 | * See if the low-level filesystem might want | ||
2358 | * to use its own hash.. | ||
2359 | */ | ||
2360 | if (base->d_flags & DCACHE_OP_HASH) { | ||
2361 | int err = base->d_op->d_hash(base, &this); | ||
2362 | if (err < 0) | ||
2363 | return ERR_PTR(err); | ||
2364 | } | ||
2365 | |||
2366 | err = inode_permission(base->d_inode, MAY_EXEC); | ||
2367 | if (err) | ||
2368 | return ERR_PTR(err); | ||
2369 | |||
2370 | /* | ||
2371 | * __d_lookup() is used to try to get a quick answer and avoid the | ||
2372 | * mutex. A false-negative does no harm. | ||
2373 | */ | ||
2374 | ret = __d_lookup(base, &this); | ||
2375 | if (ret && unlikely(ret->d_flags & DCACHE_OP_REVALIDATE)) { | ||
2376 | dput(ret); | ||
2377 | ret = NULL; | ||
2378 | } | ||
2379 | if (ret) | ||
2380 | return ret; | ||
2381 | |||
2382 | mutex_lock(&base->d_inode->i_mutex); | ||
2383 | ret = __lookup_hash(&this, base, 0); | ||
2384 | mutex_unlock(&base->d_inode->i_mutex); | ||
2385 | return ret; | ||
2386 | } | ||
2387 | EXPORT_SYMBOL(lookup_one_len_unlocked); | ||
2388 | |||
2318 | int user_path_at_empty(int dfd, const char __user *name, unsigned flags, | 2389 | int user_path_at_empty(int dfd, const char __user *name, unsigned flags, |
2319 | struct path *path, int *empty) | 2390 | struct path *path, int *empty) |
2320 | { | 2391 | { |
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 00575d776d91..2246454dec76 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -823,7 +823,7 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, | |||
823 | } else | 823 | } else |
824 | dchild = dget(dparent); | 824 | dchild = dget(dparent); |
825 | } else | 825 | } else |
826 | dchild = lookup_one_len(name, dparent, namlen); | 826 | dchild = lookup_one_len_unlocked(name, dparent, namlen); |
827 | if (IS_ERR(dchild)) | 827 | if (IS_ERR(dchild)) |
828 | return rv; | 828 | return rv; |
829 | if (d_mountpoint(dchild)) | 829 | if (d_mountpoint(dchild)) |
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 51c9e9ca39a4..325521ce389a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -2838,14 +2838,14 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd, | |||
2838 | __be32 nfserr; | 2838 | __be32 nfserr; |
2839 | int ignore_crossmnt = 0; | 2839 | int ignore_crossmnt = 0; |
2840 | 2840 | ||
2841 | dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); | 2841 | dentry = lookup_one_len_unlocked(name, cd->rd_fhp->fh_dentry, namlen); |
2842 | if (IS_ERR(dentry)) | 2842 | if (IS_ERR(dentry)) |
2843 | return nfserrno(PTR_ERR(dentry)); | 2843 | return nfserrno(PTR_ERR(dentry)); |
2844 | if (d_really_is_negative(dentry)) { | 2844 | if (d_really_is_negative(dentry)) { |
2845 | /* | 2845 | /* |
2846 | * nfsd_buffered_readdir drops the i_mutex between | 2846 | * we're not holding the i_mutex here, so there's |
2847 | * readdir and calling this callback, leaving a window | 2847 | * a window where this directory entry could have gone |
2848 | * where this directory entry could have gone away. | 2848 | * away. |
2849 | */ | 2849 | */ |
2850 | dput(dentry); | 2850 | dput(dentry); |
2851 | return nfserr_noent; | 2851 | return nfserr_noent; |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 994d66fbb446..4212aaacbb55 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -217,10 +217,16 @@ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
217 | host_err = PTR_ERR(dentry); | 217 | host_err = PTR_ERR(dentry); |
218 | if (IS_ERR(dentry)) | 218 | if (IS_ERR(dentry)) |
219 | goto out_nfserr; | 219 | goto out_nfserr; |
220 | /* | ||
221 | * check if we have crossed a mount point ... | ||
222 | */ | ||
223 | if (nfsd_mountpoint(dentry, exp)) { | 220 | if (nfsd_mountpoint(dentry, exp)) { |
221 | /* | ||
222 | * We don't need the i_mutex after all. It's | ||
223 | * still possible we could open this (regular | ||
224 | * files can be mountpoints too), but the | ||
225 | * i_mutex is just there to prevent renames of | ||
226 | * something that we might be about to delegate, | ||
227 | * and a mountpoint won't be renamed: | ||
228 | */ | ||
229 | fh_unlock(fhp); | ||
224 | if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { | 230 | if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { |
225 | dput(dentry); | 231 | dput(dentry); |
226 | goto out_nfserr; | 232 | goto out_nfserr; |
@@ -1809,7 +1815,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func, | |||
1809 | offset = *offsetp; | 1815 | offset = *offsetp; |
1810 | 1816 | ||
1811 | while (1) { | 1817 | while (1) { |
1812 | struct inode *dir_inode = file_inode(file); | ||
1813 | unsigned int reclen; | 1818 | unsigned int reclen; |
1814 | 1819 | ||
1815 | cdp->err = nfserr_eof; /* will be cleared on successful read */ | 1820 | cdp->err = nfserr_eof; /* will be cleared on successful read */ |
@@ -1828,15 +1833,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func, | |||
1828 | if (!size) | 1833 | if (!size) |
1829 | break; | 1834 | break; |
1830 | 1835 | ||
1831 | /* | ||
1832 | * Various filldir functions may end up calling back into | ||
1833 | * lookup_one_len() and the file system's ->lookup() method. | ||
1834 | * These expect i_mutex to be held, as it would within readdir. | ||
1835 | */ | ||
1836 | host_err = mutex_lock_killable(&dir_inode->i_mutex); | ||
1837 | if (host_err) | ||
1838 | break; | ||
1839 | |||
1840 | de = (struct buffered_dirent *)buf.dirent; | 1836 | de = (struct buffered_dirent *)buf.dirent; |
1841 | while (size > 0) { | 1837 | while (size > 0) { |
1842 | offset = de->offset; | 1838 | offset = de->offset; |
@@ -1853,7 +1849,6 @@ static __be32 nfsd_buffered_readdir(struct file *file, nfsd_filldir_t func, | |||
1853 | size -= reclen; | 1849 | size -= reclen; |
1854 | de = (struct buffered_dirent *)((char *)de + reclen); | 1850 | de = (struct buffered_dirent *)((char *)de + reclen); |
1855 | } | 1851 | } |
1856 | mutex_unlock(&dir_inode->i_mutex); | ||
1857 | if (size > 0) /* We bailed out early */ | 1852 | if (size > 0) /* We bailed out early */ |
1858 | break; | 1853 | break; |
1859 | 1854 | ||
diff --git a/include/linux/namei.h b/include/linux/namei.h index d8c6334cd150..d0f25d81b46a 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h | |||
@@ -77,6 +77,7 @@ extern struct dentry *kern_path_locked(const char *, struct path *); | |||
77 | extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); | 77 | extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); |
78 | 78 | ||
79 | extern struct dentry *lookup_one_len(const char *, struct dentry *, int); | 79 | extern struct dentry *lookup_one_len(const char *, struct dentry *, int); |
80 | extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); | ||
80 | 81 | ||
81 | extern int follow_down_one(struct path *); | 82 | extern int follow_down_one(struct path *); |
82 | extern int follow_down(struct path *); | 83 | extern int follow_down(struct path *); |