diff options
| author | Amir Goldstein <amir73il@gmail.com> | 2017-06-20 08:35:14 -0400 |
|---|---|---|
| committer | Miklos Szeredi <mszeredi@redhat.com> | 2017-07-04 16:03:19 -0400 |
| commit | 5f8415d6b87ecb4ebf1bbd02c538694ebb7fb57c (patch) | |
| tree | d3764780b95675c7d834ae4ba0d3d834bb9fc6e9 | |
| parent | 59be09712ab98a3060f13e31343c7abb9bc4583d (diff) | |
ovl: persistent overlay inode nlink for indexed inodes
With inodes index enabled, an overlay inode nlink counts the union of upper
and non-covered lower hardlinks. During the lifetime of a non-pure upper
inode, the following nlink modifying operations can happen:
1. Lower hardlink copy up
2. Upper hardlink created, unlinked or renamed over
3. Lower hardlink whiteout or renamed over
For the first, copy up case, the union nlink does not change, whether the
operation succeeds or fails, but the upper inode nlink may change.
Therefore, before copy up, we store the union nlink value relative to the
lower inode nlink in the index inode xattr trusted.overlay.nlink.
For the second, upper hardlink case, the union nlink should be incremented
or decremented IFF the operation succeeds, aligned with nlink change of the
upper inode. Therefore, before link/unlink/rename, we store the union nlink
value relative to the upper inode nlink in the index inode.
For the last, lower cover up case, we simplify things by preceding the
whiteout or cover up with copy up. This makes sure that there is an index
upper inode where the nlink xattr can be stored before the copied up upper
entry is unlink.
Return the overlay inode nlinks for indexed upper inodes on stat(2).
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
| -rw-r--r-- | fs/overlayfs/copy_up.c | 5 | ||||
| -rw-r--r-- | fs/overlayfs/dir.c | 19 | ||||
| -rw-r--r-- | fs/overlayfs/inode.c | 112 | ||||
| -rw-r--r-- | fs/overlayfs/overlayfs.h | 5 | ||||
| -rw-r--r-- | fs/overlayfs/util.c | 66 |
5 files changed, 204 insertions, 3 deletions
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 9f5a47338e59..f193976560de 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c | |||
| @@ -323,6 +323,10 @@ static int ovl_link_up(struct dentry *parent, struct dentry *dentry) | |||
| 323 | struct dentry *upperdir = ovl_dentry_upper(parent); | 323 | struct dentry *upperdir = ovl_dentry_upper(parent); |
| 324 | struct inode *udir = d_inode(upperdir); | 324 | struct inode *udir = d_inode(upperdir); |
| 325 | 325 | ||
| 326 | err = ovl_set_nlink_lower(dentry); | ||
| 327 | if (err) | ||
| 328 | return err; | ||
| 329 | |||
| 326 | inode_lock_nested(udir, I_MUTEX_PARENT); | 330 | inode_lock_nested(udir, I_MUTEX_PARENT); |
| 327 | upper = lookup_one_len(dentry->d_name.name, upperdir, | 331 | upper = lookup_one_len(dentry->d_name.name, upperdir, |
| 328 | dentry->d_name.len); | 332 | dentry->d_name.len); |
| @@ -335,6 +339,7 @@ static int ovl_link_up(struct dentry *parent, struct dentry *dentry) | |||
| 335 | ovl_dentry_set_upper_alias(dentry); | 339 | ovl_dentry_set_upper_alias(dentry); |
| 336 | } | 340 | } |
| 337 | inode_unlock(udir); | 341 | inode_unlock(udir); |
| 342 | ovl_set_nlink_upper(dentry); | ||
| 338 | 343 | ||
| 339 | return err; | 344 | return err; |
| 340 | } | 345 | } |
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 8b2b23181b19..641d9ee97f91 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c | |||
| @@ -591,6 +591,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, | |||
| 591 | struct dentry *new) | 591 | struct dentry *new) |
| 592 | { | 592 | { |
| 593 | int err; | 593 | int err; |
| 594 | bool locked = false; | ||
| 594 | struct inode *inode; | 595 | struct inode *inode; |
| 595 | 596 | ||
| 596 | err = ovl_want_write(old); | 597 | err = ovl_want_write(old); |
| @@ -601,6 +602,10 @@ static int ovl_link(struct dentry *old, struct inode *newdir, | |||
| 601 | if (err) | 602 | if (err) |
| 602 | goto out_drop_write; | 603 | goto out_drop_write; |
| 603 | 604 | ||
| 605 | err = ovl_nlink_start(old, &locked); | ||
| 606 | if (err) | ||
| 607 | goto out_drop_write; | ||
| 608 | |||
| 604 | inode = d_inode(old); | 609 | inode = d_inode(old); |
| 605 | ihold(inode); | 610 | ihold(inode); |
| 606 | 611 | ||
| @@ -608,6 +613,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir, | |||
| 608 | if (err) | 613 | if (err) |
| 609 | iput(inode); | 614 | iput(inode); |
| 610 | 615 | ||
| 616 | ovl_nlink_end(old, locked); | ||
| 611 | out_drop_write: | 617 | out_drop_write: |
| 612 | ovl_drop_write(old); | 618 | ovl_drop_write(old); |
| 613 | out: | 619 | out: |
| @@ -743,8 +749,8 @@ out: | |||
| 743 | 749 | ||
| 744 | static int ovl_do_remove(struct dentry *dentry, bool is_dir) | 750 | static int ovl_do_remove(struct dentry *dentry, bool is_dir) |
| 745 | { | 751 | { |
| 746 | enum ovl_path_type type; | ||
| 747 | int err; | 752 | int err; |
| 753 | bool locked = false; | ||
| 748 | const struct cred *old_cred; | 754 | const struct cred *old_cred; |
| 749 | 755 | ||
| 750 | err = ovl_want_write(dentry); | 756 | err = ovl_want_write(dentry); |
| @@ -755,7 +761,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) | |||
| 755 | if (err) | 761 | if (err) |
| 756 | goto out_drop_write; | 762 | goto out_drop_write; |
| 757 | 763 | ||
| 758 | type = ovl_path_type(dentry); | 764 | err = ovl_nlink_start(dentry, &locked); |
| 765 | if (err) | ||
| 766 | goto out_drop_write; | ||
| 759 | 767 | ||
| 760 | old_cred = ovl_override_creds(dentry->d_sb); | 768 | old_cred = ovl_override_creds(dentry->d_sb); |
| 761 | if (!ovl_lower_positive(dentry)) | 769 | if (!ovl_lower_positive(dentry)) |
| @@ -769,6 +777,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) | |||
| 769 | else | 777 | else |
| 770 | drop_nlink(dentry->d_inode); | 778 | drop_nlink(dentry->d_inode); |
| 771 | } | 779 | } |
| 780 | ovl_nlink_end(dentry, locked); | ||
| 772 | out_drop_write: | 781 | out_drop_write: |
| 773 | ovl_drop_write(dentry); | 782 | ovl_drop_write(dentry); |
| 774 | out: | 783 | out: |
| @@ -891,6 +900,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, | |||
| 891 | unsigned int flags) | 900 | unsigned int flags) |
| 892 | { | 901 | { |
| 893 | int err; | 902 | int err; |
| 903 | bool locked = false; | ||
| 894 | struct dentry *old_upperdir; | 904 | struct dentry *old_upperdir; |
| 895 | struct dentry *new_upperdir; | 905 | struct dentry *new_upperdir; |
| 896 | struct dentry *olddentry; | 906 | struct dentry *olddentry; |
| @@ -934,6 +944,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, | |||
| 934 | err = ovl_copy_up(new); | 944 | err = ovl_copy_up(new); |
| 935 | if (err) | 945 | if (err) |
| 936 | goto out_drop_write; | 946 | goto out_drop_write; |
| 947 | } else { | ||
| 948 | err = ovl_nlink_start(new, &locked); | ||
| 949 | if (err) | ||
| 950 | goto out_drop_write; | ||
| 937 | } | 951 | } |
| 938 | 952 | ||
| 939 | old_cred = ovl_override_creds(old->d_sb); | 953 | old_cred = ovl_override_creds(old->d_sb); |
| @@ -1072,6 +1086,7 @@ out_unlock: | |||
| 1072 | unlock_rename(new_upperdir, old_upperdir); | 1086 | unlock_rename(new_upperdir, old_upperdir); |
| 1073 | out_revert_creds: | 1087 | out_revert_creds: |
| 1074 | revert_creds(old_cred); | 1088 | revert_creds(old_cred); |
| 1089 | ovl_nlink_end(new, locked); | ||
| 1075 | out_drop_write: | 1090 | out_drop_write: |
| 1076 | ovl_drop_write(old); | 1091 | ovl_drop_write(old); |
| 1077 | out: | 1092 | out: |
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 44d262a0a77e..196a4e5450c0 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/cred.h> | 12 | #include <linux/cred.h> |
| 13 | #include <linux/xattr.h> | 13 | #include <linux/xattr.h> |
| 14 | #include <linux/posix_acl.h> | 14 | #include <linux/posix_acl.h> |
| 15 | #include <linux/ratelimit.h> | ||
| 15 | #include "overlayfs.h" | 16 | #include "overlayfs.h" |
| 16 | 17 | ||
| 17 | int ovl_setattr(struct dentry *dentry, struct iattr *attr) | 18 | int ovl_setattr(struct dentry *dentry, struct iattr *attr) |
| @@ -130,6 +131,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat, | |||
| 130 | if (is_dir && OVL_TYPE_MERGE(type)) | 131 | if (is_dir && OVL_TYPE_MERGE(type)) |
| 131 | stat->nlink = 1; | 132 | stat->nlink = 1; |
| 132 | 133 | ||
| 134 | /* | ||
| 135 | * Return the overlay inode nlinks for indexed upper inodes. | ||
| 136 | * Overlay inode nlink counts the union of the upper hardlinks | ||
| 137 | * and non-covered lower hardlinks. It does not include the upper | ||
| 138 | * index hardlink. | ||
| 139 | */ | ||
| 140 | if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry))) | ||
| 141 | stat->nlink = dentry->d_inode->i_nlink; | ||
| 142 | |||
| 133 | out: | 143 | out: |
| 134 | revert_creds(old_cred); | 144 | revert_creds(old_cred); |
| 135 | 145 | ||
| @@ -442,6 +452,103 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev) | |||
| 442 | } | 452 | } |
| 443 | } | 453 | } |
| 444 | 454 | ||
| 455 | /* | ||
| 456 | * With inodes index enabled, an overlay inode nlink counts the union of upper | ||
| 457 | * hardlinks and non-covered lower hardlinks. During the lifetime of a non-pure | ||
| 458 | * upper inode, the following nlink modifying operations can happen: | ||
| 459 | * | ||
| 460 | * 1. Lower hardlink copy up | ||
| 461 | * 2. Upper hardlink created, unlinked or renamed over | ||
| 462 | * 3. Lower hardlink whiteout or renamed over | ||
| 463 | * | ||
| 464 | * For the first, copy up case, the union nlink does not change, whether the | ||
| 465 | * operation succeeds or fails, but the upper inode nlink may change. | ||
| 466 | * Therefore, before copy up, we store the union nlink value relative to the | ||
| 467 | * lower inode nlink in the index inode xattr trusted.overlay.nlink. | ||
| 468 | * | ||
| 469 | * For the second, upper hardlink case, the union nlink should be incremented | ||
| 470 | * or decremented IFF the operation succeeds, aligned with nlink change of the | ||
| 471 | * upper inode. Therefore, before link/unlink/rename, we store the union nlink | ||
| 472 | * value relative to the upper inode nlink in the index inode. | ||
| 473 | * | ||
| 474 | * For the last, lower cover up case, we simplify things by preceding the | ||
| 475 | * whiteout or cover up with copy up. This makes sure that there is an index | ||
| 476 | * upper inode where the nlink xattr can be stored before the copied up upper | ||
| 477 | * entry is unlink. | ||
| 478 | */ | ||
| 479 | #define OVL_NLINK_ADD_UPPER (1 << 0) | ||
| 480 | |||
| 481 | /* | ||
| 482 | * On-disk format for indexed nlink: | ||
| 483 | * | ||
| 484 | * nlink relative to the upper inode - "U[+-]NUM" | ||
| 485 | * nlink relative to the lower inode - "L[+-]NUM" | ||
| 486 | */ | ||
| 487 | |||
| 488 | static int ovl_set_nlink_common(struct dentry *dentry, | ||
| 489 | struct dentry *realdentry, const char *format) | ||
| 490 | { | ||
| 491 | struct inode *inode = d_inode(dentry); | ||
