diff options
author | Amir Goldstein <amir73il@gmail.com> | 2017-05-15 18:26:49 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 16:03:18 -0400 |
commit | 15932c415b3ed20bd1c1e05d071b4ad498656280 (patch) | |
tree | 9a5c7e198c8529323c57534720791e232a64ddb4 | |
parent | b9ac5c274b8c9d642567022c0e319bca4db31956 (diff) |
ovl: defer upper dir lock to tempfile link
On copy up of regular file using an O_TMPFILE, lock upper dir only
before linking the tempfile in place.
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r-- | fs/overlayfs/copy_up.c | 67 | ||||
-rw-r--r-- | fs/overlayfs/util.c | 1 |
2 files changed, 38 insertions, 30 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 5e8fd99557e1..28711af7f9db 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c | |||
@@ -316,6 +316,35 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *lower, | |||
316 | return err; | 316 | return err; |
317 | } | 317 | } |
318 | 318 | ||
319 | static int ovl_install_temp(struct dentry *workdir, struct dentry *upperdir, | ||
320 | struct dentry *dentry, | ||
321 | struct dentry *temp, struct kstat *pstat, | ||
322 | bool tmpfile, struct dentry **newdentry) | ||
323 | { | ||
324 | int err; | ||
325 | struct dentry *upper; | ||
326 | struct inode *udir = d_inode(upperdir); | ||
327 | |||
328 | upper = lookup_one_len(dentry->d_name.name, upperdir, | ||
329 | dentry->d_name.len); | ||
330 | if (IS_ERR(upper)) | ||
331 | return PTR_ERR(upper); | ||
332 | |||
333 | if (tmpfile) | ||
334 | err = ovl_do_link(temp, udir, upper, true); | ||
335 | else | ||
336 | err = ovl_do_rename(d_inode(workdir), temp, udir, upper, 0); | ||
337 | |||
338 | /* Restore timestamps on parent (best effort) */ | ||
339 | if (!err) { | ||
340 | ovl_set_timestamps(upperdir, pstat); | ||
341 | *newdentry = dget(tmpfile ? upper : temp); | ||
342 | } | ||
343 | dput(upper); | ||
344 | |||
345 | return err; | ||
346 | } | ||
347 | |||
319 | static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, | 348 | static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, |
320 | struct dentry *dentry, struct path *lowerpath, | 349 | struct dentry *dentry, struct path *lowerpath, |
321 | struct kstat *stat, const char *link, | 350 | struct kstat *stat, const char *link, |
@@ -324,7 +353,6 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, | |||
324 | struct inode *wdir = workdir->d_inode; | 353 | struct inode *wdir = workdir->d_inode; |
325 | struct inode *udir = upperdir->d_inode; | 354 | struct inode *udir = upperdir->d_inode; |
326 | struct dentry *newdentry = NULL; | 355 | struct dentry *newdentry = NULL; |
327 | struct dentry *upper = NULL; | ||
328 | struct dentry *temp = NULL; | 356 | struct dentry *temp = NULL; |
329 | int err; | 357 | int err; |
330 | const struct cred *old_creds = NULL; | 358 | const struct cred *old_creds = NULL; |
@@ -371,16 +399,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, | |||
371 | BUG_ON(upperpath.dentry != NULL); | 399 | BUG_ON(upperpath.dentry != NULL); |
372 | upperpath.dentry = temp; | 400 | upperpath.dentry = temp; |
373 | 401 | ||
374 | if (tmpfile) { | 402 | err = ovl_copy_up_data(lowerpath, &upperpath, stat->size); |
375 | inode_unlock(udir); | ||
376 | err = ovl_copy_up_data(lowerpath, &upperpath, | ||
377 | stat->size); | ||
378 | inode_lock_nested(udir, I_MUTEX_PARENT); | ||
379 | } else { | ||
380 | err = ovl_copy_up_data(lowerpath, &upperpath, | ||
381 | stat->size); | ||
382 | } | ||
383 | |||
384 | if (err) | 403 | if (err) |
385 | goto out_cleanup; | 404 | goto out_cleanup; |
386 | } | 405 | } |
@@ -408,29 +427,21 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, | |||
408 | goto out_cleanup; | 427 | goto out_cleanup; |
409 | } | 428 | } |
410 | 429 | ||
411 | upper = lookup_one_len(dentry->d_name.name, upperdir, | 430 | if (tmpfile) { |
412 | dentry->d_name.len); | 431 | inode_lock_nested(udir, I_MUTEX_PARENT); |
413 | if (IS_ERR(upper)) { | 432 | err = ovl_install_temp(workdir, upperdir, dentry, temp, pstat, |
414 | err = PTR_ERR(upper); | 433 | tmpfile, &newdentry); |
415 | upper = NULL; | 434 | inode_unlock(udir); |
416 | goto out_cleanup; | 435 | } else { |
436 | err = ovl_install_temp(workdir, upperdir, dentry, temp, pstat, | ||
437 | tmpfile, &newdentry); | ||
417 | } | 438 | } |
418 | |||
419 | if (tmpfile) | ||
420 | err = ovl_do_link(temp, udir, upper, true); | ||
421 | else | ||
422 | err = ovl_do_rename(wdir, temp, udir, upper, 0); | ||
423 | if (err) | 439 | if (err) |
424 | goto out_cleanup; | 440 | goto out_cleanup; |
425 | 441 | ||
426 | newdentry = dget(tmpfile ? upper : temp); | ||
427 | ovl_inode_update(d_inode(dentry), newdentry); | 442 | ovl_inode_update(d_inode(dentry), newdentry); |
428 | |||
429 | /* Restore timestamps on parent (best effort) */ | ||
430 | ovl_set_timestamps(upperdir, pstat); | ||
431 | out: | 443 | out: |
432 | dput(temp); | 444 | dput(temp); |
433 | dput(upper); | ||
434 | return err; | 445 | return err; |
435 | 446 | ||
436 | out_cleanup: | 447 | out_cleanup: |
@@ -496,10 +507,8 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, | |||
496 | goto out_done; | 507 | goto out_done; |
497 | } | 508 | } |
498 | 509 | ||
499 | inode_lock_nested(upperdir->d_inode, I_MUTEX_PARENT); | ||
500 | err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath, | 510 | err = ovl_copy_up_locked(workdir, upperdir, dentry, lowerpath, |
501 | stat, link, &pstat, true); | 511 | stat, link, &pstat, true); |
502 | inode_unlock(upperdir->d_inode); | ||
503 | ovl_copy_up_end(dentry); | 512 | ovl_copy_up_end(dentry); |
504 | goto out_done; | 513 | goto out_done; |
505 | } | 514 | } |
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 22ed51f80e58..c80b4bf1e64f 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c | |||
@@ -236,7 +236,6 @@ void ovl_inode_update(struct inode *inode, struct dentry *upperdentry) | |||
236 | { | 236 | { |
237 | struct inode *upperinode = d_inode(upperdentry); | 237 | struct inode *upperinode = d_inode(upperdentry); |
238 | 238 | ||
239 | WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode)); | ||
240 | WARN_ON(OVL_I(inode)->__upperdentry); | 239 | WARN_ON(OVL_I(inode)->__upperdentry); |
241 | 240 | ||
242 | /* | 241 | /* |