[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>
This commit is contained in:
Ian Kent 2006-03-27 01:14:54 -08:00 коммит произвёл Linus Torvalds
Родитель 051d381259
Коммит 34ca959cfc
3 изменённых файлов: 103 добавлений и 21 удалений

Просмотреть файл

@ -3,6 +3,7 @@
* linux/fs/autofs/autofs_i.h * linux/fs/autofs/autofs_i.h
* *
* Copyright 1997-1998 Transmeta Corporation - All Rights Reserved * 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 * 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 * 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_SBI_MAGIC 0x6d4a556d
#define AUTOFS_TYP_INDIRECT 0x0001
#define AUTOFS_TYP_DIRECT 0x0002
#define AUTOFS_TYP_OFFSET 0x0004
struct autofs_sb_info { struct autofs_sb_info {
u32 magic; u32 magic;
struct dentry *root; struct dentry *root;
@ -96,6 +101,7 @@ struct autofs_sb_info {
int min_proto; int min_proto;
int max_proto; int max_proto;
unsigned long exp_timeout; unsigned long exp_timeout;
unsigned int type;
int reghost_enabled; int reghost_enabled;
int needs_reghost; int needs_reghost;
struct super_block *sb; 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_symlink_inode_operations;
extern struct inode_operations autofs4_dir_inode_operations; extern struct inode_operations autofs4_dir_inode_operations;
extern struct inode_operations autofs4_root_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_dir_operations;
extern struct file_operations autofs4_root_operations; extern struct file_operations autofs4_root_operations;

Просмотреть файл

@ -3,6 +3,7 @@
* linux/fs/autofs/inode.c * linux/fs/autofs/inode.c
* *
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * 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 * 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 * 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, ",minproto=%d", sbi->min_proto);
seq_printf(m, ",maxproto=%d", sbi->max_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; return 0;
} }
@ -186,7 +194,8 @@ static struct super_operations autofs4_sops = {
.show_options = autofs4_show_options, .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 = { static match_table_t tokens = {
{Opt_fd, "fd=%u"}, {Opt_fd, "fd=%u"},
@ -195,11 +204,15 @@ static match_table_t tokens = {
{Opt_pgrp, "pgrp=%u"}, {Opt_pgrp, "pgrp=%u"},
{Opt_minproto, "minproto=%u"}, {Opt_minproto, "minproto=%u"},
{Opt_maxproto, "maxproto=%u"}, {Opt_maxproto, "maxproto=%u"},
{Opt_indirect, "indirect"},
{Opt_direct, "direct"},
{Opt_offset, "offset"},
{Opt_err, NULL} {Opt_err, NULL}
}; };
static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, 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; char *p;
substring_t args[MAX_OPT_ARGS]; 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; return 1;
*maxproto = option; *maxproto = option;
break; 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: default:
return 1; return 1;
} }
@ -271,6 +293,11 @@ static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
return ino; 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) int autofs4_fill_super(struct super_block *s, void *data, int silent)
{ {
struct inode * root_inode; struct inode * root_inode;
@ -297,6 +324,7 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
sbi->sb = s; sbi->sb = s;
sbi->version = 0; sbi->version = 0;
sbi->sub_version = 0; sbi->sub_version = 0;
sbi->type = 0;
sbi->min_proto = 0; sbi->min_proto = 0;
sbi->max_proto = 0; sbi->max_proto = 0;
mutex_init(&sbi->wq_mutex); mutex_init(&sbi->wq_mutex);
@ -315,27 +343,31 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
if (!ino) if (!ino)
goto fail_free; goto fail_free;
root_inode = autofs4_get_inode(s, ino); root_inode = autofs4_get_inode(s, ino);
kfree(ino);
if (!root_inode) 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); root = d_alloc_root(root_inode);
pipe = NULL;
if (!root) if (!root)
goto fail_iput; goto fail_iput;
pipe = NULL;
root->d_op = &autofs4_sb_dentry_operations;
root->d_fsdata = ino;
/* Can this call block? */ /* Can this call block? */
if (parse_options(data, &pipefd, if (parse_options(data, &pipefd,
&root_inode->i_uid, &root_inode->i_gid, &root_inode->i_uid, &root_inode->i_gid,
&sbi->oz_pgrp, &sbi->oz_pgrp, &sbi->type,
&sbi->min_proto, &sbi->max_proto)) { &sbi->min_proto, &sbi->max_proto)) {
printk("autofs: called with bogus options\n"); printk("autofs: called with bogus options\n");
goto fail_dput; 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? */ /* Couldn't this be tested earlier? */
if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION || if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) { sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
@ -391,6 +423,8 @@ fail_dput:
fail_iput: fail_iput:
printk("autofs: get root dentry failed\n"); printk("autofs: get root dentry failed\n");
iput(root_inode); iput(root_inode);
fail_ino:
kfree(ino);
fail_free: fail_free:
kfree(sbi); kfree(sbi);
fail_unlock: fail_unlock:

Просмотреть файл

@ -4,7 +4,7 @@
* *
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org> * 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 * 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 * 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_dir_readdir(struct file * filp, void * dirent, filldir_t filldir);
static int autofs4_root_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 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 = { struct file_operations autofs4_root_operations = {
.open = dcache_dir_open, .open = dcache_dir_open,
@ -46,7 +47,7 @@ struct file_operations autofs4_dir_operations = {
.readdir = autofs4_dir_readdir, .readdir = autofs4_dir_readdir,
}; };
struct inode_operations autofs4_root_inode_operations = { struct inode_operations autofs4_indirect_root_inode_operations = {
.lookup = autofs4_lookup, .lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink, .unlink = autofs4_dir_unlink,
.symlink = autofs4_dir_symlink, .symlink = autofs4_dir_symlink,
@ -54,6 +55,11 @@ struct inode_operations autofs4_root_inode_operations = {
.rmdir = autofs4_dir_rmdir, .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 = { struct inode_operations autofs4_dir_inode_operations = {
.lookup = autofs4_lookup, .lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink, .unlink = autofs4_dir_unlink,
@ -252,7 +258,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags)
*/ */
status = d_invalidate(dentry); status = d_invalidate(dentry);
if (status != -EBUSY) if (status != -EBUSY)
return 0; return -ENOENT;
} }
DPRINTK("dentry=%p %.*s ino=%p", 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); DPRINTK("mount done status=%d", status);
if (status && dentry->d_inode) 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? */ /* Turn this into a real negative dentry? */
if (status == -ENOENT) { if (status == -ENOENT) {
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
return 0; return status;
} else if (status) { } else if (status) {
/* Return a negative dentry, but leave it "pending" */ /* Return a negative dentry, but leave it "pending" */
return 0; return status;
} }
/* Trigger mount for path component or follow link */ /* Trigger mount for path component or follow link */
} else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || } 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); spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock); 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); spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock); 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); struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
int oz_mode = autofs4_oz_mode(sbi); int oz_mode = autofs4_oz_mode(sbi);
int flags = nd ? nd->flags : 0; int flags = nd ? nd->flags : 0;
int status = 1; int status = 0;
/* Pending dentry */ /* Pending dentry */
if (autofs4_ispending(dentry)) { if (autofs4_ispending(dentry)) {
if (!oz_mode) if (!oz_mode)
status = try_to_fill_dentry(dentry, flags); status = try_to_fill_dentry(dentry, flags);
return status; return !status;
} }
/* Negative dentry.. invalidate if "old" */ /* Negative dentry.. invalidate if "old" */
@ -349,14 +389,14 @@ static int autofs4_revalidate(struct dentry *dentry, struct nameidata *nd)
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
if (!oz_mode) if (!oz_mode)
status = try_to_fill_dentry(dentry, flags); status = try_to_fill_dentry(dentry, flags);
return status; return !status;
} }
spin_unlock(&dcache_lock); spin_unlock(&dcache_lock);
return 1; return 1;
} }
static void autofs4_dentry_release(struct dentry *de) void autofs4_dentry_release(struct dentry *de)
{ {
struct autofs_info *inf; struct autofs_info *inf;