[v2,02/14] fs: create simple_remove() helper
diff mbox series

Message ID 20190516102641.6574-3-amir73il@gmail.com
State New
Headers show
Series
  • Sort out fsnotify_nameremove() mess
Related show

Commit Message

Amir Goldstein May 16, 2019, 10:26 a.m. UTC
There is a common pattern among pseudo filesystems for removing a dentry
from code paths that are NOT coming from vfs_{unlink,rmdir}, using a
combination of simple_{unlink,rmdir} and d_delete().

Create an helper to perform this common operation.  This helper is going
to be used as a place holder for the new fsnotify_{unlink,rmdir} hooks.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/libfs.c         | 27 +++++++++++++++++++++++++++
 include/linux/fs.h |  1 +
 2 files changed, 28 insertions(+)

Comments

Al Viro May 16, 2019, 5:07 p.m. UTC | #1
On Thu, May 16, 2019 at 01:26:29PM +0300, Amir Goldstein wrote:
> There is a common pattern among pseudo filesystems for removing a dentry
> from code paths that are NOT coming from vfs_{unlink,rmdir}, using a
> combination of simple_{unlink,rmdir} and d_delete().
> 
> Create an helper to perform this common operation.  This helper is going
> to be used as a place holder for the new fsnotify_{unlink,rmdir} hooks.

This is the wrong approach.  What we have is a bunch of places trying
to implement recursive removal of a subtree.  They are broken, each in
its own way, and I'm not talking about fsnotify crap - there are
much more unpleasant issues there.

Trying to untangle that mess is not going to be made easier by mandating
the calls of that helper of yours - if anything, it makes the whole
thing harder to massage.

It needs to be dealt with, no arguments here, but that's not a good
starting point for that...  I've taken several stabs at that, never
got anywhere satisfactory with those ;-/  I'll try to dig out the
notes/existing attempts at patch series; if you are willing to participate
in discussing those and sorting the whole thing out, you are very welcome;
just ping me in a couple of days.
Amir Goldstein May 16, 2019, 6:42 p.m. UTC | #2
On Thu, May 16, 2019 at 8:07 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Thu, May 16, 2019 at 01:26:29PM +0300, Amir Goldstein wrote:
> > There is a common pattern among pseudo filesystems for removing a dentry
> > from code paths that are NOT coming from vfs_{unlink,rmdir}, using a
> > combination of simple_{unlink,rmdir} and d_delete().
> >
> > Create an helper to perform this common operation.  This helper is going
> > to be used as a place holder for the new fsnotify_{unlink,rmdir} hooks.
>
> This is the wrong approach.  What we have is a bunch of places trying
> to implement recursive removal of a subtree.  They are broken, each in
> its own way, and I'm not talking about fsnotify crap - there are
> much more unpleasant issues there.
>
> Trying to untangle that mess is not going to be made easier by mandating
> the calls of that helper of yours - if anything, it makes the whole
> thing harder to massage.
>
> It needs to be dealt with, no arguments here, but that's not a good
> starting point for that...  I've taken several stabs at that, never
> got anywhere satisfactory with those ;-/  I'll try to dig out the
> notes/existing attempts at patch series; if you are willing to participate
> in discussing those and sorting the whole thing out, you are very welcome;
> just ping me in a couple of days.

Will do.

Thanks,
Amir.

Patch
diff mbox series

diff --git a/fs/libfs.c b/fs/libfs.c
index 4b59b1816efb..ca1132f1d5c6 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -353,6 +353,33 @@  int simple_rmdir(struct inode *dir, struct dentry *dentry)
 }
 EXPORT_SYMBOL(simple_rmdir);
 
+/*
+ * Unlike simple_unlink/rmdir, this helper is NOT called from vfs_unlink/rmdir.
+ * Caller must guaranty that d_parent and d_name are stable.
+ */
+int simple_remove(struct inode *dir, struct dentry *dentry)
+{
+	int ret;
+
+	/*
+	 * 'simple_' operations get a dentry reference on create/mkdir and drop
+	 * it on unlink/rmdir. So we have to get dentry reference here to
+	 * protect d_delete() from accessing a freed dentry.
+	 */
+	dget(dentry);
+	if (d_is_dir(dentry))
+		ret = simple_rmdir(dir, dentry);
+	else
+		ret = simple_unlink(dir, dentry);
+
+	if (!ret)
+		d_delete(dentry);
+	dput(dentry);
+
+	return ret;
+}
+EXPORT_SYMBOL(simple_remove);
+
 int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
 		  struct inode *new_dir, struct dentry *new_dentry,
 		  unsigned int flags)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..74ea5f0b3b9d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3245,6 +3245,7 @@  extern int simple_open(struct inode *inode, struct file *file);
 extern int simple_link(struct dentry *, struct inode *, struct dentry *);
 extern int simple_unlink(struct inode *, struct dentry *);
 extern int simple_rmdir(struct inode *, struct dentry *);
+extern int simple_remove(struct inode *, struct dentry *);
 extern int simple_rename(struct inode *, struct dentry *,
 			 struct inode *, struct dentry *, unsigned int);
 extern int noop_fsync(struct file *, loff_t, loff_t, int);