diff mbox series

[6/6] autofs: manage dentry info mount trigger flags better

Message ID 165544415748.250070.14681426149656207251.stgit@donald.themaw.net (mailing list archive)
State New
Headers show
Series autofs: misc patches | expand

Commit Message

Ian Kent June 17, 2022, 5:35 a.m. UTC
The autofs managed dentry flags are left set for dentries in an
autofs mount directory regardless of whether the dentry should
trigger a mount (a non-empty directory or a symlink doesn't).

But properly managing these flags can sometimes provide the loop
termination condition needed when following mounts which now uses
an -EISDIR return.

Signed-off-by: Ian Kent <raven@themaw.net>
---
 fs/autofs/root.c |   97 +++++++++++++++++++++++-------------------------------
 1 file changed, 42 insertions(+), 55 deletions(-)
diff mbox series

Patch

diff --git a/fs/autofs/root.c b/fs/autofs/root.c
index ca03c1cae2be..d140d06c5bc6 100644
--- a/fs/autofs/root.c
+++ b/fs/autofs/root.c
@@ -529,9 +529,8 @@  static struct dentry *autofs_lookup(struct inode *dir,
 
 		spin_lock(&sbi->lookup_lock);
 		spin_lock(&dentry->d_lock);
-		/* Mark entries in the root as mount triggers */
-		if (IS_ROOT(dentry->d_parent) &&
-		    autofs_type_indirect(sbi->type))
+		/* Mark indirect mount entries as mount triggers */
+		if (autofs_type_indirect(sbi->type))
 			__managed_dentry_set_managed(dentry);
 		dentry->d_fsdata = ino;
 		ino->dentry = dentry;
@@ -567,7 +566,9 @@  static int autofs_dir_symlink(struct user_namespace *mnt_userns,
 			      struct inode *dir, struct dentry *dentry,
 			      const char *symname)
 {
+	struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
 	struct autofs_info *ino = autofs_dentry_ino(dentry);
+	struct dentry *parent = dentry->d_parent;
 	struct autofs_info *p_ino;
 	struct inode *inode;
 	size_t size = strlen(symname);
@@ -602,6 +603,16 @@  static int autofs_dir_symlink(struct user_namespace *mnt_userns,
 
 	dir->i_mtime = current_time(dir);
 
+	/* Symlinks don't trigger mounts */
+	managed_dentry_clear_managed(dentry);
+	/* Clear containing directory flags if it's no longer empty */
+	if (autofs_dentry_ino(parent)->count == 2) {
+		/* Don't set or clear type indirect root */
+		if (!IS_ROOT(parent) ||
+		    !autofs_type_indirect(sbi->type))
+			managed_dentry_clear_managed(parent);
+	}
+
 	return 0;
 }
 
@@ -624,12 +635,21 @@  static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
 {
 	struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
 	struct autofs_info *ino = autofs_dentry_ino(dentry);
+	struct dentry *parent = dentry->d_parent;
 	struct autofs_info *p_ino;
 
 	p_ino = autofs_dentry_ino(dentry->d_parent);
 	p_ino->count--;
 	dput(ino->dentry);
 
+	/* Set containing directory flags if it's now empty */
+	if (autofs_dentry_ino(parent)->count == 1) {
+		/* Don't set or clear type indirect root */
+		if (!IS_ROOT(parent) ||
+		    !autofs_type_indirect(sbi->type))
+			managed_dentry_set_managed(parent);
+	}
+
 	d_inode(dentry)->i_size = 0;
 	clear_nlink(d_inode(dentry));
 
@@ -643,56 +663,11 @@  static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
 	return 0;
 }
 
-/*
- * Version 4 of autofs provides a pseudo direct mount implementation
- * that relies on directories at the leaves of a directory tree under
- * an indirect mount to trigger mounts. To allow for this we need to
- * set the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags on the leaves
- * of the directory tree. There is no need to clear the automount flag
- * following a mount or restore it after an expire because these mounts
- * are always covered. However, it is necessary to ensure that these
- * flags are clear on non-empty directories to avoid unnecessary calls
- * during path walks.
- */
-static void autofs_set_leaf_automount_flags(struct dentry *dentry)
-{
-	struct dentry *parent;
-
-	/* root and dentrys in the root are already handled */
-	if (IS_ROOT(dentry->d_parent))
-		return;
-
-	managed_dentry_set_managed(dentry);
-
-	parent = dentry->d_parent;
-	/* only consider parents below dentrys in the root */
-	if (IS_ROOT(parent->d_parent))
-		return;
-	managed_dentry_clear_managed(parent);
-}
-
-static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
-{
-	struct dentry *parent;
-
-	/* flags for dentrys in the root are handled elsewhere */
-	if (IS_ROOT(dentry->d_parent))
-		return;
-
-	managed_dentry_clear_managed(dentry);
-
-	parent = dentry->d_parent;
-	/* only consider parents below dentrys in the root */
-	if (IS_ROOT(parent->d_parent))
-		return;
-	if (autofs_dentry_ino(parent)->count == 2)
-		managed_dentry_set_managed(parent);
-}
-
 static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
 	struct autofs_info *ino = autofs_dentry_ino(dentry);
+	struct dentry *parent = dentry->d_parent;
 	struct autofs_info *p_ino;
 
 	pr_debug("dentry %p, removing %pd\n", dentry, dentry);
@@ -705,15 +680,20 @@  static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
 	d_drop(dentry);
 	spin_unlock(&sbi->lookup_lock);
 
-	if (sbi->version < 5)
-		autofs_clear_leaf_automount_flags(dentry);
-
 	p_ino = autofs_dentry_ino(dentry->d_parent);
 	p_ino->count--;
 	dput(ino->dentry);
 	d_inode(dentry)->i_size = 0;
 	clear_nlink(d_inode(dentry));
 
+	/* Set containing directory flags if it's now empty */
+	if (autofs_dentry_ino(parent)->count == 1) {
+		/* Don't set or clear type indirect root */
+		if (!IS_ROOT(parent) ||
+		    !autofs_type_indirect(sbi->type))
+			managed_dentry_set_managed(parent);
+	}
+
 	if (dir->i_nlink)
 		drop_nlink(dir);
 
@@ -726,6 +706,7 @@  static int autofs_dir_mkdir(struct user_namespace *mnt_userns,
 {
 	struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
 	struct autofs_info *ino = autofs_dentry_ino(dentry);
+	struct dentry *parent = dentry->d_parent;
 	struct autofs_info *p_ino;
 	struct inode *inode;
 
@@ -742,15 +723,21 @@  static int autofs_dir_mkdir(struct user_namespace *mnt_userns,
 		return -ENOMEM;
 	d_add(dentry, inode);
 
-	if (sbi->version < 5)
-		autofs_set_leaf_automount_flags(dentry);
-
 	dget(dentry);
 	p_ino = autofs_dentry_ino(dentry->d_parent);
 	p_ino->count++;
 	inc_nlink(dir);
 	dir->i_mtime = current_time(dir);
 
+	managed_dentry_set_managed(dentry);
+	/* Clear containing directory flags if it's no longer empty */
+	if (autofs_dentry_ino(parent)->count == 2) {
+		/* Don't set or clear type indirect root */
+		if (!IS_ROOT(parent) ||
+		    !autofs_type_indirect(sbi->type))
+			managed_dentry_clear_managed(parent);
+	}
+
 	return 0;
 }