Re: [PATCH v2 1/2] fsnotify: invalidate dcache before IN_DELETE event

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu 20-01-22 23:53:04, Amir Goldstein wrote:
> Apparently, there are some applications that use IN_DELETE event as an
> invalidation mechanism and expect that if they try to open a file with
> the name reported with the delete event, that it should not contain the
> content of the deleted file.
> 
> Commit 49246466a989 ("fsnotify: move fsnotify_nameremove() hook out of
> d_delete()") moved the fsnotify delete hook before d_delete() so fsnotify
> will have access to a positive dentry.
> 
> This allowed a race where opening the deleted file via cached dentry
> is now possible after receiving the IN_DELETE event.
> 
> To fix the regression, create a new hook fsnotify_delete() that takes
> the unlinked inode as an argument and use a helper d_delete_notify() to
> pin the inode, so we can pass it to fsnotify_delete() after d_delete().
> 
> Backporting hint: this regression is from v5.3. Although patch will
> apply with only trivial conflicts to v5.4 and v5.10, it won't build,
> because fsnotify_delete() implementation is different in each of those
> versions (see fsnotify_link()).
> 
> A follow up patch will fix the fsnotify_unlink/rmdir() calls in pseudo
> filesystem that do not need to call d_delete().
> 
> Reported-by: Ivan Delalande <colona@xxxxxxxxxx>
> Link: https://lore.kernel.org/linux-fsdevel/YeNyzoDM5hP5LtGW@visor/
> Fixes: 49246466a989 ("fsnotify: move fsnotify_nameremove() hook out of d_delete()")
> Cc: stable@xxxxxxxxxxxxxxx # v5.3+
> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>

Thanks. Both patches look good to me now, I've merged them into my tree and
will push them to Linus later this week.

								Honza

> ---
> 
> Changes since v1:
> - Split patch for pseudo filesystem (Jan)
> - Fix logic change for DCACHE_NFSFS_RENAMED (Jan)
> - btrfs also needs d_delete_notify()
> - Make fsnotify_unlink/rmdir() wrappers of fsnotify_delete()
> - FS_DELETE event always uses FSNOTIFY_EVENT_INODE data_type
> 
> 
>  fs/btrfs/ioctl.c         |  6 ++---
>  fs/namei.c               | 10 ++++----
>  include/linux/fsnotify.h | 49 +++++++++++++++++++++++++++++++++++-----
>  3 files changed, 50 insertions(+), 15 deletions(-)
> 
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index edfecfe62b4b..a5ee6ffeadf5 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -3060,10 +3060,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
>  	btrfs_inode_lock(inode, 0);
>  	err = btrfs_delete_subvolume(dir, dentry);
>  	btrfs_inode_unlock(inode, 0);
> -	if (!err) {
> -		fsnotify_rmdir(dir, dentry);
> -		d_delete(dentry);
> -	}
> +	if (!err)
> +		d_delete_notify(dir, dentry);
>  
>  out_dput:
>  	dput(dentry);
> diff --git a/fs/namei.c b/fs/namei.c
> index 1f9d2187c765..3c0568d3155b 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -3973,13 +3973,12 @@ int vfs_rmdir(struct user_namespace *mnt_userns, struct inode *dir,
>  	dentry->d_inode->i_flags |= S_DEAD;
>  	dont_mount(dentry);
>  	detach_mounts(dentry);
> -	fsnotify_rmdir(dir, dentry);
>  
>  out:
>  	inode_unlock(dentry->d_inode);
>  	dput(dentry);
>  	if (!error)
> -		d_delete(dentry);
> +		d_delete_notify(dir, dentry);
>  	return error;
>  }
>  EXPORT_SYMBOL(vfs_rmdir);
> @@ -4101,7 +4100,6 @@ int vfs_unlink(struct user_namespace *mnt_userns, struct inode *dir,
>  			if (!error) {
>  				dont_mount(dentry);
>  				detach_mounts(dentry);
> -				fsnotify_unlink(dir, dentry);
>  			}
>  		}
>  	}
> @@ -4109,9 +4107,11 @@ int vfs_unlink(struct user_namespace *mnt_userns, struct inode *dir,
>  	inode_unlock(target);
>  
>  	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
> -	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
> +	if (!error && dentry->d_flags & DCACHE_NFSFS_RENAMED) {
> +		fsnotify_unlink(dir, dentry);
> +	} else if (!error) {
>  		fsnotify_link_count(target);
> -		d_delete(dentry);
> +		d_delete_notify(dir, dentry);
>  	}
>  
>  	return error;
> diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
> index 3a2d7dc3c607..bb8467cd11ae 100644
> --- a/include/linux/fsnotify.h
> +++ b/include/linux/fsnotify.h
> @@ -224,6 +224,43 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode,
>  		      dir, &new_dentry->d_name, 0);
>  }
>  
> +/*
> + * fsnotify_delete - @dentry was unlinked and unhashed
> + *
> + * Caller must make sure that dentry->d_name is stable.
> + *
> + * Note: unlike fsnotify_unlink(), we have to pass also the unlinked inode
> + * as this may be called after d_delete() and old_dentry may be negative.
> + */
> +static inline void fsnotify_delete(struct inode *dir, struct inode *inode,
> +				   struct dentry *dentry)
> +{
> +	__u32 mask = FS_DELETE;
> +
> +	if (S_ISDIR(inode->i_mode))
> +		mask |= FS_ISDIR;
> +
> +	fsnotify_name(mask, inode, FSNOTIFY_EVENT_INODE, dir, &dentry->d_name,
> +		      0);
> +}
> +
> +/**
> + * d_delete_notify - delete a dentry and call fsnotify_delete()
> + * @dentry: The dentry to delete
> + *
> + * This helper is used to guaranty that the unlinked inode cannot be found
> + * by lookup of this name after fsnotify_delete() event has been delivered.
> + */
> +static inline void d_delete_notify(struct inode *dir, struct dentry *dentry)
> +{
> +	struct inode *inode = d_inode(dentry);
> +
> +	ihold(inode);
> +	d_delete(dentry);
> +	fsnotify_delete(dir, inode, dentry);
> +	iput(inode);
> +}
> +
>  /*
>   * fsnotify_unlink - 'name' was unlinked
>   *
> @@ -231,10 +268,10 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode,
>   */
>  static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry)
>  {
> -	/* Expected to be called before d_delete() */
> -	WARN_ON_ONCE(d_is_negative(dentry));
> +	if (WARN_ON_ONCE(d_is_negative(dentry)))
> +		return;
>  
> -	fsnotify_dirent(dir, dentry, FS_DELETE);
> +	fsnotify_delete(dir, d_inode(dentry), dentry);
>  }
>  
>  /*
> @@ -258,10 +295,10 @@ static inline void fsnotify_mkdir(struct inode *dir, struct dentry *dentry)
>   */
>  static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry)
>  {
> -	/* Expected to be called before d_delete() */
> -	WARN_ON_ONCE(d_is_negative(dentry));
> +	if (WARN_ON_ONCE(d_is_negative(dentry)))
> +		return;
>  
> -	fsnotify_dirent(dir, dentry, FS_DELETE | FS_ISDIR);
> +	fsnotify_delete(dir, d_inode(dentry), dentry);
>  }
>  
>  /*
> -- 
> 2.34.1
> 
-- 
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR



[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux