summaryrefslogtreecommitdiffstats
path: root/fs/autofs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/autofs')
-rw-r--r--fs/autofs/Makefile16
-rw-r--r--fs/autofs/dir.c90
-rw-r--r--fs/autofs/dirhash.c129
-rw-r--r--fs/autofs/init.c49
-rw-r--r--fs/autofs/inode.c273
-rw-r--r--fs/autofs/root.c362
-rw-r--r--fs/autofs/symlink.c85
-rw-r--r--fs/autofs/waitq.c162
8 files changed, 1166 insertions, 0 deletions
diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile
new file mode 100644
index 000000000..12f302635
--- /dev/null
+++ b/fs/autofs/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+#
+
+O_TARGET := autofs.o
+O_OBJS := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o
+
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c
new file mode 100644
index 000000000..22081d1a7
--- /dev/null
+++ b/fs/autofs/dir.c
@@ -0,0 +1,90 @@
+/* -*- 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 <linux/auto_fs.h>
+
+static int autofs_dir_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ 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;
+}
+
+static int autofs_dir_lookup(struct inode *dir, const char *name, int len,
+ struct inode **result)
+{
+ *result = dir;
+ if (!len)
+ return 0;
+ if (name[0] == '.') {
+ if (len == 1)
+ return 0;
+ if (name[1] == '.' && len == 2) {
+ /* Return the root directory */
+ *result = iget(dir->i_sb,AUTOFS_ROOT_INO);
+ iput(dir);
+ return 0;
+ }
+ }
+ *result = NULL;
+ iput(dir);
+ return -ENOENT; /* No other entries */
+}
+
+static struct file_operations autofs_dir_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ autofs_dir_readdir, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+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, /* read_page */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c
new file mode 100644
index 000000000..8ea5325c4
--- /dev/null
+++ b/fs/autofs/dirhash.c
@@ -0,0 +1,129 @@
+/* -*- 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 <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/auto_fs.h>
+
+/* Adapted from the Dragon Book, page 436 */
+/* This particular hashing algorithm requires autofs_hash_t == u32 */
+autofs_hash_t autofs_hash(const char *name, int len)
+{
+ autofs_hash_t h = 0;
+ while ( len-- ) {
+ h = (h << 4) + (unsigned char) (*name++);
+ h ^= ((h & 0xf0000000) >> 24);
+ }
+ return h;
+}
+
+void autofs_initialize_hash(struct autofs_dirhash *dh) {
+ memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
+}
+
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len)
+{
+ struct autofs_dir_ent *dhn;
+
+ DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash));
+ autofs_say(name,len);
+
+ for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
+ if ( hash == dhn->hash &&
+ len == dhn->len &&
+ !memcmp(name, dhn->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);
+
+ dhnp = &dh->h[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;
+ 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;
+}
+
+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/fs/autofs/init.c b/fs/autofs/init.c
new file mode 100644
index 000000000..a4857cb99
--- /dev/null
+++ b/fs/autofs/init.c
@@ -0,0 +1,49 @@
+/* -*- 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/auto_fs.h>
+
+struct file_system_type autofs_fs_type = {
+ autofs_read_super, "autofs", 0, NULL
+};
+
+int init_autofs_fs(void)
+{
+ return register_filesystem(&autofs_fs_type);
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ int status;
+
+ if ((status = init_autofs_fs()) == 0)
+ register_symtab(0);
+ return status;
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&autofs_fs_type);
+}
+#endif
+
+#ifdef DEBUG
+void autofs_say(const char *name, int len)
+{
+ printk("(%d: ", len);
+ while ( len-- )
+ printk("%c", *name++);
+ printk(")\n");
+}
+#endif
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
new file mode 100644
index 000000000..60b805a07
--- /dev/null
+++ b/fs/autofs/inode.c
@@ -0,0 +1,273 @@
+/* -*- 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 <linux/auto_fs.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+
+static void autofs_put_inode(struct inode *inode)
+{
+ if (inode->i_nlink)
+ return;
+ inode->i_size = 0;
+}
+
+static void autofs_put_super(struct super_block *sb)
+{
+ struct autofs_sb_info *sbi;
+ unsigned int n;
+
+ lock_super(sb);
+ sbi = (struct autofs_sb_info *) sb->u.generic_sbp;
+ 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);
+ }
+ fput(sbi->pipe, sbi->pipe->f_inode);
+
+ 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 void 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,
+ NULL,
+ autofs_write_inode,
+ autofs_put_inode,
+ autofs_put_super,
+ NULL,
+ 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)
+{
+ int pipefd;
+ struct autofs_sb_info *sbi;
+ int minproto, maxproto;
+
+ MOD_INC_USE_COUNT;
+
+ lock_super(s);
+ sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
+ if ( !sbi ) {
+ s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
+
+ s->u.generic_sbp = sbi;
+ sbi->catatonic = 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;
+ unlock_super(s);
+ if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) {
+ s->s_dev = 0;
+ kfree(sbi);
+ printk("autofs: get root inode failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+ iput(s->s_mounted);
+ s->s_dev = 0;
+ kfree(sbi);
+ printk("autofs: called with bogus options\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
+ iput(s->s_mounted);
+ s->s_dev = 0;
+ kfree(sbi);
+ printk("autofs: kernel does not match daemon version\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+
+ DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
+ sbi->pipe = fget(pipefd);
+ if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) {
+ if ( sbi->pipe ) {
+ fput(sbi->pipe, sbi->pipe->f_inode);
+ printk("autofs: pipe file descriptor does not contain proper ops\n");
+ } else {
+ printk("autofs: could not open pipe file descriptor\n");
+ }
+ iput(s->s_mounted);
+ s->s_dev = 0;
+ kfree(sbi);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+ return s;
+}
+
+static void 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;
+ copy_to_user(buf, &tmp, bufsiz);
+}
+
+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_mounted->i_uid;
+ inode->i_gid = inode->i_sb->s_mounted->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)
+{
+ inode->i_dirt = 0;
+}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
new file mode 100644
index 000000000..d9056dcb1
--- /dev/null
+++ b/fs/autofs/root.c
@@ -0,0 +1,362 @@
+/* -*- 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/malloc.h>
+#include <linux/ioctl.h>
+#include <linux/auto_fs.h>
+
+static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t);
+static int autofs_root_lookup(struct inode *,const char *,int,struct inode **);
+static int autofs_root_symlink(struct inode *,const char *,int,const char *);
+static int autofs_root_unlink(struct inode *,const char *,int);
+static int autofs_root_rmdir(struct inode *,const char *,int);
+static int autofs_root_mkdir(struct inode *,const char *,int,int);
+static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
+
+static struct file_operations autofs_root_operations = {
+ NULL, /* lseek */
+ NULL, /* read */
+ NULL, /* write */
+ autofs_root_readdir, /* readdir */
+ NULL, /* select */
+ autofs_root_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* open */
+ NULL, /* release */
+ NULL /* fsync */
+};
+
+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 */
+};
+
+static int autofs_root_readdir(struct inode *inode, struct file *filp,
+ void *dirent, filldir_t filldir)
+{
+ struct autofs_dir_ent *ent;
+ struct autofs_dirhash *dirhash;
+ 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 autofs_root_lookup(struct inode *dir, const char *name, int len,
+ struct inode **result)
+{
+ struct autofs_sb_info *sbi;
+ struct autofs_dir_ent *ent;
+ struct inode *res;
+ autofs_hash_t hash;
+ int status, oz_mode;
+
+ DPRINTK(("autofs_root_lookup: name = "));
+ autofs_say(name,len);
+
+ *result = NULL;
+ if (!dir)
+ return -ENOENT;
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return -ENOTDIR;
+ }
+
+ /* Handle special cases: . and ..; since this is a root directory,
+ they both point to the inode itself */
+ *result = dir;
+ if (!len)
+ return 0;
+ if (name[0] == '.') {
+ if (len == 1)
+ return 0;
+ if (name[1] == '.' && len == 2)
+ return 0;
+ }
+
+ *result = res = NULL;
+ sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+
+ hash = autofs_hash(name,len);
+
+ 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));
+
+ do {
+ while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
+ DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
+
+ if ( oz_mode ) {
+ iput(dir);
+ return -ENOENT;
+ } else {
+ status = autofs_wait(sbi,hash,name,len);
+ DPRINTK(("autofs_wait returned %d\n", status));
+ if ( status ) {
+ iput(dir);
+ return status;
+ }
+ }
+ }
+
+ DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
+
+ if (!(res = iget(dir->i_sb,ent->ino))) {
+ printk("autofs: iget returned null!\n");
+ iput(dir);
+ return -EACCES;
+ }
+
+ if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) {
+ /* Not a mount point yet, call 1-800-DAEMON */
+ DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp));
+ iput(res);
+ res = NULL;
+ status = autofs_wait(sbi,hash,name,len);
+ if ( status ) {
+ iput(dir);
+ return status;
+ }
+ }
+ } while(!res);
+
+ *result = res;
+ iput(dir);
+ return 0;
+}
+
+static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ autofs_hash_t hash = autofs_hash(name,len);
+ struct autofs_dir_ent *ent;
+ unsigned int n;
+ int slsize;
+ struct autofs_symlink *sl;
+
+ DPRINTK(("autofs_root_symlink: %s <- ", symname));
+ autofs_say(name,len);
+
+ iput(dir);
+
+ if ( !autofs_oz_mode(sbi) )
+ return -EPERM;
+
+ if ( autofs_hash_lookup(dh,hash,name,len) )
+ 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(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 = hash;
+ memcpy(ent->name,name,ent->len = len);
+ ent->expiry = END_OF_TIME;
+
+ autofs_hash_insert(dh,ent);
+
+ return 0;
+}
+
+static int autofs_root_unlink(struct inode *dir, const char *name, int len)
+{
+ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ autofs_hash_t hash = autofs_hash(name,len);
+ struct autofs_dir_ent *ent;
+ unsigned int n;
+
+ if ( !autofs_oz_mode(sbi) )
+ return -EPERM;
+
+ ent = autofs_hash_lookup(dh,hash,name,len);
+ 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);
+
+ return 0;
+}
+
+static int autofs_root_rmdir(struct inode *dir, const char *name, int len)
+{
+ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ autofs_hash_t hash = autofs_hash(name,len);
+ struct autofs_dir_ent *ent;
+
+ if ( !autofs_oz_mode(sbi) ) {
+ iput(dir);
+ return -EPERM;
+ }
+ ent = autofs_hash_lookup(dh,hash,name,len);
+ if ( !ent ) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) {
+ iput(dir);
+ return -ENOTDIR; /* Not a directory */
+ }
+ autofs_hash_delete(ent);
+ dir->i_nlink--;
+ iput(dir);
+
+ return 0;
+}
+
+static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
+ struct autofs_dirhash *dh = &sbi->dirhash;
+ autofs_hash_t hash = autofs_hash(name,len);
+ struct autofs_dir_ent *ent;
+
+ if ( !autofs_oz_mode(sbi) ) {
+ iput(dir);
+ return -EPERM;
+ }
+ ent = autofs_hash_lookup(dh,hash,name,len);
+ if ( ent ) {
+ iput(dir);
+ return -EEXIST;
+ }
+ if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
+ printk("autofs: Out of inode numbers -- what the heck did you do??\n");
+ iput(dir);
+ return -ENOSPC;
+ }
+ ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
+ if ( !ent ) {
+ iput(dir);
+ return -ENOSPC;
+ }
+ ent->name = kmalloc(len, GFP_KERNEL);
+ if ( !ent->name ) {
+ kfree(ent);
+ iput(dir);
+ return -ENOSPC;
+ }
+ ent->hash = hash;
+ memcpy(ent->name, name, ent->len = len);
+ ent->ino = sbi->next_dir_ino++;
+ ent->expiry = END_OF_TIME;
+ autofs_hash_insert(dh,ent);
+ dir->i_nlink++;
+ iput(dir);
+
+ 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 = %04x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp));
+
+ switch(cmd) {
+ case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
+ if ( !autofs_oz_mode(sbi) && !fsuser() )
+ return -EPERM;
+ return autofs_wait_release(sbi,arg,0);
+ case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
+ /* Optional: add to failure cache */
+ if ( !autofs_oz_mode(sbi) && !fsuser() )
+ return -EPERM;
+ return autofs_wait_release(sbi,arg,-ENOENT);
+ case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+ if ( !autofs_oz_mode(sbi) && !fsuser() )
+ return -EPERM;
+ autofs_catatonic_mode(sbi);
+ return 0;
+ default:
+ return -ENOTTY; /* Should this be ENOSYS? */
+ }
+}
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
new file mode 100644
index 000000000..0e932c169
--- /dev/null
+++ b/fs/autofs/symlink.c
@@ -0,0 +1,85 @@
+/* -*- 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 <linux/auto_fs.h>
+
+static int autofs_follow_link(struct inode *dir, struct inode *inode,
+ int flag, int mode, struct inode **res_inode)
+{
+ int error;
+ char *link;
+
+ *res_inode = NULL;
+ if (!dir) {
+ dir = current->fs->root;
+ dir->i_count++;
+ }
+ if (!inode) {
+ iput(dir);
+ return -ENOENT;
+ }
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(dir);
+ *res_inode = inode;
+ return 0;
+ }
+ if (current->link_count > 5) {
+ iput(dir);
+ iput(inode);
+ return -ELOOP;
+ }
+ link = ((struct autofs_symlink *)inode->u.generic_ip)->data;
+ current->link_count++;
+ error = open_namei(link,flag,mode,res_inode,dir);
+ current->link_count--;
+ iput(inode);
+ return error;
+}
+
+static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
+{
+ struct autofs_symlink *sl;
+ int len;
+
+ if (!S_ISLNK(inode->i_mode)) {
+ iput(inode);
+ return -EINVAL;
+ }
+ sl = (struct autofs_symlink *)inode->u.generic_ip;
+ len = sl->len;
+ if (len > buflen) len = buflen;
+ copy_to_user(buffer,sl->data,len);
+ iput(inode);
+ return len;
+}
+
+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 */
+};
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
new file mode 100644
index 000000000..6dc6d0b13
--- /dev/null
+++ b/fs/autofs/waitq.c
@@ -0,0 +1,162 @@
+/* -*- 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/signal.h>
+#include <linux/sched.h>
+#include <linux/auto_fs.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;
+ }
+}
+
+static int autofs_write(struct file *file, const void *addr, int bytes)
+{
+ unsigned short fs;
+ unsigned long old_signal;
+ const char *data = (const char *)addr;
+ int written;
+
+ /** 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_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);
+
+ 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, autofs_hash_t hash, const char *name, int len)
+{
+ struct autofs_wait_queue *wq;
+ int status;
+
+ for ( wq = sbi->queues ; wq ; wq = wq->next ) {
+ if ( wq->hash == hash &&
+ wq->len == len &&
+ !memcmp(wq->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(len,GFP_KERNEL);
+ if ( !wq->name ) {
+ kfree(wq);
+ return -ENOMEM;
+ }
+ wq->wait_queue_token = autofs_next_wait_queue++;
+ init_waitqueue(&wq->queue);
+ wq->hash = hash;
+ wq->len = len;
+ memcpy(wq->name, name, len);
+ wq->next = sbi->queues;
+ sbi->queues = wq;
+
+ /* autofs_notify_daemon() may block */
+ wq->wait_ctr++;
+ 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 = (current->signal & ~current->blocked) ? -EINTR : 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;
+
+ wake_up(&wq->queue);
+
+ return 0;
+}
+