Skip to content

Commit 764baba

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: hash non-dir by lower inode for fsnotify
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]>
1 parent 7928b2c commit 764baba

File tree

1 file changed

+40
-18
lines changed

1 file changed

+40
-18
lines changed

fs/overlayfs/inode.c

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -669,38 +669,59 @@ struct inode *ovl_lookup_inode(struct super_block *sb, struct dentry *real,
669669
return inode;
670670
}
671671

672+
/*
673+
* Does overlay inode need to be hashed by lower inode?
674+
*/
675+
static 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+
672705
struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
673706
struct dentry *lowerdentry, struct dentry *index,
674707
unsigned int numlower)
675708
{
676-
struct ovl_fs *ofs = sb->s_fs_info;
677709
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
678710
struct inode *inode;
679-
/* Already indexed or could be indexed on copy up? */
680-
bool indexed = (index || (ovl_indexdir(sb) && !upperdentry));
681-
struct dentry *origin = indexed ? lowerdentry : NULL;
711+
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, index);
682712
bool is_dir;
683713

684-
if (WARN_ON(upperdentry && indexed && !lowerdentry))
685-
return ERR_PTR(-EIO);
686-
687714
if (!realinode)
688715
realinode = d_inode(lowerdentry);
689716

690717
/*
691-
* Copy up origin (lower) may exist for non-indexed non-dir upper, but
692-
* we must not use lower as hash key in that case.
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.
718+
* Copy up origin (lower) may exist for non-indexed upper, but we must
719+
* not use lower as hash key if this is a broken hardlink.
697720
*/
698721
is_dir = S_ISDIR(realinode->i_mode);
699-
if (is_dir && (indexed || !sb->s_export_op || !ofs->upper_mnt))
700-
origin = lowerdentry;
701-
702-
if (upperdentry || origin) {
703-
struct inode *key = d_inode(origin ?: upperdentry);
722+
if (upperdentry || bylower) {
723+
struct inode *key = d_inode(bylower ? lowerdentry :
724+
upperdentry);
704725
unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
705726

706727
inode = iget5_locked(sb, (unsigned long) key,
@@ -728,6 +749,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct dentry *upperdentry,
728749
nlink = ovl_get_nlink(lowerdentry, upperdentry, nlink);
729750
set_nlink(inode, nlink);
730751
} else {
752+
/* Lower hardlink that will be broken on copy up */
731753
inode = new_inode(sb);
732754
if (!inode)
733755
goto out_nomem;

0 commit comments

Comments
 (0)