diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 61 |
1 files changed, 15 insertions, 46 deletions
diff --git a/fs/namei.c b/fs/namei.c index 6a82fb7e2127..70580ab1445c 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -3030,9 +3030,13 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, | |||
3030 | } | 3030 | } |
3031 | if (*opened & FILE_CREATED) | 3031 | if (*opened & FILE_CREATED) |
3032 | fsnotify_create(dir, dentry); | 3032 | fsnotify_create(dir, dentry); |
3033 | path->dentry = dentry; | 3033 | if (unlikely(d_is_negative(dentry))) { |
3034 | path->mnt = nd->path.mnt; | 3034 | error = -ENOENT; |
3035 | return 1; | 3035 | } else { |
3036 | path->dentry = dentry; | ||
3037 | path->mnt = nd->path.mnt; | ||
3038 | return 1; | ||
3039 | } | ||
3036 | } | 3040 | } |
3037 | } | 3041 | } |
3038 | dput(dentry); | 3042 | dput(dentry); |
@@ -3201,9 +3205,7 @@ static int do_last(struct nameidata *nd, | |||
3201 | int acc_mode = op->acc_mode; | 3205 | int acc_mode = op->acc_mode; |
3202 | unsigned seq; | 3206 | unsigned seq; |
3203 | struct inode *inode; | 3207 | struct inode *inode; |
3204 | struct path save_parent = { .dentry = NULL, .mnt = NULL }; | ||
3205 | struct path path; | 3208 | struct path path; |
3206 | bool retried = false; | ||
3207 | int error; | 3209 | int error; |
3208 | 3210 | ||
3209 | nd->flags &= ~LOOKUP_PARENT; | 3211 | nd->flags &= ~LOOKUP_PARENT; |
@@ -3246,7 +3248,6 @@ static int do_last(struct nameidata *nd, | |||
3246 | return -EISDIR; | 3248 | return -EISDIR; |
3247 | } | 3249 | } |
3248 | 3250 | ||
3249 | retry_lookup: | ||
3250 | if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) { | 3251 | if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) { |
3251 | error = mnt_want_write(nd->path.mnt); | 3252 | error = mnt_want_write(nd->path.mnt); |
3252 | if (!error) | 3253 | if (!error) |
@@ -3298,6 +3299,10 @@ retry_lookup: | |||
3298 | got_write = false; | 3299 | got_write = false; |
3299 | } | 3300 | } |
3300 | 3301 | ||
3302 | error = follow_managed(&path, nd); | ||
3303 | if (unlikely(error < 0)) | ||
3304 | return error; | ||
3305 | |||
3301 | if (unlikely(d_is_negative(path.dentry))) { | 3306 | if (unlikely(d_is_negative(path.dentry))) { |
3302 | path_to_nameidata(&path, nd); | 3307 | path_to_nameidata(&path, nd); |
3303 | return -ENOENT; | 3308 | return -ENOENT; |
@@ -3313,10 +3318,6 @@ retry_lookup: | |||
3313 | return -EEXIST; | 3318 | return -EEXIST; |
3314 | } | 3319 | } |
3315 | 3320 | ||
3316 | error = follow_managed(&path, nd); | ||
3317 | if (unlikely(error < 0)) | ||
3318 | return error; | ||
3319 | |||
3320 | seq = 0; /* out of RCU mode, so the value doesn't matter */ | 3321 | seq = 0; /* out of RCU mode, so the value doesn't matter */ |
3321 | inode = d_backing_inode(path.dentry); | 3322 | inode = d_backing_inode(path.dentry); |
3322 | finish_lookup: | 3323 | finish_lookup: |
@@ -3327,23 +3328,14 @@ finish_lookup: | |||
3327 | if (unlikely(error)) | 3328 | if (unlikely(error)) |
3328 | return error; | 3329 | return error; |
3329 | 3330 | ||
3330 | if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) { | 3331 | path_to_nameidata(&path, nd); |
3331 | path_to_nameidata(&path, nd); | ||
3332 | } else { | ||
3333 | save_parent.dentry = nd->path.dentry; | ||
3334 | save_parent.mnt = mntget(path.mnt); | ||
3335 | nd->path.dentry = path.dentry; | ||
3336 | |||
3337 | } | ||
3338 | nd->inode = inode; | 3332 | nd->inode = inode; |
3339 | nd->seq = seq; | 3333 | nd->seq = seq; |
3340 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ | 3334 | /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */ |
3341 | finish_open: | 3335 | finish_open: |
3342 | error = complete_walk(nd); | 3336 | error = complete_walk(nd); |
3343 | if (error) { | 3337 | if (error) |
3344 | path_put(&save_parent); | ||
3345 | return error; | 3338 | return error; |
3346 | } | ||
3347 | audit_inode(nd->name, nd->path.dentry, 0); | 3339 | audit_inode(nd->name, nd->path.dentry, 0); |
3348 | error = -EISDIR; | 3340 | error = -EISDIR; |
3349 | if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) | 3341 | if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) |
@@ -3366,13 +3358,9 @@ finish_open_created: | |||
3366 | goto out; | 3358 | goto out; |
3367 | BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ | 3359 | BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ |
3368 | error = vfs_open(&nd->path, file, current_cred()); | 3360 | error = vfs_open(&nd->path, file, current_cred()); |
3369 | if (!error) { | 3361 | if (error) |
3370 | *opened |= FILE_OPENED; | ||
3371 | } else { | ||
3372 | if (error == -EOPENSTALE) | ||
3373 | goto stale_open; | ||
3374 | goto out; | 3362 | goto out; |
3375 | } | 3363 | *opened |= FILE_OPENED; |
3376 | opened: | 3364 | opened: |
3377 | error = open_check_o_direct(file); | 3365 | error = open_check_o_direct(file); |
3378 | if (!error) | 3366 | if (!error) |
@@ -3388,26 +3376,7 @@ out: | |||
3388 | } | 3376 | } |
3389 | if (got_write) | 3377 | if (got_write) |
3390 | mnt_drop_write(nd->path.mnt); | 3378 | mnt_drop_write(nd->path.mnt); |
3391 | path_put(&save_parent); | ||
3392 | return error; | 3379 | return error; |
3393 | |||
3394 | stale_open: | ||
3395 | /* If no saved parent or already retried then can't retry */ | ||
3396 | if (!save_parent.dentry || retried) | ||
3397 | goto out; | ||
3398 | |||
3399 | BUG_ON(save_parent.dentry != dir); | ||
3400 | path_put(&nd->path); | ||
3401 | nd->path = save_parent; | ||
3402 | nd->inode = dir->d_inode; | ||
3403 | save_parent.mnt = NULL; | ||
3404 | save_parent.dentry = NULL; | ||
3405 | if (got_write) { | ||
3406 | mnt_drop_write(nd->path.mnt); | ||
3407 | got_write = false; | ||
3408 | } | ||
3409 | retried = true; | ||
3410 | goto retry_lookup; | ||
3411 | } | 3380 | } |
3412 | 3381 | ||
3413 | static int do_tmpfile(struct nameidata *nd, unsigned flags, | 3382 | static int do_tmpfile(struct nameidata *nd, unsigned flags, |