aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2018-02-04 08:35:09 -0500
committerMiklos Szeredi <mszeredi@redhat.com>2018-02-16 09:53:20 -0500
commit764baba80168ad3adafb521d2ab483ccbc49e344 (patch)
treeef3d5d54c0732740a41f98604caee86c2e939be1
parent7928b2cbe55b2a410a0f5c1f154610059c57b1b2 (diff)
ovl: hash non-dir by lower inode for fsnotify
Commit 31747eda41ef ("ovl: hash directory inodes for fsnotify") fixed an issue of inotify watch on directory that stops getting events after dropping dentry caches. A similar issue exists for non-dir non-upper files, for example: $ mkdir -p lower upper work merged $ touch lower/foo $ mount -t overlay -o lowerdir=lower,workdir=work,upperdir=upper none merged $ inotifywait merged/foo & $ echo 2 > /proc/sys/vm/drop_caches $ cat merged/foo inotifywait doesn't get the OPEN event, because ovl_lookup() called from 'cat' allocates a new overlay inode and does not reuse the watched inode. Fix this by hashing non-dir overlay inodes by lower real inode in the following cases that were not hashed before this change: - A non-upper overlay mount - A lower non-hardlink when index=off A helper ovl_hash_bylower() was added to put all the logic and documentation about which real inode an overlay inode is hashed by into one place. The issue dates back to initial version of overlayfs, but this patch depends on ovl_inode code that was introduced in kernel v4.13. Cc: <stable@vger.kernel.org> #v4.13 Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/overlayfs/inode.c58
1 files changed, 40 insertions, 18 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index fcd97b783fa1..3b1bd469accd 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -669,38 +669,59 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
669 return inode; 669 return inode;
670} 670}
671 671
672/*
673 * Does overlay inode need to be hashed by lower inode?
674 */
675static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
676 struct dentry *lower, struct dentry *index)
677{
678 struct ovl_fs *ofs = sb->s_fs_info;
679
680 /* No, if pure upper */
681 if (!lower)
682 return false;
683
684 /* Yes, if already indexed */
685 if (index)
686 return true;
687
688 /* Yes, if won't be copied up */
689 if (!ofs->upper_mnt)
690 return true;
691
692 /* No, if lower hardlink is or will be broken on copy up */
693 if ((upper || !ovl_indexdir(sb)) &&
694 !d_is_dir(lower) && d_inode(lower)->i_nlink > 1)
695 return false;
696
697 /* No, if non-indexed upper with NFS export */
698 if (sb->s_export_op && upper)
699 return false;
700
701 /* Otherwise, hash by lower inode for fsnotify */
702 return true;
703}
704
672struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry, 705struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
673 struct dentry *lowerdentry, struct dentry *index, 706 struct dentry *lowerdentry, struct dentry *index,
674 unsigned int numlower) 707 unsigned int numlower)
675{ 708{
676 struct ovl_fs *ofs = sb->s_fs_info;
677 struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL; 709 struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
678 struct inode *inode; 710 struct inode *inode;
679 /* Already indexed or could be indexed on copy up? */ 711 bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
680 bool indexed = (index || (ovl_indexdir(sb) && !upperdentry));
681 struct dentry *origin = indexed ? lowerdentry : NULL;
682 bool is_dir; 712 bool is_dir;
683 713
684 if (WARN_ON(upperdentry && indexed && !lowerdentry))
685 return ERR_PTR(-EIO);
686
687 if (!realinode) 714 if (!realinode)
688 realinode = d_inode(lowerdentry); 715 realinode = d_inode(lowerdentry);
689 716
690 /* 717 /*
691 * Copy up origin (lower) may exist for non-indexed non-dir upper, but 718 * Copy up origin (lower) may exist for non-indexed upper, but we must
692 * we must not use lower as hash key in that case. 719 * not use lower as hash key if this is a broken hardlink.
693 * Hash non-dir that is or could be indexed by origin inode.
694 * Hash dir that is or could be merged by origin inode.
695 * Hash pure upper and non-indexed non-dir by upper inode.
696 * Hash non-indexed dir by upper inode for NFS export.
697 */ 720 */
698 is_dir = S_ISDIR(realinode->i_mode); 721 is_dir = S_ISDIR(realinode->i_mode);
699 if (is_dir && (indexed || !sb->s_export_op || !ofs->upper_mnt)) 722 if (upperdentry || bylower) {
700 origin = lowerdentry; 723 struct inode *key = d_inode(bylower ? lowerdentry :
701 724 upperdentry);
702 if (upperdentry || origin) {
703 struct inode *key = d_inode(origin ?: upperdentry);
704 unsigned int nlink = is_dir ? 1 : realinode->i_nlink; 725 unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
705 726
706 inode = iget5_locked(sb, (unsigned long) key, 727 inode = iget5_locked(sb, (unsigned long) key,
@@ -728,6 +749,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
728 nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink); 749 nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
729 set_nlink(inode, nlink); 750 set_nlink(inode, nlink);
730 } else { 751 } else {
752 /* Lower hardlink that will be broken on copy up */
731 inode = new_inode(sb); 753 inode = new_inode(sb);
732 if (!inode) 754 if (!inode)
733 goto out_nomem; 755 goto out_nomem;