diff options
Diffstat (limited to 'fs/autofs')
-rw-r--r-- | fs/autofs/Makefile | 16 | ||||
-rw-r--r-- | fs/autofs/dir.c | 90 | ||||
-rw-r--r-- | fs/autofs/dirhash.c | 129 | ||||
-rw-r--r-- | fs/autofs/init.c | 49 | ||||
-rw-r--r-- | fs/autofs/inode.c | 273 | ||||
-rw-r--r-- | fs/autofs/root.c | 362 | ||||
-rw-r--r-- | fs/autofs/symlink.c | 85 | ||||
-rw-r--r-- | fs/autofs/waitq.c | 162 |
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; +} + |