diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/Makefile | 35 | ||||
-rw-r--r-- | kernel/autofs_i.h | 154 | ||||
-rw-r--r-- | kernel/dir.c | 86 | ||||
-rw-r--r-- | kernel/dirhash.c | 160 | ||||
-rw-r--r-- | kernel/init.c | 52 | ||||
-rw-r--r-- | kernel/inode.c | 350 | ||||
-rw-r--r-- | kernel/linux/auto_fs.h | 62 | ||||
-rw-r--r-- | kernel/root.c | 480 | ||||
-rw-r--r-- | kernel/symlink.c | 58 | ||||
-rw-r--r-- | kernel/waitq.c | 171 |
10 files changed, 1608 insertions, 0 deletions
diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..1681c3d --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the linux autofs-filesystem routines. +# +# We can build this either out of the kernel tree or the autofs tools tree. +# + +O_TARGET := autofs.o +O_OBJS := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o + +M_OBJS := $(O_TARGET) + +ifdef TOPDIR +# +# Part of the kernel code +# +include $(TOPDIR)/Rules.make +else +# +# Standalone (handy for development) +# +include ../Makefile.rules + +CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS) + +all: $(O_TARGET) + +$(O_TARGET): $(O_OBJS) + $(LD) -r -o $(O_TARGET) $(O_OBJS) + +install: $(O_TARGET) + install -c $(O_TARGET) /lib/modules/`uname -r`/fs + +clean: + rm -f *.o *.s +endif diff --git a/kernel/autofs_i.h b/kernel/autofs_i.h new file mode 100644 index 0000000..f827bda --- /dev/null +++ b/kernel/autofs_i.h @@ -0,0 +1,154 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * linux/fs/autofs/autofs_i.h + * + * Copyright 1997 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_fs.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/malloc.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/wait.h> +#include <asm/uaccess.h> + +#ifdef DEBUG +#define DPRINTK(D) (printk D) +#else +#define DPRINTK(D) ((void)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 */ + +/* Structures associated with the root directory hash table */ + +#define AUTOFS_HASH_SIZE 67 + +struct autofs_dir_ent { + int hash; + struct autofs_dir_ent *next; + struct autofs_dir_ent **back; + char *name; + int len; + ino_t ino; + /* The following entries are for the expiry system */ + unsigned long last_usage; + struct autofs_dir_ent *exp_next; + struct autofs_dir_ent *exp_prev; +}; + +struct autofs_dirhash { + struct autofs_dir_ent *h[AUTOFS_HASH_SIZE]; + struct autofs_dir_ent expiry_head; +}; + +struct autofs_wait_queue { + unsigned long wait_queue_token; + struct wait_queue *queue; + struct autofs_wait_queue *next; + /* 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; + int wait_ctr; +}; + +struct autofs_symlink { + int len; + char *data; + time_t mtime; +}; + +#define AUTOFS_MAX_SYMLINKS 256 + +#define AUTOFS_ROOT_INO 1 +#define AUTOFS_FIRST_SYMLINK 2 +#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS) + +#define AUTOFS_SYMLINK_BITMAP_LEN ((AUTOFS_MAX_SYMLINKS+31)/32) + +#ifndef END_OF_TIME +#define END_OF_TIME ((time_t)((unsigned long)((time_t)(~0UL)) >> 1)) +#endif + +#define AUTOFS_SBI_MAGIC 0x6d4a556d + +struct autofs_sb_info { + u32 magic; + struct file *pipe; + pid_t oz_pgrp; + int catatonic; + unsigned long exp_timeout; + ino_t next_dir_ino; + struct autofs_wait_queue *queues; /* Wait queue pointer */ + struct autofs_dirhash dirhash; /* Root directory hash */ + struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS]; + u32 symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN]; +}; + +/* autofs_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 autofs_oz_mode(struct autofs_sb_info *sbi) { + return sbi->catatonic || current->pgrp == sbi->oz_pgrp; +} + +/* Hash operations */ + +void autofs_initialize_hash(struct autofs_dirhash *); +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *); +void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *); +void autofs_hash_delete(struct autofs_dir_ent *); +struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *); +void autofs_hash_nuke(struct autofs_dirhash *); + +/* Expiration-handling functions */ + +void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *); +struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *,unsigned long); + +/* Operations structures */ + +extern struct inode_operations autofs_root_inode_operations; +extern struct inode_operations autofs_symlink_inode_operations; +extern struct inode_operations autofs_dir_inode_operations; + +/* Initializing function */ + +struct super_block *autofs_read_super(struct super_block *, void *,int); + +/* Queue management functions */ + +int autofs_wait(struct autofs_sb_info *,struct qstr *); +int autofs_wait_release(struct autofs_sb_info *,unsigned long,int); +void autofs_catatonic_mode(struct autofs_sb_info *); + +#ifdef DEBUG +void autofs_say(const char *name, int len); +#else +#define autofs_say(n,l) ((void)0) +#endif diff --git a/kernel/dir.c b/kernel/dir.c new file mode 100644 index 0000000..9264806 --- /dev/null +++ b/kernel/dir.c @@ -0,0 +1,86 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/dir.c + * + * Copyright 1997 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 autofs_dir_readdir(struct file *filp, + void *dirent, filldir_t filldir) +{ + struct inode *inode=filp->f_dentry->d_inode; + if (!inode || !S_ISDIR(inode->i_mode)) + return -ENOTDIR; + + switch((unsigned long) filp->f_pos) + { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) + return 0; + filp->f_pos++; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0) + return 0; + filp->f_pos++; + /* fall through */ + } + return 1; +} + +/* + * No entries except for "." and "..", both of which are handled by the VFS layer + */ +static int autofs_dir_lookup(struct inode *dir, struct dentry * dentry) +{ + d_add(dentry, NULL); + return 0; +} + +static struct file_operations autofs_dir_operations = { + NULL, /* llseek */ + NULL, /* read */ + NULL, /* write */ + autofs_dir_readdir, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations autofs_dir_inode_operations = { + &autofs_dir_operations, /* file operations */ + NULL, /* create */ + autofs_dir_lookup, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; + diff --git a/kernel/dirhash.c b/kernel/dirhash.c new file mode 100644 index 0000000..60a3c69 --- /dev/null +++ b/kernel/dirhash.c @@ -0,0 +1,160 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/dirhash.c + * + * Copyright 1997 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" + +/* Functions for maintenance of expiry queue */ + +static void autofs_init_usage(struct autofs_dirhash *dh, + struct autofs_dir_ent *ent) +{ + ent->exp_next = &dh->expiry_head; + ent->exp_prev = dh->expiry_head.exp_prev; + dh->expiry_head.exp_prev->exp_next = ent; + dh->expiry_head.exp_prev = ent; + ent->last_usage = jiffies; +} + +static void autofs_delete_usage(struct autofs_dir_ent *ent) +{ + ent->exp_prev->exp_next = ent->exp_next; + ent->exp_next->exp_prev = ent->exp_prev; +} + +void autofs_update_usage(struct autofs_dirhash *dh, + struct autofs_dir_ent *ent) +{ + autofs_delete_usage(ent); /* Unlink from current position */ + autofs_init_usage(dh,ent); /* Relink at queue tail */ +} + +struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh, + unsigned long timeout) +{ + struct autofs_dir_ent *ent; + + ent = dh->expiry_head.exp_next; + + if ( ent == &(dh->expiry_head) ) return NULL; + return (jiffies - ent->last_usage >= timeout) ? ent : NULL; +} + +void autofs_initialize_hash(struct autofs_dirhash *dh) { + memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *)); + dh->expiry_head.exp_next = dh->expiry_head.exp_prev = + &dh->expiry_head; +} + +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name) +{ + struct autofs_dir_ent *dhn; + + DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash)); + autofs_say(name->name,name->len); + + for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) { + if ( name->hash == dhn->hash && + name->len == dhn->len && + !memcmp(name->name, dhn->name, name->len) ) + break; + } + + return dhn; +} + +void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) +{ + struct autofs_dir_ent **dhnp; + + DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash)); + autofs_say(ent->name,ent->len); + + autofs_init_usage(dh,ent); + + dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; + ent->next = *dhnp; + ent->back = dhnp; + *dhnp = ent; +} + +void autofs_hash_delete(struct autofs_dir_ent *ent) +{ + *(ent->back) = ent->next; + + autofs_delete_usage(ent); + + kfree(ent->name); + kfree(ent); +} + +/* + * Used by readdir(). We must validate "ptr", so we can't simply make it + * a pointer. Values below 0xffff are reserved; calling with any value + * <= 0x10000 will return the first entry found. + */ +struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr) +{ + int bucket, ecount, i; + struct autofs_dir_ent *ent; + + bucket = (*ptr >> 16) - 1; + ecount = *ptr & 0xffff; + + if ( bucket < 0 ) { + bucket = ecount = 0; + } + + DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount)); + + ent = NULL; + + while ( bucket < AUTOFS_HASH_SIZE ) { + ent = dh->h[bucket]; + for ( i = ecount ; ent && i ; i-- ) + ent = ent->next; + + if (ent) { + ecount++; /* Point to *next* entry */ + break; + } + + bucket++; ecount = 0; + } + +#ifdef DEBUG + if ( !ent ) + printk("autofs_hash_enum: nothing found\n"); + else { + printk("autofs_hash_enum: found hash %08x, name", ent->hash); + autofs_say(ent->name,ent->len); + } +#endif + + *ptr = ((bucket+1) << 16) + ecount; + return ent; +} + +/* Delete everything. This is used on filesystem destruction, so we + make no attempt to keep the pointers valid */ +void autofs_hash_nuke(struct autofs_dirhash *dh) +{ + int i; + struct autofs_dir_ent *ent, *nent; + + for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { + for ( ent = dh->h[i] ; ent ; ent = nent ) { + nent = ent->next; + kfree(ent->name); + kfree(ent); + } + } +} diff --git a/kernel/init.c b/kernel/init.c new file mode 100644 index 0000000..7c05082 --- /dev/null +++ b/kernel/init.c @@ -0,0 +1,52 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/init.c + * + * Copyright 1997 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 file_system_type autofs_fs_type = { + "autofs", + 0, + autofs_read_super, + NULL +}; + +#ifdef MODULE +int init_module(void) +{ + return register_filesystem(&autofs_fs_type); +} + +void cleanup_module(void) +{ + unregister_filesystem(&autofs_fs_type); +} + +#else /* MODULE */ + +__initfunc(int init_autofs_fs(void)) +{ + return register_filesystem(&autofs_fs_type); +} + +#endif /* !MODULE */ + +#ifdef DEBUG +void autofs_say(const char *name, int len) +{ + printk("(%d: ", len); + while ( len-- ) + printk("%c", *name++); + printk(")\n"); +} +#endif diff --git a/kernel/inode.c b/kernel/inode.c new file mode 100644 index 0000000..cbcefc3 --- /dev/null +++ b/kernel/inode.c @@ -0,0 +1,350 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/inode.c + * + * Copyright 1997 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/malloc.h> +#include <linux/file.h> +#include <linux/locks.h> +#include <asm/bitops.h> +#include "autofs_i.h" +#define __NO_VERSION__ +#include <linux/module.h> + +/* + * Dummy functions - do we ever actually want to do + * something here? + */ +static void autofs_put_inode(struct inode *inode) +{ +} + +static void autofs_delete_inode(struct inode *inode) +{ + inode->i_size = 0; +} + +static void autofs_put_super(struct super_block *sb) +{ + struct autofs_sb_info *sbi = + (struct autofs_sb_info *) sb->u.generic_sbp; + unsigned int n; + + if ( !sbi->catatonic ) + autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ + + lock_super(sb); + autofs_hash_nuke(&sbi->dirhash); + for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) { + if ( test_bit(n, sbi->symlink_bitmap) ) + kfree(sbi->symlink[n].data); + } + + sb->s_dev = 0; + kfree(sb->u.generic_sbp); + unlock_super(sb); + + DPRINTK(("autofs: shutting down\n")); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +static void autofs_read_inode(struct inode *inode); +static void autofs_write_inode(struct inode *inode); + +static struct super_operations autofs_sops = { + autofs_read_inode, + autofs_write_inode, + autofs_put_inode, + autofs_delete_inode, + NULL, /* notify_change */ + autofs_put_super, + NULL, /* write_super */ + autofs_statfs, + NULL +}; + +static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto) +{ + char *this_char, *value; + + *uid = current->uid; + *gid = current->gid; + *pgrp = current->pgrp; + + *minproto = *maxproto = AUTOFS_PROTO_VERSION; + + *pipefd = -1; + + if ( !options ) return 1; + for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { + if ((value = strchr(this_char,'=')) != NULL) + *value++ = 0; + if (!strcmp(this_char,"fd")) { + if (!value || !*value) + return 1; + *pipefd = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"uid")) { + if (!value || !*value) + return 1; + *uid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"gid")) { + if (!value || !*value) + return 1; + *gid = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"pgrp")) { + if (!value || !*value) + return 1; + *pgrp = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"minproto")) { + if (!value || !*value) + return 1; + *minproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else if (!strcmp(this_char,"maxproto")) { + if (!value || !*value) + return 1; + *maxproto = simple_strtoul(value,&value,0); + if (*value) + return 1; + } + else break; + } + return (*pipefd < 0); +} + +struct super_block *autofs_read_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; + int minproto, maxproto; + + MOD_INC_USE_COUNT; + + lock_super(s); + /* Super block already completed? */ + if (s->s_root) + goto out_unlock; + + sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); + if ( !sbi ) + goto fail_unlock; + DPRINTK(("autofs: starting up, sbi = %p\n",sbi)); + + s->u.generic_sbp = sbi; + sbi->magic = AUTOFS_SBI_MAGIC; + sbi->catatonic = 0; + sbi->exp_timeout = 0; + sbi->oz_pgrp = current->pgrp; + autofs_initialize_hash(&sbi->dirhash); + sbi->queues = NULL; + memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN); + sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; + s->s_op = &autofs_sops; + s->s_root = NULL; + unlock_super(s); /* shouldn't we keep it locked a while longer? */ + + /* + * Get the root inode and dentry, but defer checking for errors. + */ + root_inode = iget(s, AUTOFS_ROOT_INO); + root = d_alloc_root(root_inode, NULL); + pipe = NULL; + + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_dput; + + 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 ( minproto > AUTOFS_PROTO_VERSION || + maxproto < AUTOFS_PROTO_VERSION ) { + printk("autofs: kernel does not match daemon version\n"); + goto fail_dput; + } + + DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp)); + pipe = fget(pipefd); + /* + * Check whether somebody else completed the super block. + */ + if (s->s_root) + goto out_fput; + + 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 s; + + /* + * Success ... somebody else completed the super block for us. + */ +out_unlock: + unlock_super(s); + goto out_dec; +out_fput: + if (pipe) + fput(pipe); +out_dput: + if (root) + dput(root); + else + iput(root_inode); +out_dec: + MOD_DEC_USE_COUNT; + return s; + + /* + * Failure ... clear the s_dev slot and clean up. + */ +fail_fput: + printk("autofs: pipe file descriptor does not contain proper ops\n"); + /* + * fput() can block, so we clear the super block first. + */ + s->s_dev = 0; + fput(pipe); + /* fall through */ +fail_dput: + /* + * dput() can block, so we clear the super block first. + */ + s->s_dev = 0; + dput(root); + goto fail_free; +fail_iput: + printk("autofs: get root dentry failed\n"); + /* + * iput() can block, so we clear the super block first. + */ + s->s_dev = 0; + iput(root_inode); +fail_free: + kfree(sbi); + goto fail_dec; +fail_unlock: + unlock_super(s); +fail_dec: + s->s_dev = 0; + MOD_DEC_USE_COUNT; + return NULL; +} + +static int autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) +{ + struct statfs tmp; + + tmp.f_type = AUTOFS_SUPER_MAGIC; + tmp.f_bsize = 1024; + tmp.f_blocks = 0; + tmp.f_bfree = 0; + tmp.f_bavail = 0; + tmp.f_files = 0; + tmp.f_ffree = 0; + tmp.f_namelen = NAME_MAX; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; +} + +static void autofs_read_inode(struct inode *inode) +{ + ino_t ino = inode->i_ino; + unsigned int n; + struct autofs_sb_info *sbi = + (struct autofs_sb_info *) inode->i_sb->u.generic_sbp; + + inode->i_op = NULL; + inode->i_mode = 0; + inode->i_nlink = 2; + inode->i_size = 0; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + + if ( ino == AUTOFS_ROOT_INO ) { + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR; + inode->i_op = &autofs_root_inode_operations; + inode->i_uid = inode->i_gid = 0; /* Changed in read_super */ + return; + } + + inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; + inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; + + if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) { + /* Symlink inode - should be in symlink list */ + struct autofs_symlink *sl; + + n = ino - AUTOFS_FIRST_SYMLINK; + if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) { + printk("autofs: Looking for bad symlink inode 0x%08x\n", (unsigned int) ino); + return; + } + + inode->i_op = &autofs_symlink_inode_operations; + sl = &sbi->symlink[n]; + inode->u.generic_ip = sl; + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_mtime = inode->i_ctime = sl->mtime; + inode->i_size = sl->len; + inode->i_nlink = 1; + } else { + /* All non-root directory inodes look the same */ + inode->i_op = &autofs_dir_inode_operations; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + } +} + +static void autofs_write_inode(struct inode *inode) +{ +} diff --git a/kernel/linux/auto_fs.h b/kernel/linux/auto_fs.h new file mode 100644 index 0000000..2b25d29 --- /dev/null +++ b/kernel/linux/auto_fs.h @@ -0,0 +1,62 @@ +/* -*- linux-c -*- ------------------------------------------------------- * + * + * linux/include/linux/auto_fs.h + * + * Copyright 1997 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. + * + * ----------------------------------------------------------------------- */ + + +#ifndef _LINUX_AUTO_FS_H +#define _LINUX_AUTO_FS_H + +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/limits.h> +#include <linux/ioctl.h> +#include <asm/types.h> + +#define AUTOFS_PROTO_VERSION 3 + +enum autofs_packet_type { + autofs_ptype_missing, /* Missing entry (mount request) */ + autofs_ptype_expire, /* Expire entry (umount request) */ +}; + +struct autofs_packet_hdr { + int proto_version; /* Protocol version */ + enum autofs_packet_type type; /* Type of packet */ +}; + +struct autofs_packet_missing { + struct autofs_packet_hdr hdr; + unsigned long wait_queue_token; + int len; + char name[NAME_MAX+1]; +}; + +struct autofs_packet_expire { + struct autofs_packet_hdr hdr; + int len; + char name[NAME_MAX+1]; +}; + +#define AUTOFS_IOC_READY _IO(0x93,0x60) +#define AUTOFS_IOC_FAIL _IO(0x93,0x61) +#define AUTOFS_IOC_CATATONIC _IO(0x93,0x62) +#define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int) +#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long) +#define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire) + +#ifdef __KERNEL__ + +/* Init function */ +int init_autofs_fs(void); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_AUTO_FS_H */ diff --git a/kernel/root.c b/kernel/root.c new file mode 100644 index 0000000..919e61b --- /dev/null +++ b/kernel/root.c @@ -0,0 +1,480 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/root.c + * + * Copyright 1997 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/errno.h> +#include <linux/stat.h> +#include <linux/param.h> +#include "autofs_i.h" + +static int autofs_root_readdir(struct file *,void *,filldir_t); +static int autofs_root_lookup(struct inode *,struct dentry *); +static int autofs_root_symlink(struct inode *,struct dentry *,const char *); +static int autofs_root_unlink(struct inode *,struct dentry *); +static int autofs_root_rmdir(struct inode *,struct dentry *); +static int autofs_root_mkdir(struct inode *,struct dentry *,int); +static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); + +static struct file_operations autofs_root_operations = { + NULL, /* llseek */ + NULL, /* read */ + NULL, /* write */ + autofs_root_readdir, /* readdir */ + NULL, /* poll */ + autofs_root_ioctl, /* ioctl */ + NULL, /* mmap */ + NULL, /* open */ + NULL, /* release */ + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL /* lock */ +}; + +struct inode_operations autofs_root_inode_operations = { + &autofs_root_operations, /* file operations */ + NULL, /* create */ + autofs_root_lookup, /* lookup */ + NULL, /* link */ + autofs_root_unlink, /* unlink */ + autofs_root_symlink, /* symlink */ + autofs_root_mkdir, /* mkdir */ + autofs_root_rmdir, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; + +static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct autofs_dir_ent *ent; + struct autofs_dirhash *dirhash; + struct inode * inode = filp->f_dentry->d_inode; + off_t onr, nr; + + if (!inode || !S_ISDIR(inode->i_mode)) + return -ENOTDIR; + + dirhash = &((struct autofs_sb_info *)inode->i_sb->u.generic_sbp)->dirhash; + nr = filp->f_pos; + + switch(nr) + { + case 0: + if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + default: + while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) { + if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0) + return 0; + filp->f_pos = nr; + } + break; + } + + return 0; +} + +static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi) +{ + struct inode * inode; + struct autofs_dir_ent *ent; + + while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name))) { + int status = autofs_wait(sbi, &dentry->d_name); + + /* Turn this into a real negative dentry? */ + if (status == -ENOENT) { + dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + return 1; + } else if (status) { + /* Return a negative dentry, but leave it "pending" */ + return 1; + } + } + + /* Abuse this field as a pointer to the directory entry, used to + find the expire list pointers */ + dentry->d_time = (unsigned long) ent; + + if (!dentry->d_inode) { + inode = iget(sb, ent->ino); + if (!inode) { + /* Failed, but leave pending for next time */ + return 1; + } + dentry->d_inode = inode; + } + + if (S_ISDIR(dentry->d_inode->i_mode)) { + while (dentry == dentry->d_mounts) + schedule(); + } + + autofs_update_usage(&sbi->dirhash,ent); + + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + 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 autofs_revalidate(struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode * dir = dentry->d_parent->d_inode; + struct autofs_dir_ent *ent; + + sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + + /* Pending dentry */ + if (dentry->d_flags & DCACHE_AUTOFS_PENDING) { + if (autofs_oz_mode(sbi)) + return 1; + + return try_to_fill_dentry(dentry, dir->i_sb, sbi); + } + + /* Negative dentry.. invalidate if "old" */ + if (!dentry->d_inode) + return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); + + /* Update the usage list */ + ent = (struct autofs_dir_ent *) dentry->d_time; + autofs_update_usage(&sbi->dirhash,ent); + return 1; +} + +static struct dentry_operations autofs_dentry_operations = { + autofs_revalidate, + NULL, /* d_hash */ + NULL, /* d_compare */ +}; + +static int autofs_root_lookup(struct inode *dir, struct dentry * dentry) +{ + struct autofs_sb_info *sbi; + struct inode *res; + int oz_mode; + + DPRINTK(("autofs_root_lookup: name = ")); + autofs_say(dentry->d_name.name,dentry->d_name.len); + + if (!S_ISDIR(dir->i_mode)) + return -ENOTDIR; + + res = NULL; + sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + + oz_mode = autofs_oz_mode(sbi); + DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, 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 = &autofs_dentry_operations; + dentry->d_flags |= DCACHE_AUTOFS_PENDING; + d_add(dentry, NULL); + + up(&dir->i_sem); + autofs_revalidate(dentry); + 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) { + if (signal_pending(current)) + return -ERESTARTNOINTR; + } + + return 0; +} + +static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + struct autofs_dirhash *dh = &sbi->dirhash; + struct autofs_dir_ent *ent; + unsigned int n; + int slsize; + struct autofs_symlink *sl; + + DPRINTK(("autofs_root_symlink: %s <- ", symname)); + autofs_say(dentry->d_name.name,dentry->d_name.len); + + if ( !autofs_oz_mode(sbi) ) + return -EPERM; + + if ( autofs_hash_lookup(dh, &dentry->d_name) ) + return -EEXIST; + + n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); + if ( n >= AUTOFS_MAX_SYMLINKS ) + return -ENOSPC; + + set_bit(n,sbi->symlink_bitmap); + sl = &sbi->symlink[n]; + sl->len = strlen(symname); + sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL); + if ( !sl->data ) { + clear_bit(n,sbi->symlink_bitmap); + return -ENOSPC; + } + + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); + if ( !ent ) { + kfree(sl->data); + clear_bit(n,sbi->symlink_bitmap); + return -ENOSPC; + } + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); + if ( !ent->name ) { + kfree(sl->data); + kfree(ent); + clear_bit(n,sbi->symlink_bitmap); + return -ENOSPC; + } + + memcpy(sl->data,symname,slsize); + sl->mtime = CURRENT_TIME; + + ent->ino = AUTOFS_FIRST_SYMLINK + n; + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name,ent->len = dentry->d_name.len); + + autofs_hash_insert(dh,ent); + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); + + 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, which we + * obviously do not want (we're dropping the entry not because it + * doesn't exist, but because it has timed out). + * + * Also see autofs_root_rmdir().. + */ +static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) +{ + struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + struct autofs_dirhash *dh = &sbi->dirhash; + struct autofs_dir_ent *ent; + unsigned int n; + + if ( !autofs_oz_mode(sbi) ) + return -EPERM; + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( !ent ) + return -ENOENT; + + n = ent->ino - AUTOFS_FIRST_SYMLINK; + if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) ) + return -EINVAL; /* Not a symlink inode, can't unlink */ + + autofs_hash_delete(ent); + clear_bit(n,sbi->symlink_bitmap); + kfree(sbi->symlink[n].data); + d_drop(dentry); + + return 0; +} + +static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + struct autofs_dirhash *dh = &sbi->dirhash; + struct autofs_dir_ent *ent; + + if ( !autofs_oz_mode(sbi) ) + return -EPERM; + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( !ent ) + return -ENOENT; + + if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) + return -ENOTDIR; /* Not a directory */ + + autofs_hash_delete(ent); + dir->i_nlink--; + d_drop(dentry); + + return 0; +} + +static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp; + struct autofs_dirhash *dh = &sbi->dirhash; + struct autofs_dir_ent *ent; + + if ( !autofs_oz_mode(sbi) ) + return -EPERM; + + ent = autofs_hash_lookup(dh, &dentry->d_name); + if ( ent ) + return -EEXIST; + + if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { + printk("autofs: Out of inode numbers -- what the heck did you do??\n"); + return -ENOSPC; + } + + ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); + if ( !ent ) + return -ENOSPC; + + ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL); + if ( !ent->name ) { + kfree(ent); + return -ENOSPC; + } + + ent->hash = dentry->d_name.hash; + memcpy(ent->name, dentry->d_name.name, ent->len = dentry->d_name.len); + ent->ino = sbi->next_dir_ino++; + autofs_hash_insert(dh,ent); + dir->i_nlink++; + d_instantiate(dentry, iget(dir->i_sb,ent->ino)); + + return 0; +} + +/* Get/set timeout ioctl() operation */ +static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi, + unsigned long *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 autofs_get_protover(int *p) +{ + return put_user(AUTOFS_PROTO_VERSION, p); +} + +/* Perform an expiry operation */ +static inline int autofs_expire_run(struct autofs_sb_info *sbi, + struct autofs_packet_expire *pkt_p) +{ + struct autofs_dir_ent *ent; + struct autofs_packet_expire pkt; + struct autofs_dirhash *dh = &(sbi->dirhash); + + memset(&pkt,0,sizeof pkt); + + pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; + pkt.hdr.type = autofs_ptype_expire; + + if ( !sbi->exp_timeout || + !(ent = autofs_expire(dh,sbi->exp_timeout)) ) + return -EAGAIN; + + pkt.len = ent->len; + memcpy(pkt.name, ent->name, pkt.len); + pkt.name[pkt.len] = '\0'; + + if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) + return -EFAULT; + + autofs_update_usage(dh,ent); + + return 0; +} + +/* + * ioctl()'s on the root directory is the chief method for the daemon to + * generate kernel reactions + */ +static int autofs_root_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct autofs_sb_info *sbi = + (struct autofs_sb_info *)inode->i_sb->u.generic_sbp; + + DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp)); + + if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) || + _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT ) + return -ENOTTY; + + if ( !autofs_oz_mode(sbi) && !fsuser() ) + return -EPERM; + + switch(cmd) { + case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */ + return autofs_wait_release(sbi,arg,0); + case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */ + return autofs_wait_release(sbi,arg,-ENOENT); + case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */ + autofs_catatonic_mode(sbi); + return 0; + case AUTOFS_IOC_PROTOVER: /* Get protocol version */ + return autofs_get_protover((int *)arg); + case AUTOFS_IOC_SETTIMEOUT: + return autofs_get_set_timeout(sbi,(unsigned long *)arg); + case AUTOFS_IOC_EXPIRE: + return autofs_expire_run(sbi,(struct autofs_packet_expire *)arg); + default: + return -ENOSYS; + } +} diff --git a/kernel/symlink.c b/kernel/symlink.c new file mode 100644 index 0000000..b42955f --- /dev/null +++ b/kernel/symlink.c @@ -0,0 +1,58 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/symlink.c + * + * Copyright 1997 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/string.h> +#include <linux/sched.h> +#include "autofs_i.h" + +static int autofs_readlink(struct inode *inode, char *buffer, int buflen) +{ + struct autofs_symlink *sl; + int len; + + sl = (struct autofs_symlink *)inode->u.generic_ip; + len = sl->len; + if (len > buflen) len = buflen; + copy_to_user(buffer,sl->data,len); + return len; +} + +static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base) +{ + struct autofs_symlink *sl; + + sl = (struct autofs_symlink *)inode->u.generic_ip; + return lookup_dentry(sl->data, base, 1); +} + +struct inode_operations autofs_symlink_inode_operations = { + NULL, /* file operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + autofs_readlink, /* readlink */ + autofs_follow_link, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL, /* updatepage */ + NULL /* revalidate */ +}; diff --git a/kernel/waitq.c b/kernel/waitq.c new file mode 100644 index 0000000..e56825d --- /dev/null +++ b/kernel/waitq.c @@ -0,0 +1,171 @@ +/* -*- linux-c -*- --------------------------------------------------------- * + * + * linux/fs/autofs/waitq.c + * + * Copyright 1997 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/malloc.h> +#include <linux/sched.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 int autofs_next_wait_queue = 1; + +void autofs_catatonic_mode(struct autofs_sb_info *sbi) +{ + struct autofs_wait_queue *wq, *nwq; + + DPRINTK(("autofs: entering catatonic mode\n")); + + 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(&wq->queue); + wq = nwq; + } + fput(sbi->pipe); /* Close the pipe */ +} + +static int autofs_write(struct file *file, const void *addr, int bytes) +{ + unsigned long fs; + unsigned long old_signal; + const char *data = (const char *)addr; + int written = 0; + + /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ + + /* Save pointer to user space and point back to kernel space */ + fs = get_fs(); + set_fs(KERNEL_DS); + + old_signal = current->signal; + + while ( bytes && (written = file->f_op->write(file->f_dentry->d_inode,file,data,bytes)) > 0 ) { + data += written; + bytes -= written; + } + + if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) { + /* Keep the currently executing process from receiving a + SIGPIPE unless it was already supposed to get one */ + current->signal &= ~(1 << (SIGPIPE-1)); + } + set_fs(fs); + + return (bytes > 0); +} + +static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq) +{ + struct autofs_packet_missing pkt; + + DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token)); + autofs_say(wq->name,wq->len); + + memset(&pkt,0,sizeof pkt); /* For security reasons */ + + pkt.hdr.proto_version = AUTOFS_PROTO_VERSION; + pkt.hdr.type = autofs_ptype_missing; + pkt.wait_queue_token = wq->wait_queue_token; + pkt.len = wq->len; + memcpy(pkt.name, wq->name, pkt.len); + pkt.name[pkt.len] = '\0'; + + if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) ) + autofs_catatonic_mode(sbi); +} + +int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name) +{ + struct autofs_wait_queue *wq; + int status; + + for ( wq = sbi->queues ; wq ; wq = wq->next ) { + if ( wq->hash == name->hash && + wq->len == name->len && + wq->name && !memcmp(wq->name,name->name,name->len) ) + break; + } + + if ( !wq ) { + /* Create a new wait queue */ + wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); + if ( !wq ) + return -ENOMEM; + + wq->name = kmalloc(name->len,GFP_KERNEL); + if ( !wq->name ) { + kfree(wq); + return -ENOMEM; + } + wq->wait_queue_token = autofs_next_wait_queue++; + init_waitqueue(&wq->queue); + wq->hash = name->hash; + wq->len = name->len; + wq->status = -EINTR; /* Status return if interrupted */ + memcpy(wq->name, name->name, name->len); + wq->next = sbi->queues; + sbi->queues = wq; + + /* autofs_notify_daemon() may block */ + wq->wait_ctr = 2; + autofs_notify_daemon(sbi,wq); + } else + wq->wait_ctr++; + + if ( wq->name ) { + /* wq->name is NULL if and only if the lock is released */ + interruptible_sleep_on(&wq->queue); + } else { + DPRINTK(("autofs_wait: skipped sleeping\n")); + } + + status = wq->status; + + if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */ + kfree(wq); + + return status; +} + + +int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status) +{ + struct autofs_wait_queue *wq, **wql; + + for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) { + if ( wq->wait_queue_token == wait_queue_token ) + break; + } + if ( !wq ) + return -EINVAL; + + *wql = wq->next; /* Unlink from chain */ + kfree(wq->name); + wq->name = NULL; /* Do not wait on this queue */ + + wq->status = status; + + if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */ + kfree(wq); + else + wake_up(&wq->queue); + + return 0; +} + |