Skip to content

Commit d134e91

Browse files
amir73ilgregkh
authored andcommitted
ovl: hash non-dir by lower inode for fsnotify
commit 764baba upstream. Commit 31747ed ("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: <[email protected]> #v4.13 Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]> Signed-off-by: Mark Salyzyn <[email protected]> #4.14 Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1054700 commit d134e91

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

fs/overlayfs/inode.c

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <linux/posix_acl.h>
1515
#include <linux/ratelimit.h>
1616
#include "overlayfs.h"
17+
#include "ovl_entry.h"
1718

1819
int ovl_setattr(struct dentry *dentry, struct iattr *attr)
1920
{
@@ -608,39 +609,63 @@ static bool ovl_verify_inode(struct inode *inode, struct dentry *lowerdentry,
608609
return true;
609610
}
610611

612+
/*
613+
* Does overlay inode need to be hashed by lower inode?
614+
*/
615+
static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
616+
struct dentry *lower, struct dentry *index)
617+
{
618+
struct ovl_fs *ofs = sb->s_fs_info;
619+
620+
/* No, if pure upper */
621+
if (!lower)
622+
return false;
623+
624+
/* Yes, if already indexed */
625+
if (index)
626+
return true;
627+
628+
/* Yes, if won't be copied up */
629+
if (!ofs->upper_mnt)
630+
return true;
631+
632+
/* No, if lower hardlink is or will be broken on copy up */
633+
if ((upper || !ovl_indexdir(sb)) &&
634+
!d_is_dir(lower) && d_inode(lower)->i_nlink > 1)
635+
return false;
636+
637+
/* No, if non-indexed upper with NFS export */
638+
if (sb->s_export_op && upper)
639+
return false;
640+
641+
/* Otherwise, hash by lower inode for fsnotify */
642+
return true;
643+
}
644+
611645
struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
612646
struct dentry *index)
613647
{
648+
struct super_block *sb = dentry->d_sb;
614649
struct dentry *lowerdentry = ovl_dentry_lower(dentry);
615650
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
616651
struct inode *inode;
617-
/* Already indexed or could be indexed on copy up? */
618-
bool indexed = (index || (ovl_indexdir(dentry->d_sb) && !upperdentry));
619-
struct dentry *origin = indexed ? lowerdentry : NULL;
652+
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
620653
bool is_dir;
621654

622-
if (WARN_ON(upperdentry && indexed && !lowerdentry))
623-
return ERR_PTR(-EIO);
624-
625655
if (!realinode)
626656
realinode = d_inode(lowerdentry);
627657

628658
/*
629-
* Copy up origin (lower) may exist for non-indexed non-dir upper, but
630-
* we must not use lower as hash key in that case.
631-
* Hash non-dir that is or could be indexed by origin inode.
632-
* Hash dir that is or could be merged by origin inode.
633-
* Hash pure upper and non-indexed non-dir by upper inode.
659+
* Copy up origin (lower) may exist for non-indexed upper, but we must
660+
* not use lower as hash key if this is a broken hardlink.
634661
*/
635662
is_dir = S_ISDIR(realinode->i_mode);
636-
if (is_dir)
637-
origin = lowerdentry;
638-
639-
if (upperdentry || origin) {
640-
struct inode *key = d_inode(origin ?: upperdentry);
663+
if (upperdentry || bylower) {
664+
struct inode *key = d_inode(bylower ? lowerdentry :
665+
upperdentry);
641666
unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
642667

643-
inode = iget5_locked(dentry->d_sb, (unsigned long) key,
668+
inode = iget5_locked(sb, (unsigned long) key,
644669
ovl_inode_test, ovl_inode_set, key);
645670
if (!inode)
646671
goto out_nomem;
@@ -664,7 +689,8 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry,
664689
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
665690
set_nlink(inode, nlink);
666691
} else {
667-
inode = new_inode(dentry->d_sb);
692+
/* Lower hardlink that will be broken on copy up */
693+
inode = new_inode(sb);
668694
if (!inode)
669695
goto out_nomem;
670696
}

0 commit comments

Comments
 (0)