diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 37 |
1 files changed, 35 insertions, 2 deletions
diff --git a/fs/namei.c b/fs/namei.c index 998d5316921a..7d694194024a 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2202,6 +2202,8 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2202 | struct file *filp; | 2202 | struct file *filp; |
2203 | struct inode *inode; | 2203 | struct inode *inode; |
2204 | int symlink_ok = 0; | 2204 | int symlink_ok = 0; |
2205 | struct path save_parent = { .dentry = NULL, .mnt = NULL }; | ||
2206 | bool retried = false; | ||
2205 | int error; | 2207 | int error; |
2206 | 2208 | ||
2207 | nd->flags &= ~LOOKUP_PARENT; | 2209 | nd->flags &= ~LOOKUP_PARENT; |
@@ -2267,6 +2269,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path, | |||
2267 | if (nd->last.name[nd->last.len]) | 2269 | if (nd->last.name[nd->last.len]) |
2268 | goto exit; | 2270 | goto exit; |
2269 | 2271 | ||
2272 | retry_lookup: | ||
2270 | mutex_lock(&dir->d_inode->i_mutex); | 2273 | mutex_lock(&dir->d_inode->i_mutex); |
2271 | 2274 | ||
2272 | dentry = lookup_hash(nd); | 2275 | dentry = lookup_hash(nd); |
@@ -2349,12 +2352,21 @@ finish_lookup: | |||
2349 | return NULL; | 2352 | return NULL; |
2350 | } | 2353 | } |
2351 | 2354 | ||
2352 | path_to_nameidata(path, nd); | 2355 | if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) { |
2356 | path_to_nameidata(path, nd); | ||
2357 | } else { | ||
2358 | save_parent.dentry = nd->path.dentry; | ||
2359 | save_parent.mnt = mntget(path->mnt); | ||
2360 | nd->path.dentry = path->dentry; | ||
2361 | |||
2362 | } | ||
2353 | nd->inode = inode; | 2363 | nd->inode = inode; |
2354 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ | 2364 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ |
2355 | error = complete_walk(nd); | 2365 | error = complete_walk(nd); |
2356 | if (error) | 2366 | if (error) { |
2367 | path_put(&save_parent); | ||
2357 | return ERR_PTR(error); | 2368 | return ERR_PTR(error); |
2369 | } | ||
2358 | error = -EISDIR; | 2370 | error = -EISDIR; |
2359 | if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode)) | 2371 | if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode)) |
2360 | goto exit; | 2372 | goto exit; |
@@ -2377,6 +2389,20 @@ common: | |||
2377 | if (error) | 2389 | if (error) |
2378 | goto exit; | 2390 | goto exit; |
2379 | filp = nameidata_to_filp(nd); | 2391 | filp = nameidata_to_filp(nd); |
2392 | if (filp == ERR_PTR(-EOPENSTALE) && save_parent.dentry && !retried) { | ||
2393 | BUG_ON(save_parent.dentry != dir); | ||
2394 | path_put(&nd->path); | ||
2395 | nd->path = save_parent; | ||
2396 | nd->inode = dir->d_inode; | ||
2397 | save_parent.mnt = NULL; | ||
2398 | save_parent.dentry = NULL; | ||
2399 | if (want_write) { | ||
2400 | mnt_drop_write(nd->path.mnt); | ||
2401 | want_write = 0; | ||
2402 | } | ||
2403 | retried = true; | ||
2404 | goto retry_lookup; | ||
2405 | } | ||
2380 | if (!IS_ERR(filp)) { | 2406 | if (!IS_ERR(filp)) { |
2381 | error = ima_file_check(filp, op->acc_mode); | 2407 | error = ima_file_check(filp, op->acc_mode); |
2382 | if (error) { | 2408 | if (error) { |
@@ -2396,6 +2422,7 @@ common: | |||
2396 | out: | 2422 | out: |
2397 | if (want_write) | 2423 | if (want_write) |
2398 | mnt_drop_write(nd->path.mnt); | 2424 | mnt_drop_write(nd->path.mnt); |
2425 | path_put(&save_parent); | ||
2399 | terminate_walk(nd); | 2426 | terminate_walk(nd); |
2400 | return filp; | 2427 | return filp; |
2401 | 2428 | ||
@@ -2459,6 +2486,12 @@ out: | |||
2459 | if (base) | 2486 | if (base) |
2460 | fput(base); | 2487 | fput(base); |
2461 | release_open_intent(nd); | 2488 | release_open_intent(nd); |
2489 | if (filp == ERR_PTR(-EOPENSTALE)) { | ||
2490 | if (flags & LOOKUP_RCU) | ||
2491 | filp = ERR_PTR(-ECHILD); | ||
2492 | else | ||
2493 | filp = ERR_PTR(-ESTALE); | ||
2494 | } | ||
2462 | return filp; | 2495 | return filp; |
2463 | 2496 | ||
2464 | out_filp: | 2497 | out_filp: |