summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Kent <raven@themaw.net>2006-03-27 13:14:54 +0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-27 20:44:40 +0400
commit34ca959cfc15cf09ad4da4f31ab034691e51af78 (patch)
tree83bbe0a2b94ca58f39c65b1e605f44d421b9564b
parent051d381259eb57d6074d02a6ba6e90e744f1a29f (diff)
downloadlinux-34ca959cfc15cf09ad4da4f31ab034691e51af78.tar.xz
[PATCH] autofs4: add v5 follow_link mount trigger method
This patch adds a follow_link inode method for the root of an autofs direct mount trigger. It also adds the corresponding mount options and updates the show_mount method. Signed-off-by: Ian Kent <raven@themaw.net> Cc: Al Viro <viro@ftp.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/autofs4/autofs_i.h8
-rw-r--r--fs/autofs4/inode.c52
-rw-r--r--fs/autofs4/root.c64
3 files changed, 103 insertions, 21 deletions
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
index bc1b0543d2b6..ed388a1d8fc4 100644
--- a/fs/autofs4/autofs_i.h
+++ b/fs/autofs4/autofs_i.h
@@ -3,6 +3,7 @@
* linux/fs/autofs/autofs_i.h
*
* Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
+ * Copyright 2005-2006 Ian Kent <raven@themaw.net>
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -84,6 +85,10 @@ struct autofs_wait_queue {
#define AUTOFS_SBI_MAGIC 0x6d4a556d
+#define AUTOFS_TYP_INDIRECT 0x0001
+#define AUTOFS_TYP_DIRECT 0x0002
+#define AUTOFS_TYP_OFFSET 0x0004
+
struct autofs_sb_info {
u32 magic;
struct dentry *root;
@@ -96,6 +101,7 @@ struct autofs_sb_info {
int min_proto;
int max_proto;
unsigned long exp_timeout;
+ unsigned int type;
int reghost_enabled;
int needs_reghost;
struct super_block *sb;
@@ -162,6 +168,8 @@ int autofs4_expire_multi(struct super_block *, struct vfsmount *,
extern struct inode_operations autofs4_symlink_inode_operations;
extern struct inode_operations autofs4_dir_inode_operations;
extern struct inode_operations autofs4_root_inode_operations;
+extern struct inode_operations autofs4_indirect_root_inode_operations;
+extern struct inode_operations autofs4_direct_root_inode_operations;
extern struct file_operations autofs4_dir_operations;
extern struct file_operations autofs4_root_operations;
diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
index d9a71dab40fc..3801bed94e45 100644
--- a/fs/autofs4/inode.c
+++ b/fs/autofs4/inode.c
@@ -3,6 +3,7 @@
* linux/fs/autofs/inode.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 2005-2006 Ian Kent <raven@themaw.net>
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -177,6 +178,13 @@ static int autofs4_show_options(struct seq_file *m, struct vfsmount *mnt)
seq_printf(m, ",minproto=%d", sbi->min_proto);
seq_printf(m, ",maxproto=%d", sbi->max_proto);
+ if (sbi->type & AUTOFS_TYP_OFFSET)
+ seq_printf(m, ",offset");
+ else if (sbi->type & AUTOFS_TYP_DIRECT)
+ seq_printf(m, ",direct");
+ else
+ seq_printf(m, ",indirect");
+
return 0;
}
@@ -186,7 +194,8 @@ static struct super_operations autofs4_sops = {
.show_options = autofs4_show_options,
};
-enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto};
+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto,
+ Opt_indirect, Opt_direct, Opt_offset};
static match_table_t tokens = {
{Opt_fd, "fd=%u"},
@@ -195,11 +204,15 @@ static match_table_t tokens = {
{Opt_pgrp, "pgrp=%u"},
{Opt_minproto, "minproto=%u"},
{Opt_maxproto, "maxproto=%u"},
+ {Opt_indirect, "indirect"},
+ {Opt_direct, "direct"},
+ {Opt_offset, "offset"},
{Opt_err, NULL}
};
static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
- pid_t *pgrp, int *minproto, int *maxproto)
+ pid_t *pgrp, unsigned int *type,
+ int *minproto, int *maxproto)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -253,6 +266,15 @@ static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
return 1;
*maxproto = option;
break;
+ case Opt_indirect:
+ *type = AUTOFS_TYP_INDIRECT;
+ break;
+ case Opt_direct:
+ *type = AUTOFS_TYP_DIRECT;
+ break;
+ case Opt_offset:
+ *type = AUTOFS_TYP_DIRECT | AUTOFS_TYP_OFFSET;
+ break;
default:
return 1;
}
@@ -271,6 +293,11 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
return ino;
}
+void autofs4_dentry_release(struct dentry *);
+static struct dentry_operations autofs4_sb_dentry_operations = {
+ .d_release = autofs4_dentry_release,
+};
+
int autofs4_fill_super(struct super_block *s, void *data, int silent)
{
struct inode * root_inode;
@@ -297,6 +324,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
sbi->sb = s;
sbi->version = 0;
sbi->sub_version = 0;
+ sbi->type = 0;
sbi->min_proto = 0;
sbi->max_proto = 0;
mutex_init(&sbi->wq_mutex);
@@ -315,27 +343,31 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
if (!ino)
goto fail_free;
root_inode = autofs4_get_inode(s, ino);
- kfree(ino);
if (!root_inode)
- goto fail_free;
+ goto fail_ino;
- root_inode->i_op = &autofs4_root_inode_operations;
- root_inode->i_fop = &autofs4_root_operations;
root = d_alloc_root(root_inode);
- pipe = NULL;
-
if (!root)
goto fail_iput;
+ pipe = NULL;
+
+ root->d_op = &autofs4_sb_dentry_operations;
+ root->d_fsdata = ino;
/* Can this call block? */
if (parse_options(data, &pipefd,
&root_inode->i_uid, &root_inode->i_gid,
- &sbi->oz_pgrp,
+ &sbi->oz_pgrp, &sbi->type,
&sbi->min_proto, &sbi->max_proto)) {
printk("autofs: called with bogus options\n");
goto fail_dput;
}
+ root_inode->i_fop = &autofs4_root_operations;
+ root_inode->i_op = sbi->type & AUTOFS_TYP_DIRECT ?
+ &autofs4_direct_root_inode_operations :
+ &autofs4_indirect_root_inode_operations;
+
/* Couldn't this be tested earlier? */
if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
@@ -391,6 +423,8 @@ fail_dput:
fail_iput:
printk("autofs: get root dentry failed\n");
iput(root_inode);
+fail_ino:
+ kfree(ino);
fail_free:
kfree(sbi);
fail_unlock:
diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
index 26eb1f024866..3f0048582248 100644
--- a/fs/autofs4/root.c
+++ b/fs/autofs4/root.c
@@ -4,7 +4,7 @@
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
- * Copyright 2001-2003 Ian Kent <raven@themaw.net>
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
*
* This file is part of the Linux kernel and is made available under
* the terms of the GNU General Public License, version 2, or at your
@@ -30,6 +30,7 @@ static int autofs4_dir_close(struct inode *inode, struct file *file);
static int autofs4_dir_readdir(struct file * filp, void * dirent, filldir_t filldir);
static int autofs4_root_readdir(struct file * filp, void * dirent, filldir_t filldir);
static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *);
+static void *autofs4_follow_link(struct dentry *, struct nameidata *);
struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
@@ -46,7 +47,7 @@ struct file_operations autofs4_dir_operations = {
.readdir = autofs4_dir_readdir,
};
-struct inode_operations autofs4_root_inode_operations = {
+struct inode_operations autofs4_indirect_root_inode_operations = {
.lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink,
.symlink = autofs4_dir_symlink,
@@ -54,6 +55,11 @@ struct inode_operations autofs4_root_inode_operations = {
.rmdir = autofs4_dir_rmdir,
};
+struct inode_operations autofs4_direct_root_inode_operations = {
+ .lookup = autofs4_lookup,
+ .follow_link = autofs4_follow_link,
+};
+
struct inode_operations autofs4_dir_inode_operations = {
.lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink,
@@ -252,7 +258,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
*/
status = d_invalidate(dentry);
if (status != -EBUSY)
- return 0;
+ return -ENOENT;
}
DPRINTK("dentry=%p %.*s ino=%p",
@@ -271,17 +277,17 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
DPRINTK("mount done status=%d", status);
if (status && dentry->d_inode)
- return 0; /* Try to get the kernel to invalidate this dentry */
+ return status; /* Try to get the kernel to invalidate this dentry */
/* Turn this into a real negative dentry? */
if (status == -ENOENT) {
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 0;
+ return status;
} else if (status) {
/* Return a negative dentry, but leave it "pending" */
- return 0;
+ return status;
}
/* Trigger mount for path component or follow link */
} else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
@@ -300,7 +306,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 0;
+ return status;
}
}
@@ -311,7 +317,41 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
- return 1;
+ return status;
+}
+
+/* For autofs direct mounts the follow link triggers the mount */
+static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+ struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
+ int oz_mode = autofs4_oz_mode(sbi);
+ unsigned int lookup_type;
+ int status;
+
+ DPRINTK("dentry=%p %.*s oz_mode=%d nd->flags=%d",
+ dentry, dentry->d_name.len, dentry->d_name.name, oz_mode,
+ nd->flags);
+
+ /* If it's our master or we shouldn't trigger a mount we're done */
+ lookup_type = nd->flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY);
+ if (oz_mode || !lookup_type)
+ goto done;
+
+ status = try_to_fill_dentry(dentry, 0);
+ if (status)
+ goto out_error;
+
+ if (!autofs4_follow_mount(&nd->mnt, &nd->dentry)) {
+ status = -ENOENT;
+ goto out_error;
+ }
+
+done:
+ return NULL;
+
+out_error:
+ path_release(nd);
+ return ERR_PTR(status);
}
/*
@@ -326,13 +366,13 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
int oz_mode = autofs4_oz_mode(sbi);
int flags = nd ? nd->flags : 0;
- int status = 1;
+ int status = 0;
/* Pending dentry */
if (autofs4_ispending(dentry)) {
if (!oz_mode)
status = try_to_fill_dentry(dentry, flags);
- return status;
+ return !status;
}
/* Negative dentry.. invalidate if "old" */
@@ -349,14 +389,14 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
spin_unlock(&dcache_lock);
if (!oz_mode)
status = try_to_fill_dentry(dentry, flags);
- return status;
+ return !status;
}
spin_unlock(&dcache_lock);
return 1;
}
-static void autofs4_dentry_release(struct dentry *de)
+void autofs4_dentry_release(struct dentry *de)
{
struct autofs_info *inf;