aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2013-07-26 06:23:25 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-09-03 22:50:29 -0400
commit8033426e6bdb2690d302872ac1e1fadaec1a5581 (patch)
treec6f1a95b751575b7d3af19df48bea3bd0a0c4748
parent590fb51f1cf99c4a48a3b1bd65885192e877b561 (diff)
vfs: allow umount to handle mountpoints without revalidating them
Christopher reported a regression where he was unable to unmount a NFS filesystem where the root had gone stale. The problem is that d_revalidate handles the root of the filesystem differently from other dentries, but d_weak_revalidate does not. We could simply fix this by making d_weak_revalidate return success on IS_ROOT dentries, but there are cases where we do want to revalidate the root of the fs. A umount is really a special case. We generally aren't interested in anything but the dentry and vfsmount that's attached at that point. If the inode turns out to be stale we just don't care since the intent is to stop using it anyway. Try to handle this situation better by treating umount as a special case in the lookup code. Have it resolve the parent using normal means, and then do a lookup of the final dentry without revalidating it. In most cases, the final lookup will come out of the dcache, but the case where there's a trailing symlink or !LAST_NORM entry on the end complicates things a bit. Cc: Neil Brown <neilb@suse.de> Reported-by: Christopher T Vogan <cvogan@us.ibm.com> Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/namei.c182
-rw-r--r--fs/namespace.c2
-rw-r--r--include/linux/namei.h1
3 files changed, 184 insertions, 1 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 89a612e392eb..b234e4ec0a71 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2184,6 +2184,188 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
2184 return s; 2184 return s;
2185} 2185}
2186 2186
2187/**
2188 * umount_lookup_last - look up last component for umount
2189 * @nd: pathwalk nameidata - currently pointing at parent directory of "last"
2190 * @path: pointer to container for result
2191 *
2192 * This is a special lookup_last function just for umount. In this case, we
2193 * need to resolve the path without doing any revalidation.
2194 *
2195 * The nameidata should be the result of doing a LOOKUP_PARENT pathwalk. Since
2196 * mountpoints are always pinned in the dcache, their ancestors are too. Thus,
2197 * in almost all cases, this lookup will be served out of the dcache. The only
2198 * cases where it won't are if nd->last refers to a symlink or the path is
2199 * bogus and it doesn't exist.
2200 *
2201 * Returns:
2202 * -error: if there was an error during lookup. This includes -ENOENT if the
2203 * lookup found a negative dentry. The nd->path reference will also be
2204 * put in this case.
2205 *
2206 * 0: if we successfully resolved nd->path and found it to not to be a
2207 * symlink that needs to be followed. "path" will also be populated.
2208 * The nd->path reference will also be put.
2209 *
2210 * 1: if we successfully resolved nd->last and found it to be a symlink
2211 * that needs to be followed. "path" will be populated with the path
2212 * to the link, and nd->path will *not* be put.
2213 */
2214static int
2215umount_lookup_last(struct nameidata *nd, struct path *path)
2216{
2217 int error = 0;
2218 struct dentry *dentry;
2219 struct dentry *dir = nd->path.dentry;
2220
2221 if (unlikely(nd->flags & LOOKUP_RCU)) {
2222 WARN_ON_ONCE(1);
2223 error = -ECHILD;
2224 goto error_check;
2225 }
2226
2227 nd->flags &= ~LOOKUP_PARENT;
2228
2229 if (unlikely(nd->last_type != LAST_NORM)) {
2230 error = handle_dots(nd, nd->last_type);
2231 if (!error)
2232 dentry = dget(nd->path.dentry);
2233 goto error_check;
2234 }
2235
2236 mutex_lock(&dir->d_inode->i_mutex);
2237 dentry = d_lookup(dir, &nd->last);
2238 if (!dentry) {
2239 /*
2240 * No cached dentry. Mounted dentries are pinned in the cache,
2241 * so that means that this dentry is probably a symlink or the
2242 * path doesn't actually point to a mounted dentry.
2243 */
2244 dentry = d_alloc(dir, &nd->last);
2245 if (!dentry) {
2246 error = -ENOMEM;
2247 } else {
2248 dentry = lookup_real(dir->d_inode, dentry, nd->flags);
2249 if (IS_ERR(dentry))
2250 error = PTR_ERR(dentry);
2251 }
2252 }
2253 mutex_unlock(&dir->d_inode->i_mutex);
2254
2255error_check:
2256 if (!error) {
2257 if (!dentry->d_inode) {
2258 error = -ENOENT;
2259 dput(dentry);
2260 } else {
2261 path->dentry = dentry;
2262 path->mnt = mntget(nd->path.mnt);
2263 if (should_follow_link(dentry->d_inode,
2264 nd->flags & LOOKUP_FOLLOW))
2265 return 1;
2266 follow_mount(path);
2267 }
2268 }
2269 terminate_walk(nd);
2270 return error;
2271}
2272
2273/**
2274 * path_umountat - look up a path to be umounted
2275 * @dfd: directory file descriptor to start walk from
2276 * @name: full pathname to walk
2277 * @flags: lookup flags
2278 * @nd: pathwalk nameidata
2279 *
2280 * Look up the given name, but don't attempt to revalidate the last component.
2281 * Returns 0 and "path" will be valid on success; Retuns error otherwise.
2282 */
2283static int
2284path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
2285{
2286 struct file *base = NULL;
2287 struct nameidata nd;
2288 int err;
2289
2290 err = path_init(dfd, name, flags | LOOKUP_PARENT, &nd, &base);
2291 if (unlikely(err))
2292 return err;
2293
2294 current->total_link_count = 0;
2295 err = link_path_walk(name, &nd);
2296 if (err)
2297 goto out;
2298
2299 /* If we're in rcuwalk, drop out of it to handle last component */
2300 if (nd.flags & LOOKUP_RCU) {
2301 err = unlazy_walk(&nd, NULL);
2302 if (err) {
2303 terminate_walk(&nd);
2304 goto out;
2305 }
2306 }
2307
2308 err = umount_lookup_last(&nd, path);
2309 while (err > 0) {
2310 void *cookie;
2311 struct path link = *path;
2312 err = may_follow_link(&link, &nd);
2313 if (unlikely(err))
2314 break;
2315 nd.flags |= LOOKUP_PARENT;
2316 err = follow_link(&link, &nd, &cookie);
2317 if (err)
2318 break;
2319 err = umount_lookup_last(&nd, path);
2320 put_link(&nd, &link, cookie);
2321 }
2322out:
2323 if (base)
2324 fput(base);
2325
2326 if (nd.root.mnt && !(nd.flags & LOOKUP_ROOT))
2327 path_put(&nd.root);
2328
2329 return err;
2330}
2331
2332/**
2333 * user_path_umountat - lookup a path from userland in order to umount it
2334 * @dfd: directory file descriptor
2335 * @name: pathname from userland
2336 * @flags: lookup flags
2337 * @path: pointer to container to hold result
2338 *
2339 * A umount is a special case for path walking. We're not actually interested
2340 * in the inode in this situation, and ESTALE errors can be a problem. We
2341 * simply want track down the dentry and vfsmount attached at the mountpoint
2342 * and avoid revalidating the last component.
2343 *
2344 * Returns 0 and populates "path" on success.
2345 */
2346int
2347user_path_umountat(int dfd, const char __user *name, unsigned int flags,
2348 struct path *path)
2349{
2350 struct filename *s = getname(name);
2351 int error;
2352
2353 if (IS_ERR(s))
2354 return PTR_ERR(s);
2355
2356 error = path_umountat(dfd, s->name, path, flags | LOOKUP_RCU);
2357 if (unlikely(error == -ECHILD))
2358 error = path_umountat(dfd, s->name, path, flags);
2359 if (unlikely(error == -ESTALE))
2360 error = path_umountat(dfd, s->name, path, flags | LOOKUP_REVAL);
2361
2362 if (likely(!error))
2363 audit_inode(s, path->dentry, 0);
2364
2365 putname(s);
2366 return error;
2367}
2368
2187/* 2369/*
2188 * It's inline, so penalty for filesystems that don't use sticky bit is 2370 * It's inline, so penalty for filesystems that don't use sticky bit is
2189 * minimal. 2371 * minimal.
diff --git a/fs/namespace.c b/fs/namespace.c
index a45ba4f267fe..ad8ea9bc2518 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1318,7 +1318,7 @@ SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
1318 if (!(flags & UMOUNT_NOFOLLOW)) 1318 if (!(flags & UMOUNT_NOFOLLOW))
1319 lookup_flags |= LOOKUP_FOLLOW; 1319 lookup_flags |= LOOKUP_FOLLOW;
1320 1320
1321 retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); 1321 retval = user_path_umountat(AT_FDCWD, name, lookup_flags, &path);
1322 if (retval) 1322 if (retval)
1323 goto out; 1323 goto out;
1324 mnt = real_mount(path.mnt); 1324 mnt = real_mount(path.mnt);
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 5a5ff57ceed4..cd09751c71a0 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -58,6 +58,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
58 58
59extern int user_path_at(int, const char __user *, unsigned, struct path *); 59extern int user_path_at(int, const char __user *, unsigned, struct path *);
60extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty); 60extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
61extern int user_path_umountat(int, const char __user *, unsigned int, struct path *);
61 62
62#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) 63#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)
63#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) 64#define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path)