Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

7
fs/autofs4/Makefile Normal file
View File

@@ -0,0 +1,7 @@
#
# Makefile for the linux autofs-filesystem routines.
#
obj-$(CONFIG_AUTOFS4_FS) += autofs4.o
autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o

193
fs/autofs4/autofs_i.h Normal file
View File

@@ -0,0 +1,193 @@
/* -*- c -*- ------------------------------------------------------------- *
*
* linux/fs/autofs/autofs_i.h
*
* Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
*
* 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
* option, any later version, incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
/* Internal header file for autofs */
#include <linux/auto_fs4.h>
#include <linux/list.h>
/* This is the range of ioctl() numbers we claim as ours */
#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
#define AUTOFS_IOC_COUNT 32
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <asm/current.h>
#include <asm/uaccess.h>
/* #define DEBUG */
#ifdef DEBUG
#define DPRINTK(fmt,args...) do { printk(KERN_DEBUG "pid %d: %s: " fmt "\n" , current->pid , __FUNCTION__ , ##args); } while(0)
#else
#define DPRINTK(fmt,args...) do {} while(0)
#endif
#define AUTOFS_SUPER_MAGIC 0x0187
/*
* If the daemon returns a negative response (AUTOFS_IOC_FAIL) then the
* kernel will keep the negative response cached for up to the time given
* here, although the time can be shorter if the kernel throws the dcache
* entry away. This probably should be settable from user space.
*/
#define AUTOFS_NEGATIVE_TIMEOUT (60*HZ) /* 1 minute */
/* Unified info structure. This is pointed to by both the dentry and
inode structures. Each file in the filesystem has an instance of this
structure. It holds a reference to the dentry, so dentries are never
flushed while the file exists. All name lookups are dealt with at the
dentry level, although the filesystem can interfere in the validation
process. Readdir is implemented by traversing the dentry lists. */
struct autofs_info {
struct dentry *dentry;
struct inode *inode;
int flags;
struct autofs_sb_info *sbi;
unsigned long last_used;
mode_t mode;
size_t size;
void (*free)(struct autofs_info *);
union {
const char *symlink;
} u;
};
#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */
struct autofs_wait_queue {
wait_queue_head_t queue;
struct autofs_wait_queue *next;
autofs_wqt_t wait_queue_token;
/* We use the following to see what we are waiting for */
int hash;
int len;
char *name;
/* This is for status reporting upon return */
int status;
atomic_t wait_ctr;
};
#define AUTOFS_SBI_MAGIC 0x6d4a556d
struct autofs_sb_info {
u32 magic;
struct file *pipe;
pid_t oz_pgrp;
int catatonic;
int version;
int sub_version;
unsigned long exp_timeout;
int reghost_enabled;
int needs_reghost;
struct super_block *sb;
struct semaphore wq_sem;
struct autofs_wait_queue *queues; /* Wait queue pointer */
};
static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
{
return (struct autofs_sb_info *)(sb->s_fs_info);
}
static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry)
{
return (struct autofs_info *)(dentry->d_fsdata);
}
/* autofs4_oz_mode(): do we see the man behind the curtain? (The
processes which do manipulations for us in user space sees the raw
filesystem without "magic".) */
static inline int autofs4_oz_mode(struct autofs_sb_info *sbi) {
return sbi->catatonic || process_group(current) == sbi->oz_pgrp;
}
/* Does a dentry have some pending activity? */
static inline int autofs4_ispending(struct dentry *dentry)
{
struct autofs_info *inf = autofs4_dentry_ino(dentry);
return (dentry->d_flags & DCACHE_AUTOFS_PENDING) ||
(inf != NULL && inf->flags & AUTOFS_INF_EXPIRING);
}
static inline void autofs4_copy_atime(struct file *src, struct file *dst)
{
dst->f_dentry->d_inode->i_atime = src->f_dentry->d_inode->i_atime;
return;
}
struct inode *autofs4_get_inode(struct super_block *, struct autofs_info *);
void autofs4_free_ino(struct autofs_info *);
/* Expiration */
int is_autofs4_dentry(struct dentry *);
int autofs4_expire_run(struct super_block *, struct vfsmount *,
struct autofs_sb_info *,
struct autofs_packet_expire __user *);
int autofs4_expire_multi(struct super_block *, struct vfsmount *,
struct autofs_sb_info *, int __user *);
/* Operations structures */
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 file_operations autofs4_dir_operations;
extern struct file_operations autofs4_root_operations;
/* Initializing function */
int autofs4_fill_super(struct super_block *, void *, int);
struct autofs_info *autofs4_init_ino(struct autofs_info *, struct autofs_sb_info *sbi, mode_t mode);
/* Queue management functions */
enum autofs_notify
{
NFY_NONE,
NFY_MOUNT,
NFY_EXPIRE
};
int autofs4_wait(struct autofs_sb_info *,struct dentry *, enum autofs_notify);
int autofs4_wait_release(struct autofs_sb_info *,autofs_wqt_t,int);
void autofs4_catatonic_mode(struct autofs_sb_info *);
static inline int simple_positive(struct dentry *dentry)
{
return dentry->d_inode && !d_unhashed(dentry);
}
static inline int simple_empty_nolock(struct dentry *dentry)
{
struct dentry *child;
int ret = 0;
list_for_each_entry(child, &dentry->d_subdirs, d_child)
if (simple_positive(child))
goto out;
ret = 1;
out:
return ret;
}

358
fs/autofs4/expire.c Normal file
View File

@@ -0,0 +1,358 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/expire.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
* Copyright 2001-2003 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include "autofs_i.h"
static unsigned long now;
/* Check if a dentry can be expired return 1 if it can else return 0 */
static inline int autofs4_can_expire(struct dentry *dentry,
unsigned long timeout, int do_now)
{
struct autofs_info *ino = autofs4_dentry_ino(dentry);
/* dentry in the process of being deleted */
if (ino == NULL)
return 0;
/* No point expiring a pending mount */
if (dentry->d_flags & DCACHE_AUTOFS_PENDING)
return 0;
if (!do_now) {
/* Too young to die */
if (time_after(ino->last_used + timeout, now))
return 0;
/* update last_used here :-
- obviously makes sense if it is in use now
- less obviously, prevents rapid-fire expire
attempts if expire fails the first time */
ino->last_used = now;
}
return 1;
}
/* Check a mount point for busyness return 1 if not busy, otherwise */
static int autofs4_check_mount(struct vfsmount *mnt, struct dentry *dentry)
{
int status = 0;
DPRINTK("dentry %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
mntget(mnt);
dget(dentry);
if (!follow_down(&mnt, &dentry))
goto done;
while (d_mountpoint(dentry) && follow_down(&mnt, &dentry))
;
/* This is an autofs submount, we can't expire it */
if (is_autofs4_dentry(dentry))
goto done;
/* The big question */
if (may_umount_tree(mnt) == 0)
status = 1;
done:
DPRINTK("returning = %d", status);
mntput(mnt);
dput(dentry);
return status;
}
/* Check a directory tree of mount points for busyness
* The tree is not busy iff no mountpoints are busy
* Return 1 if the tree is busy or 0 otherwise
*/
static int autofs4_check_tree(struct vfsmount *mnt,
struct dentry *top,
unsigned long timeout,
int do_now)
{
struct dentry *this_parent = top;
struct list_head *next;
DPRINTK("parent %p %.*s",
top, (int)top->d_name.len, top->d_name.name);
/* Negative dentry - give up */
if (!simple_positive(top))
return 0;
/* Timeout of a tree mount is determined by its top dentry */
if (!autofs4_can_expire(top, timeout, do_now))
return 0;
spin_lock(&dcache_lock);
repeat:
next = this_parent->d_subdirs.next;
resume:
while (next != &this_parent->d_subdirs) {
struct dentry *dentry = list_entry(next, struct dentry, d_child);
/* Negative dentry - give up */
if (!simple_positive(dentry)) {
next = next->next;
continue;
}
DPRINTK("dentry %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
if (!simple_empty_nolock(dentry)) {
this_parent = dentry;
goto repeat;
}
dentry = dget(dentry);
spin_unlock(&dcache_lock);
if (d_mountpoint(dentry)) {
/* First busy => tree busy */
if (!autofs4_check_mount(mnt, dentry)) {
dput(dentry);
return 0;
}
}
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
}
if (this_parent != top) {
next = this_parent->d_child.next;
this_parent = this_parent->d_parent;
goto resume;
}
spin_unlock(&dcache_lock);
return 1;
}
static struct dentry *autofs4_check_leaves(struct vfsmount *mnt,
struct dentry *parent,
unsigned long timeout,
int do_now)
{
struct dentry *this_parent = parent;
struct list_head *next;
DPRINTK("parent %p %.*s",
parent, (int)parent->d_name.len, parent->d_name.name);
spin_lock(&dcache_lock);
repeat:
next = this_parent->d_subdirs.next;
resume:
while (next != &this_parent->d_subdirs) {
struct dentry *dentry = list_entry(next, struct dentry, d_child);
/* Negative dentry - give up */
if (!simple_positive(dentry)) {
next = next->next;
continue;
}
DPRINTK("dentry %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
if (!list_empty(&dentry->d_subdirs)) {
this_parent = dentry;
goto repeat;
}
dentry = dget(dentry);
spin_unlock(&dcache_lock);
if (d_mountpoint(dentry)) {
/* Can we expire this guy */
if (!autofs4_can_expire(dentry, timeout, do_now))
goto cont;
/* Can we umount this guy */
if (autofs4_check_mount(mnt, dentry))
return dentry;
}
cont:
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
}
if (this_parent != parent) {
next = this_parent->d_child.next;
this_parent = this_parent->d_parent;
goto resume;
}
spin_unlock(&dcache_lock);
return NULL;
}
/*
* Find an eligible tree to time-out
* A tree is eligible if :-
* - it is unused by any user process
* - it has been unused for exp_timeout time
*/
static struct dentry *autofs4_expire(struct super_block *sb,
struct vfsmount *mnt,
struct autofs_sb_info *sbi,
int how)
{
unsigned long timeout;
struct dentry *root = sb->s_root;
struct dentry *expired = NULL;
struct list_head *next;
int do_now = how & AUTOFS_EXP_IMMEDIATE;
int exp_leaves = how & AUTOFS_EXP_LEAVES;
if ( !sbi->exp_timeout || !root )
return NULL;
now = jiffies;
timeout = sbi->exp_timeout;
spin_lock(&dcache_lock);
next = root->d_subdirs.next;
/* On exit from the loop expire is set to a dgot dentry
* to expire or it's NULL */
while ( next != &root->d_subdirs ) {
struct dentry *dentry = list_entry(next, struct dentry, d_child);
/* Negative dentry - give up */
if ( !simple_positive(dentry) ) {
next = next->next;
continue;
}
dentry = dget(dentry);
spin_unlock(&dcache_lock);
/* Case 1: indirect mount or top level direct mount */
if (d_mountpoint(dentry)) {
DPRINTK("checking mountpoint %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
/* Can we expire this guy */
if (!autofs4_can_expire(dentry, timeout, do_now))
goto next;
/* Can we umount this guy */
if (autofs4_check_mount(mnt, dentry)) {
expired = dentry;
break;
}
goto next;
}
if ( simple_empty(dentry) )
goto next;
/* Case 2: tree mount, expire iff entire tree is not busy */
if (!exp_leaves) {
if (autofs4_check_tree(mnt, dentry, timeout, do_now)) {
expired = dentry;
break;
}
/* Case 3: direct mount, expire individual leaves */
} else {
expired = autofs4_check_leaves(mnt, dentry, timeout, do_now);
if (expired) {
dput(dentry);
break;
}
}
next:
dput(dentry);
spin_lock(&dcache_lock);
next = next->next;
}
if ( expired ) {
DPRINTK("returning %p %.*s",
expired, (int)expired->d_name.len, expired->d_name.name);
spin_lock(&dcache_lock);
list_del(&expired->d_parent->d_subdirs);
list_add(&expired->d_parent->d_subdirs, &expired->d_child);
spin_unlock(&dcache_lock);
return expired;
}
spin_unlock(&dcache_lock);
return NULL;
}
/* Perform an expiry operation */
int autofs4_expire_run(struct super_block *sb,
struct vfsmount *mnt,
struct autofs_sb_info *sbi,
struct autofs_packet_expire __user *pkt_p)
{
struct autofs_packet_expire pkt;
struct dentry *dentry;
memset(&pkt,0,sizeof pkt);
pkt.hdr.proto_version = sbi->version;
pkt.hdr.type = autofs_ptype_expire;
if ((dentry = autofs4_expire(sb, mnt, sbi, 0)) == NULL)
return -EAGAIN;
pkt.len = dentry->d_name.len;
memcpy(pkt.name, dentry->d_name.name, pkt.len);
pkt.name[pkt.len] = '\0';
dput(dentry);
if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
return -EFAULT;
return 0;
}
/* Call repeatedly until it returns -EAGAIN, meaning there's nothing
more to be done */
int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt,
struct autofs_sb_info *sbi, int __user *arg)
{
struct dentry *dentry;
int ret = -EAGAIN;
int do_now = 0;
if (arg && get_user(do_now, arg))
return -EFAULT;
if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) {
struct autofs_info *de_info = autofs4_dentry_ino(dentry);
/* This is synchronous because it makes the daemon a
little easier */
de_info->flags |= AUTOFS_INF_EXPIRING;
ret = autofs4_wait(sbi, dentry, NFY_EXPIRE);
de_info->flags &= ~AUTOFS_INF_EXPIRING;
dput(dentry);
}
return ret;
}

42
fs/autofs4/init.c Normal file
View File

@@ -0,0 +1,42 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/init.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include <linux/module.h>
#include <linux/init.h>
#include "autofs_i.h"
static struct super_block *autofs_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_nodev(fs_type, flags, data, autofs4_fill_super);
}
static struct file_system_type autofs_fs_type = {
.owner = THIS_MODULE,
.name = "autofs",
.get_sb = autofs_get_sb,
.kill_sb = kill_anon_super,
};
static int __init init_autofs4_fs(void)
{
return register_filesystem(&autofs_fs_type);
}
static void __exit exit_autofs4_fs(void)
{
unregister_filesystem(&autofs_fs_type);
}
module_init(init_autofs4_fs)
module_exit(exit_autofs4_fs)
MODULE_LICENSE("GPL");

324
fs/autofs4/inode.c Normal file
View File

@@ -0,0 +1,324 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/inode.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/file.h>
#include <linux/pagemap.h>
#include <linux/parser.h>
#include <linux/bitops.h>
#include "autofs_i.h"
#include <linux/module.h>
static void ino_lnkfree(struct autofs_info *ino)
{
if (ino->u.symlink) {
kfree(ino->u.symlink);
ino->u.symlink = NULL;
}
}
struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
struct autofs_sb_info *sbi, mode_t mode)
{
int reinit = 1;
if (ino == NULL) {
reinit = 0;
ino = kmalloc(sizeof(*ino), GFP_KERNEL);
}
if (ino == NULL)
return NULL;
ino->flags = 0;
ino->mode = mode;
ino->inode = NULL;
ino->dentry = NULL;
ino->size = 0;
ino->last_used = jiffies;
ino->sbi = sbi;
if (reinit && ino->free)
(ino->free)(ino);
memset(&ino->u, 0, sizeof(ino->u));
ino->free = NULL;
if (S_ISLNK(mode))
ino->free = ino_lnkfree;
return ino;
}
void autofs4_free_ino(struct autofs_info *ino)
{
if (ino->dentry) {
ino->dentry->d_fsdata = NULL;
if (ino->dentry->d_inode)
dput(ino->dentry);
ino->dentry = NULL;
}
if (ino->free)
(ino->free)(ino);
kfree(ino);
}
static void autofs4_put_super(struct super_block *sb)
{
struct autofs_sb_info *sbi = autofs4_sbi(sb);
sb->s_fs_info = NULL;
if ( !sbi->catatonic )
autofs4_catatonic_mode(sbi); /* Free wait queues, close pipe */
kfree(sbi);
DPRINTK("shutting down");
}
static struct super_operations autofs4_sops = {
.put_super = autofs4_put_super,
.statfs = simple_statfs,
};
enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto};
static match_table_t tokens = {
{Opt_fd, "fd=%u"},
{Opt_uid, "uid=%u"},
{Opt_gid, "gid=%u"},
{Opt_pgrp, "pgrp=%u"},
{Opt_minproto, "minproto=%u"},
{Opt_maxproto, "maxproto=%u"},
{Opt_err, NULL}
};
static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid,
pid_t *pgrp, int *minproto, int *maxproto)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
*uid = current->uid;
*gid = current->gid;
*pgrp = process_group(current);
*minproto = AUTOFS_MIN_PROTO_VERSION;
*maxproto = AUTOFS_MAX_PROTO_VERSION;
*pipefd = -1;
if (!options)
return 1;
while ((p = strsep(&options, ",")) != NULL) {
int token;
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case Opt_fd:
if (match_int(args, pipefd))
return 1;
break;
case Opt_uid:
if (match_int(args, &option))
return 1;
*uid = option;
break;
case Opt_gid:
if (match_int(args, &option))
return 1;
*gid = option;
break;
case Opt_pgrp:
if (match_int(args, &option))
return 1;
*pgrp = option;
break;
case Opt_minproto:
if (match_int(args, &option))
return 1;
*minproto = option;
break;
case Opt_maxproto:
if (match_int(args, &option))
return 1;
*maxproto = option;
break;
default:
return 1;
}
}
return (*pipefd < 0);
}
static struct autofs_info *autofs4_mkroot(struct autofs_sb_info *sbi)
{
struct autofs_info *ino;
ino = autofs4_init_ino(NULL, sbi, S_IFDIR | 0755);
if (!ino)
return NULL;
return ino;
}
int autofs4_fill_super(struct super_block *s, void *data, int silent)
{
struct inode * root_inode;
struct dentry * root;
struct file * pipe;
int pipefd;
struct autofs_sb_info *sbi;
struct autofs_info *ino;
int minproto, maxproto;
sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL);
if ( !sbi )
goto fail_unlock;
DPRINTK("starting up, sbi = %p",sbi);
memset(sbi, 0, sizeof(*sbi));
s->s_fs_info = sbi;
sbi->magic = AUTOFS_SBI_MAGIC;
sbi->catatonic = 0;
sbi->exp_timeout = 0;
sbi->oz_pgrp = process_group(current);
sbi->sb = s;
sbi->version = 0;
sbi->sub_version = 0;
init_MUTEX(&sbi->wq_sem);
sbi->queues = NULL;
s->s_blocksize = 1024;
s->s_blocksize_bits = 10;
s->s_magic = AUTOFS_SUPER_MAGIC;
s->s_op = &autofs4_sops;
s->s_time_gran = 1;
/*
* Get the root inode and dentry, but defer checking for errors.
*/
ino = autofs4_mkroot(sbi);
if (!ino)
goto fail_free;
root_inode = autofs4_get_inode(s, ino);
kfree(ino);
if (!root_inode)
goto fail_free;
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;
/* Can this call block? */
if (parse_options(data, &pipefd,
&root_inode->i_uid, &root_inode->i_gid,
&sbi->oz_pgrp,
&minproto, &maxproto)) {
printk("autofs: called with bogus options\n");
goto fail_dput;
}
/* Couldn't this be tested earlier? */
if (maxproto < AUTOFS_MIN_PROTO_VERSION ||
minproto > AUTOFS_MAX_PROTO_VERSION) {
printk("autofs: kernel does not match daemon version "
"daemon (%d, %d) kernel (%d, %d)\n",
minproto, maxproto,
AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
goto fail_dput;
}
sbi->version = maxproto > AUTOFS_MAX_PROTO_VERSION ? AUTOFS_MAX_PROTO_VERSION : maxproto;
sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
DPRINTK("pipe fd = %d, pgrp = %u", pipefd, sbi->oz_pgrp);
pipe = fget(pipefd);
if ( !pipe ) {
printk("autofs: could not open pipe file descriptor\n");
goto fail_dput;
}
if ( !pipe->f_op || !pipe->f_op->write )
goto fail_fput;
sbi->pipe = pipe;
/*
* Success! Install the root dentry now to indicate completion.
*/
s->s_root = root;
return 0;
/*
* Failure ... clean up.
*/
fail_fput:
printk("autofs: pipe file descriptor does not contain proper ops\n");
fput(pipe);
/* fall through */
fail_dput:
dput(root);
goto fail_free;
fail_iput:
printk("autofs: get root dentry failed\n");
iput(root_inode);
fail_free:
kfree(sbi);
fail_unlock:
return -EINVAL;
}
struct inode *autofs4_get_inode(struct super_block *sb,
struct autofs_info *inf)
{
struct inode *inode = new_inode(sb);
if (inode == NULL)
return NULL;
inf->inode = inode;
inode->i_mode = inf->mode;
if (sb->s_root) {
inode->i_uid = sb->s_root->d_inode->i_uid;
inode->i_gid = sb->s_root->d_inode->i_gid;
} else {
inode->i_uid = 0;
inode->i_gid = 0;
}
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
if (S_ISDIR(inf->mode)) {
inode->i_nlink = 2;
inode->i_op = &autofs4_dir_inode_operations;
inode->i_fop = &autofs4_dir_operations;
} else if (S_ISLNK(inf->mode)) {
inode->i_size = inf->size;
inode->i_op = &autofs4_symlink_inode_operations;
}
return inode;
}

808
fs/autofs4/root.c Normal file
View File

@@ -0,0 +1,808 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/root.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
* Copyright 2001-2003 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/param.h>
#include <linux/time.h>
#include <linux/smp_lock.h>
#include "autofs_i.h"
static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *);
static int autofs4_dir_unlink(struct inode *,struct dentry *);
static int autofs4_dir_rmdir(struct inode *,struct dentry *);
static int autofs4_dir_mkdir(struct inode *,struct dentry *,int);
static int autofs4_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
static int autofs4_dir_open(struct inode *inode, struct file *file);
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 int autofs4_dcache_readdir(struct file *, void *, filldir_t);
struct file_operations autofs4_root_operations = {
.open = dcache_dir_open,
.release = dcache_dir_close,
.read = generic_read_dir,
.readdir = autofs4_root_readdir,
.ioctl = autofs4_root_ioctl,
};
struct file_operations autofs4_dir_operations = {
.open = autofs4_dir_open,
.release = autofs4_dir_close,
.read = generic_read_dir,
.readdir = autofs4_dir_readdir,
};
struct inode_operations autofs4_root_inode_operations = {
.lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink,
.symlink = autofs4_dir_symlink,
.mkdir = autofs4_dir_mkdir,
.rmdir = autofs4_dir_rmdir,
};
struct inode_operations autofs4_dir_inode_operations = {
.lookup = autofs4_lookup,
.unlink = autofs4_dir_unlink,
.symlink = autofs4_dir_symlink,
.mkdir = autofs4_dir_mkdir,
.rmdir = autofs4_dir_rmdir,
};
static int autofs4_root_readdir(struct file *file, void *dirent,
filldir_t filldir)
{
struct autofs_sb_info *sbi = autofs4_sbi(file->f_dentry->d_sb);
int oz_mode = autofs4_oz_mode(sbi);
DPRINTK("called, filp->f_pos = %lld", file->f_pos);
/*
* Don't set reghost flag if:
* 1) f_pos is larger than zero -- we've already been here.
* 2) we haven't even enabled reghosting in the 1st place.
* 3) this is the daemon doing a readdir
*/
if (oz_mode && file->f_pos == 0 && sbi->reghost_enabled)
sbi->needs_reghost = 1;
DPRINTK("needs_reghost = %d", sbi->needs_reghost);
return autofs4_dcache_readdir(file, dirent, filldir);
}
/* Update usage from here to top of tree, so that scan of
top-level directories will give a useful result */
static void autofs4_update_usage(struct dentry *dentry)
{
struct dentry *top = dentry->d_sb->s_root;
spin_lock(&dcache_lock);
for(; dentry != top; dentry = dentry->d_parent) {
struct autofs_info *ino = autofs4_dentry_ino(dentry);
if (ino) {
update_atime(dentry->d_inode);
ino->last_used = jiffies;
}
}
spin_unlock(&dcache_lock);
}
/*
* From 2.4 kernel readdir.c
*/
static int autofs4_dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
int i;
struct dentry *dentry = filp->f_dentry;
i = filp->f_pos;
switch (i) {
case 0:
if (filldir(dirent, ".", 1, i, dentry->d_inode->i_ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
case 1:
if (filldir(dirent, "..", 2, i, dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
break;
i++;
filp->f_pos++;
/* fallthrough */
default: {
struct list_head *list;
int j = i-2;
spin_lock(&dcache_lock);
list = dentry->d_subdirs.next;
for (;;) {
if (list == &dentry->d_subdirs) {
spin_unlock(&dcache_lock);
return 0;
}
if (!j)
break;
j--;
list = list->next;
}
while(1) {
struct dentry *de = list_entry(list, struct dentry, d_child);
if (!d_unhashed(de) && de->d_inode) {
spin_unlock(&dcache_lock);
if (filldir(dirent, de->d_name.name, de->d_name.len, filp->f_pos, de->d_inode->i_ino, DT_UNKNOWN) < 0)
break;
spin_lock(&dcache_lock);
}
filp->f_pos++;
list = list->next;
if (list != &dentry->d_subdirs)
continue;
spin_unlock(&dcache_lock);
break;
}
}
}
return 0;
}
static int autofs4_dir_open(struct inode *inode, struct file *file)
{
struct dentry *dentry = file->f_dentry;
struct vfsmount *mnt = file->f_vfsmnt;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
int status;
DPRINTK("file=%p dentry=%p %.*s",
file, dentry, dentry->d_name.len, dentry->d_name.name);
if (autofs4_oz_mode(sbi))
goto out;
if (autofs4_ispending(dentry)) {
DPRINTK("dentry busy");
return -EBUSY;
}
if (!d_mountpoint(dentry) && dentry->d_op && dentry->d_op->d_revalidate) {
struct nameidata nd;
int empty;
/* In case there are stale directory dentrys from a failed mount */
spin_lock(&dcache_lock);
empty = list_empty(&dentry->d_subdirs);
spin_unlock(&dcache_lock);
if (!empty)
d_invalidate(dentry);
nd.flags = LOOKUP_DIRECTORY;
status = (dentry->d_op->d_revalidate)(dentry, &nd);
if (!status)
return -ENOENT;
}
if (d_mountpoint(dentry)) {
struct file *fp = NULL;
struct vfsmount *fp_mnt = mntget(mnt);
struct dentry *fp_dentry = dget(dentry);
while (follow_down(&fp_mnt, &fp_dentry) && d_mountpoint(fp_dentry));
fp = dentry_open(fp_dentry, fp_mnt, file->f_flags);
status = PTR_ERR(fp);
if (IS_ERR(fp)) {
file->private_data = NULL;
return status;
}
file->private_data = fp;
}
out:
return 0;
}
static int autofs4_dir_close(struct inode *inode, struct file *file)
{
struct dentry *dentry = file->f_dentry;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
DPRINTK("file=%p dentry=%p %.*s",
file, dentry, dentry->d_name.len, dentry->d_name.name);
if (autofs4_oz_mode(sbi))
goto out;
if (autofs4_ispending(dentry)) {
DPRINTK("dentry busy");
return -EBUSY;
}
if (d_mountpoint(dentry)) {
struct file *fp = file->private_data;
if (!fp)
return -ENOENT;
filp_close(fp, current->files);
file->private_data = NULL;
}
out:
return 0;
}
static int autofs4_dir_readdir(struct file *file, void *dirent, filldir_t filldir)
{
struct dentry *dentry = file->f_dentry;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
int status;
DPRINTK("file=%p dentry=%p %.*s",
file, dentry, dentry->d_name.len, dentry->d_name.name);
if (autofs4_oz_mode(sbi))
goto out;
if (autofs4_ispending(dentry)) {
DPRINTK("dentry busy");
return -EBUSY;
}
if (d_mountpoint(dentry)) {
struct file *fp = file->private_data;
if (!fp)
return -ENOENT;
if (!fp->f_op || !fp->f_op->readdir)
goto out;
status = vfs_readdir(fp, filldir, dirent);
file->f_pos = fp->f_pos;
if (status)
autofs4_copy_atime(file, fp);
return status;
}
out:
return autofs4_dcache_readdir(file, dirent, filldir);
}
static int try_to_fill_dentry(struct dentry *dentry,
struct super_block *sb,
struct autofs_sb_info *sbi, int flags)
{
struct autofs_info *de_info = autofs4_dentry_ino(dentry);
int status = 0;
/* Block on any pending expiry here; invalidate the dentry
when expiration is done to trigger mount request with a new
dentry */
if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) {
DPRINTK("waiting for expire %p name=%.*s",
dentry, dentry->d_name.len, dentry->d_name.name);
status = autofs4_wait(sbi, dentry, NFY_NONE);
DPRINTK("expire done status=%d", status);
return 0;
}
DPRINTK("dentry=%p %.*s ino=%p",
dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
/* Wait for a pending mount, triggering one if there isn't one already */
if (dentry->d_inode == NULL) {
DPRINTK("waiting for mount name=%.*s",
dentry->d_name.len, dentry->d_name.name);
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
DPRINTK("mount done status=%d", status);
if (status && dentry->d_inode)
return 0; /* Try to get the kernel to invalidate this dentry */
/* Turn this into a real negative dentry? */
if (status == -ENOENT) {
dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT;
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
return 1;
} else if (status) {
/* Return a negative dentry, but leave it "pending" */
return 1;
}
/* Trigger mount for path component or follow link */
} else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) ||
current->link_count) {
DPRINTK("waiting for mount name=%.*s",
dentry->d_name.len, dentry->d_name.name);
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
status = autofs4_wait(sbi, dentry, NFY_MOUNT);
DPRINTK("mount done status=%d", status);
if (status) {
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
return 0;
}
}
/* We don't update the usages for the autofs daemon itself, this
is necessary for recursive autofs mounts */
if (!autofs4_oz_mode(sbi))
autofs4_update_usage(dentry);
spin_lock(&dentry->d_lock);
dentry->d_flags &= ~DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
return 1;
}
/*
* Revalidate is called on every cache lookup. Some of those
* cache lookups may actually happen while the dentry is not
* yet completely filled in, and revalidate has to delay such
* lookups..
*/
static int autofs4_revalidate(struct dentry * dentry, struct nameidata *nd)
{
struct inode * dir = dentry->d_parent->d_inode;
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;
/* Pending dentry */
if (autofs4_ispending(dentry)) {
if (!oz_mode)
status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags);
return status;
}
/* Negative dentry.. invalidate if "old" */
if (dentry->d_inode == NULL)
return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
/* Check for a non-mountpoint directory with no contents */
spin_lock(&dcache_lock);
if (S_ISDIR(dentry->d_inode->i_mode) &&
!d_mountpoint(dentry) &&
list_empty(&dentry->d_subdirs)) {
DPRINTK("dentry=%p %.*s, emptydir",
dentry, dentry->d_name.len, dentry->d_name.name);
spin_unlock(&dcache_lock);
if (!oz_mode)
status = try_to_fill_dentry(dentry, dir->i_sb, sbi, flags);
return status;
}
spin_unlock(&dcache_lock);
/* Update the usage list */
if (!oz_mode)
autofs4_update_usage(dentry);
return 1;
}
static void autofs4_dentry_release(struct dentry *de)
{
struct autofs_info *inf;
DPRINTK("releasing %p", de);
inf = autofs4_dentry_ino(de);
de->d_fsdata = NULL;
if (inf) {
inf->dentry = NULL;
inf->inode = NULL;
autofs4_free_ino(inf);
}
}
/* For dentries of directories in the root dir */
static struct dentry_operations autofs4_root_dentry_operations = {
.d_revalidate = autofs4_revalidate,
.d_release = autofs4_dentry_release,
};
/* For other dentries */
static struct dentry_operations autofs4_dentry_operations = {
.d_revalidate = autofs4_revalidate,
.d_release = autofs4_dentry_release,
};
/* Lookups in the root directory */
static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
{
struct autofs_sb_info *sbi;
int oz_mode;
DPRINTK("name = %.*s",
dentry->d_name.len, dentry->d_name.name);
if (dentry->d_name.len > NAME_MAX)
return ERR_PTR(-ENAMETOOLONG);/* File name too long to exist */
sbi = autofs4_sbi(dir->i_sb);
oz_mode = autofs4_oz_mode(sbi);
DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
current->pid, process_group(current), sbi->catatonic, oz_mode);
/*
* Mark the dentry incomplete, but add it. This is needed so
* that the VFS layer knows about the dentry, and we can count
* on catching any lookups through the revalidate.
*
* Let all the hard work be done by the revalidate function that
* needs to be able to do this anyway..
*
* We need to do this before we release the directory semaphore.
*/
dentry->d_op = &autofs4_root_dentry_operations;
if (!oz_mode) {
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_AUTOFS_PENDING;
spin_unlock(&dentry->d_lock);
}
dentry->d_fsdata = NULL;
d_add(dentry, NULL);
if (dentry->d_op && dentry->d_op->d_revalidate) {
up(&dir->i_sem);
(dentry->d_op->d_revalidate)(dentry, nd);
down(&dir->i_sem);
}
/*
* If we are still pending, check if we had to handle
* a signal. If so we can force a restart..
*/
if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
/* See if we were interrupted */
if (signal_pending(current)) {
sigset_t *sigset = &current->pending.signal;
if (sigismember (sigset, SIGKILL) ||
sigismember (sigset, SIGQUIT) ||
sigismember (sigset, SIGINT)) {
return ERR_PTR(-ERESTARTNOINTR);
}
}
}
/*
* If this dentry is unhashed, then we shouldn't honour this
* lookup even if the dentry is positive. Returning ENOENT here
* doesn't do the right thing for all system calls, but it should
* be OK for the operations we permit from an autofs.
*/
if ( dentry->d_inode && d_unhashed(dentry) )
return ERR_PTR(-ENOENT);
return NULL;
}
static int autofs4_dir_symlink(struct inode *dir,
struct dentry *dentry,
const char *symname)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct inode *inode;
char *cp;
DPRINTK("%s <- %.*s", symname,
dentry->d_name.len, dentry->d_name.name);
if (!autofs4_oz_mode(sbi))
return -EACCES;
ino = autofs4_init_ino(ino, sbi, S_IFLNK | 0555);
if (ino == NULL)
return -ENOSPC;
ino->size = strlen(symname);
ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL);
if (cp == NULL) {
kfree(ino);
return -ENOSPC;
}
strcpy(cp, symname);
inode = autofs4_get_inode(dir->i_sb, ino);
d_instantiate(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode)
dentry->d_op = &autofs4_root_dentry_operations;
else
dentry->d_op = &autofs4_dentry_operations;
dentry->d_fsdata = ino;
ino->dentry = dget(dentry);
ino->inode = inode;
dir->i_mtime = CURRENT_TIME;
return 0;
}
/*
* NOTE!
*
* Normal filesystems would do a "d_delete()" to tell the VFS dcache
* that the file no longer exists. However, doing that means that the
* VFS layer can turn the dentry into a negative dentry. We don't want
* this, because since the unlink is probably the result of an expire.
* We simply d_drop it, which allows the dentry lookup to remount it
* if necessary.
*
* If a process is blocked on the dentry waiting for the expire to finish,
* it will invalidate the dentry and try to mount with a new one.
*
* Also see autofs4_dir_rmdir()..
*/
static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
/* This allows root to remove symlinks */
if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
return -EACCES;
dput(ino->dentry);
dentry->d_inode->i_size = 0;
dentry->d_inode->i_nlink = 0;
dir->i_mtime = CURRENT_TIME;
d_drop(dentry);
return 0;
}
static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
if (!autofs4_oz_mode(sbi))
return -EACCES;
spin_lock(&dcache_lock);
if (!list_empty(&dentry->d_subdirs)) {
spin_unlock(&dcache_lock);
return -ENOTEMPTY;
}
spin_lock(&dentry->d_lock);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
dput(ino->dentry);
dentry->d_inode->i_size = 0;
dentry->d_inode->i_nlink = 0;
if (dir->i_nlink)
dir->i_nlink--;
return 0;
}
static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb);
struct autofs_info *ino = autofs4_dentry_ino(dentry);
struct inode *inode;
if ( !autofs4_oz_mode(sbi) )
return -EACCES;
DPRINTK("dentry %p, creating %.*s",
dentry, dentry->d_name.len, dentry->d_name.name);
ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555);
if (ino == NULL)
return -ENOSPC;
inode = autofs4_get_inode(dir->i_sb, ino);
d_instantiate(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode)
dentry->d_op = &autofs4_root_dentry_operations;
else
dentry->d_op = &autofs4_dentry_operations;
dentry->d_fsdata = ino;
ino->dentry = dget(dentry);
ino->inode = inode;
dir->i_nlink++;
dir->i_mtime = CURRENT_TIME;
return 0;
}
/* Get/set timeout ioctl() operation */
static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi,
unsigned long __user *p)
{
int rv;
unsigned long ntimeout;
if ( (rv = get_user(ntimeout, p)) ||
(rv = put_user(sbi->exp_timeout/HZ, p)) )
return rv;
if ( ntimeout > ULONG_MAX/HZ )
sbi->exp_timeout = 0;
else
sbi->exp_timeout = ntimeout * HZ;
return 0;
}
/* Return protocol version */
static inline int autofs4_get_protover(struct autofs_sb_info *sbi, int __user *p)
{
return put_user(sbi->version, p);
}
/* Return protocol sub version */
static inline int autofs4_get_protosubver(struct autofs_sb_info *sbi, int __user *p)
{
return put_user(sbi->sub_version, p);
}
/*
* Tells the daemon whether we need to reghost or not. Also, clears
* the reghost_needed flag.
*/
static inline int autofs4_ask_reghost(struct autofs_sb_info *sbi, int __user *p)
{
int status;
DPRINTK("returning %d", sbi->needs_reghost);
status = put_user(sbi->needs_reghost, p);
if ( status )
return status;
sbi->needs_reghost = 0;
return 0;
}
/*
* Enable / Disable reghosting ioctl() operation
*/
static inline int autofs4_toggle_reghost(struct autofs_sb_info *sbi, int __user *p)
{
int status;
int val;
status = get_user(val, p);
DPRINTK("reghost = %d", val);
if (status)
return status;
/* turn on/off reghosting, with the val */
sbi->reghost_enabled = val;
return 0;
}
/*
* Tells the daemon whether it can umount the autofs mount.
*/
static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
{
int status = 0;
if (may_umount(mnt) == 0)
status = 1;
DPRINTK("returning %d", status);
status = put_user(status, p);
return status;
}
/* Identify autofs4_dentries - this is so we can tell if there's
an extra dentry refcount or not. We only hold a refcount on the
dentry if its non-negative (ie, d_inode != NULL)
*/
int is_autofs4_dentry(struct dentry *dentry)
{
return dentry && dentry->d_inode &&
(dentry->d_op == &autofs4_root_dentry_operations ||
dentry->d_op == &autofs4_dentry_operations) &&
dentry->d_fsdata != NULL;
}
/*
* ioctl()'s on the root directory is the chief method for the daemon to
* generate kernel reactions
*/
static int autofs4_root_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct autofs_sb_info *sbi = autofs4_sbi(inode->i_sb);
void __user *p = (void __user *)arg;
DPRINTK("cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u",
cmd,arg,sbi,process_group(current));
if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
_IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
return -ENOTTY;
if ( !autofs4_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) )
return -EPERM;
switch(cmd) {
case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
return autofs4_wait_release(sbi,(autofs_wqt_t)arg,0);
case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
return autofs4_wait_release(sbi,(autofs_wqt_t)arg,-ENOENT);
case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
autofs4_catatonic_mode(sbi);
return 0;
case AUTOFS_IOC_PROTOVER: /* Get protocol version */
return autofs4_get_protover(sbi, p);
case AUTOFS_IOC_PROTOSUBVER: /* Get protocol sub version */
return autofs4_get_protosubver(sbi, p);
case AUTOFS_IOC_SETTIMEOUT:
return autofs4_get_set_timeout(sbi, p);
case AUTOFS_IOC_TOGGLEREGHOST:
return autofs4_toggle_reghost(sbi, p);
case AUTOFS_IOC_ASKREGHOST:
return autofs4_ask_reghost(sbi, p);
case AUTOFS_IOC_ASKUMOUNT:
return autofs4_ask_umount(filp->f_vfsmnt, p);
/* return a single thing to expire */
case AUTOFS_IOC_EXPIRE:
return autofs4_expire_run(inode->i_sb,filp->f_vfsmnt,sbi, p);
/* same as above, but can send multiple expires through pipe */
case AUTOFS_IOC_EXPIRE_MULTI:
return autofs4_expire_multi(inode->i_sb,filp->f_vfsmnt,sbi, p);
default:
return -ENOSYS;
}
}

25
fs/autofs4/symlink.c Normal file
View File

@@ -0,0 +1,25 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/symlink.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
*
* 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include "autofs_i.h"
static int autofs4_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct autofs_info *ino = autofs4_dentry_ino(dentry);
nd_set_link(nd, (char *)ino->u.symlink);
return 0;
}
struct inode_operations autofs4_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = autofs4_follow_link
};

303
fs/autofs4/waitq.c Normal file
View File

@@ -0,0 +1,303 @@
/* -*- c -*- --------------------------------------------------------------- *
*
* linux/fs/autofs/waitq.c
*
* Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
* Copyright 2001-2003 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
* option, any later version, incorporated herein by reference.
*
* ------------------------------------------------------------------------- */
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/signal.h>
#include <linux/file.h>
#include "autofs_i.h"
/* We make this a static variable rather than a part of the superblock; it
is better if we don't reassign numbers easily even across filesystems */
static autofs_wqt_t autofs4_next_wait_queue = 1;
/* These are the signals we allow interrupting a pending mount */
#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGQUIT))
void autofs4_catatonic_mode(struct autofs_sb_info *sbi)
{
struct autofs_wait_queue *wq, *nwq;
DPRINTK("entering catatonic mode");
sbi->catatonic = 1;
wq = sbi->queues;
sbi->queues = NULL; /* Erase all wait queues */
while ( wq ) {
nwq = wq->next;
wq->status = -ENOENT; /* Magic is gone - report failure */
kfree(wq->name);
wq->name = NULL;
wake_up_interruptible(&wq->queue);
wq = nwq;
}
if (sbi->pipe) {
fput(sbi->pipe); /* Close the pipe */
sbi->pipe = NULL;
}
shrink_dcache_sb(sbi->sb);
}
static int autofs4_write(struct file *file, const void *addr, int bytes)
{
unsigned long sigpipe, flags;
mm_segment_t fs;
const char *data = (const char *)addr;
ssize_t wr = 0;
/** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
sigpipe = sigismember(&current->pending.signal, SIGPIPE);
/* Save pointer to user space and point back to kernel space */
fs = get_fs();
set_fs(KERNEL_DS);
while (bytes &&
(wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) {
data += wr;
bytes -= wr;
}
set_fs(fs);
/* Keep the currently executing process from receiving a
SIGPIPE unless it was already supposed to get one */
if (wr == -EPIPE && !sigpipe) {
spin_lock_irqsave(&current->sighand->siglock, flags);
sigdelset(&current->pending.signal, SIGPIPE);
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
}
return (bytes > 0);
}
static void autofs4_notify_daemon(struct autofs_sb_info *sbi,
struct autofs_wait_queue *wq,
int type)
{
union autofs_packet_union pkt;
size_t pktsz;
DPRINTK("wait id = 0x%08lx, name = %.*s, type=%d",
wq->wait_queue_token, wq->len, wq->name, type);
memset(&pkt,0,sizeof pkt); /* For security reasons */
pkt.hdr.proto_version = sbi->version;
pkt.hdr.type = type;
if (type == autofs_ptype_missing) {
struct autofs_packet_missing *mp = &pkt.missing;
pktsz = sizeof(*mp);
mp->wait_queue_token = wq->wait_queue_token;
mp->len = wq->len;
memcpy(mp->name, wq->name, wq->len);
mp->name[wq->len] = '\0';
} else if (type == autofs_ptype_expire_multi) {
struct autofs_packet_expire_multi *ep = &pkt.expire_multi;
pktsz = sizeof(*ep);
ep->wait_queue_token = wq->wait_queue_token;
ep->len = wq->len;
memcpy(ep->name, wq->name, wq->len);
ep->name[wq->len] = '\0';
} else {
printk("autofs4_notify_daemon: bad type %d!\n", type);
return;
}
if (autofs4_write(sbi->pipe, &pkt, pktsz))
autofs4_catatonic_mode(sbi);
}
static int autofs4_getpath(struct autofs_sb_info *sbi,
struct dentry *dentry, char **name)
{
struct dentry *root = sbi->sb->s_root;
struct dentry *tmp;
char *buf = *name;
char *p;
int len = 0;
spin_lock(&dcache_lock);
for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
len += tmp->d_name.len + 1;
if (--len > NAME_MAX) {
spin_unlock(&dcache_lock);
return 0;
}
*(buf + len) = '\0';
p = buf + len - dentry->d_name.len;
strncpy(p, dentry->d_name.name, dentry->d_name.len);
for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) {
*(--p) = '/';
p -= tmp->d_name.len;
strncpy(p, tmp->d_name.name, tmp->d_name.len);
}
spin_unlock(&dcache_lock);
return len;
}
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry,
enum autofs_notify notify)
{
struct autofs_wait_queue *wq;
char *name;
int len, status;
/* In catatonic mode, we don't wait for nobody */
if ( sbi->catatonic )
return -ENOENT;
name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
if (!name)
return -ENOMEM;
len = autofs4_getpath(sbi, dentry, &name);
if (!len) {
kfree(name);
return -ENOENT;
}
if (down_interruptible(&sbi->wq_sem)) {
kfree(name);
return -EINTR;
}
for (wq = sbi->queues ; wq ; wq = wq->next) {
if (wq->hash == dentry->d_name.hash &&
wq->len == len &&
wq->name && !memcmp(wq->name, name, len))
break;
}
if ( !wq ) {
/* Create a new wait queue */
wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
if ( !wq ) {
kfree(name);
up(&sbi->wq_sem);
return -ENOMEM;
}
wq->wait_queue_token = autofs4_next_wait_queue;
if (++autofs4_next_wait_queue == 0)
autofs4_next_wait_queue = 1;
wq->next = sbi->queues;
sbi->queues = wq;
init_waitqueue_head(&wq->queue);
wq->hash = dentry->d_name.hash;
wq->name = name;
wq->len = len;
wq->status = -EINTR; /* Status return if interrupted */
atomic_set(&wq->wait_ctr, 2);
up(&sbi->wq_sem);
DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d",
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
/* autofs4_notify_daemon() may block */
if (notify != NFY_NONE) {
autofs4_notify_daemon(sbi,wq,
notify == NFY_MOUNT ?
autofs_ptype_missing :
autofs_ptype_expire_multi);
}
} else {
atomic_inc(&wq->wait_ctr);
up(&sbi->wq_sem);
kfree(name);
DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d",
(unsigned long) wq->wait_queue_token, wq->len, wq->name, notify);
}
/* wq->name is NULL if and only if the lock is already released */
if ( sbi->catatonic ) {
/* We might have slept, so check again for catatonic mode */
wq->status = -ENOENT;
if ( wq->name ) {
kfree(wq->name);
wq->name = NULL;
}
}
if ( wq->name ) {
/* Block all but "shutdown" signals while waiting */
sigset_t oldset;
unsigned long irqflags;
spin_lock_irqsave(&current->sighand->siglock, irqflags);
oldset = current->blocked;
siginitsetinv(&current->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]);
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
wait_event_interruptible(wq->queue, wq->name == NULL);
spin_lock_irqsave(&current->sighand->siglock, irqflags);
current->blocked = oldset;
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, irqflags);
} else {
DPRINTK("skipped sleeping");
}
status = wq->status;
/* Are we the last process to need status? */
if (atomic_dec_and_test(&wq->wait_ctr))
kfree(wq);
return status;
}
int autofs4_wait_release(struct autofs_sb_info *sbi, autofs_wqt_t wait_queue_token, int status)
{
struct autofs_wait_queue *wq, **wql;
down(&sbi->wq_sem);
for ( wql = &sbi->queues ; (wq = *wql) != 0 ; wql = &wq->next ) {
if ( wq->wait_queue_token == wait_queue_token )
break;
}
if ( !wq ) {
up(&sbi->wq_sem);
return -EINVAL;
}
*wql = wq->next; /* Unlink from chain */
up(&sbi->wq_sem);
kfree(wq->name);
wq->name = NULL; /* Do not wait on this queue */
wq->status = status;
if (atomic_dec_and_test(&wq->wait_ctr)) /* Is anyone still waiting for this guy? */
kfree(wq);
else
wake_up_interruptible(&wq->queue);
return 0;
}