diff options
Diffstat (limited to 'fs/autofs')
-rw-r--r-- | fs/autofs/.cvsignore | 1 | ||||
-rw-r--r-- | fs/autofs/Makefile | 29 | ||||
-rw-r--r-- | fs/autofs/autofs_i.h | 175 | ||||
-rw-r--r-- | fs/autofs/dir.c | 2 | ||||
-rw-r--r-- | fs/autofs/dirhash.c | 49 | ||||
-rw-r--r-- | fs/autofs/init.c | 27 | ||||
-rw-r--r-- | fs/autofs/inode.c | 11 | ||||
-rw-r--r-- | fs/autofs/root.c | 106 | ||||
-rw-r--r-- | fs/autofs/symlink.c | 2 | ||||
-rw-r--r-- | fs/autofs/waitq.c | 18 |
10 files changed, 375 insertions, 45 deletions
diff --git a/fs/autofs/.cvsignore b/fs/autofs/.cvsignore new file mode 100644 index 000000000..4671378ae --- /dev/null +++ b/fs/autofs/.cvsignore @@ -0,0 +1 @@ +.depend diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile index 12f302635..1681c3d31 100644 --- a/fs/autofs/Makefile +++ b/fs/autofs/Makefile @@ -1,11 +1,7 @@ # # 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... +# We can build this either out of the kernel tree or the autofs tools tree. # O_TARGET := autofs.o @@ -13,4 +9,27 @@ 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/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h new file mode 100644 index 000000000..d3b6e484f --- /dev/null +++ b/fs/autofs/autofs_i.h @@ -0,0 +1,175 @@ +/* -*- 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> + +#define kver(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#if LINUX_VERSION_CODE < kver(2,1,0) + +/* Segmentation stuff for pre-2.1 kernels */ +#include <asm/segment.h> + +static inline int copy_to_user(void *dst, void *src, unsigned long len) +{ + int rv = verify_area(VERIFY_WRITE, dst, len); + if ( rv ) + return -1; + memcpy_tofs(dst,src,len); + return 0; +} + +static inline int copy_from_user(void *dst, void *src, unsigned long len) +{ + int rv = verify_area(VERIFY_READ, src, len); + if ( rv ) + return -1; + memcpy_fromfs(dst,src,len); + return 0; +} + +#else + +/* Segmentation stuff for post-2.1 kernels */ +#include <asm/uaccess.h> +#define register_symtab(x) ((void)0) + +#endif + +#ifdef DEBUG +#define DPRINTK(D) printk D; +#else +#define DPRINTK(D) +#endif + +#define AUTOFS_SUPER_MAGIC 0x0187 + +/* Structures associated with the root directory hash */ + +#define AUTOFS_HASH_SIZE 67 + +typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */ + +struct autofs_dir_ent { + autofs_hash_t 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 */ + autofs_hash_t 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 + +struct autofs_sb_info { + 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? */ +static inline int autofs_oz_mode(struct autofs_sb_info *sbi) { + return sbi->catatonic || current->pgrp == sbi->oz_pgrp; +} + +/* Hash operations */ + +autofs_hash_t autofs_hash(const char *,int); +void autofs_initialize_hash(struct autofs_dirhash *); +struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int); +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 *,autofs_hash_t,const char *,int); +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) +#endif diff --git a/fs/autofs/dir.c b/fs/autofs/dir.c index 22081d1a7..461688e9f 100644 --- a/fs/autofs/dir.c +++ b/fs/autofs/dir.c @@ -10,7 +10,7 @@ * * ------------------------------------------------------------------------- */ -#include <linux/auto_fs.h> +#include "autofs_i.h" static int autofs_dir_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir) diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 8ea5325c4..90c18695a 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -10,9 +10,43 @@ * * ------------------------------------------------------------------------- */ -#include <linux/string.h> -#include <linux/malloc.h> -#include <linux/auto_fs.h> +#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; +} /* Adapted from the Dragon Book, page 436 */ /* This particular hashing algorithm requires autofs_hash_t == u32 */ @@ -28,6 +62,8 @@ autofs_hash_t autofs_hash(const char *name, int len) 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, autofs_hash_t hash, const char *name, int len) @@ -54,6 +90,8 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash)); autofs_say(ent->name,ent->len); + autofs_init_usage(dh,ent); + dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE]; ent->next = *dhnp; ent->back = dhnp; @@ -63,6 +101,9 @@ void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent) void autofs_hash_delete(struct autofs_dir_ent *ent) { *(ent->back) = ent->next; + + autofs_delete_usage(ent); + kfree(ent->name); kfree(ent); } @@ -114,6 +155,8 @@ struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t * 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; diff --git a/fs/autofs/init.c b/fs/autofs/init.c index a4857cb99..1b3f6f165 100644 --- a/fs/autofs/init.c +++ b/fs/autofs/init.c @@ -11,23 +11,24 @@ * ------------------------------------------------------------------------- */ #include <linux/module.h> -#include <linux/auto_fs.h> +#include "autofs_i.h" -struct file_system_type autofs_fs_type = { +#if LINUX_VERSION_CODE < kver(2,1,36) +#define __initfunc(X) X +#else +#include <linux/init.h> +#endif + +static 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) + if ((status = register_filesystem(&autofs_fs_type)) == 0) register_symtab(0); return status; } @@ -36,7 +37,15 @@ void cleanup_module(void) { unregister_filesystem(&autofs_fs_type); } -#endif + +#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) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 60b805a07..a8c176a02 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -15,7 +15,7 @@ #include <linux/file.h> #include <linux/locks.h> #include <asm/bitops.h> -#include <linux/auto_fs.h> +#include "autofs_i.h" #define __NO_VERSION__ #include <linux/module.h> @@ -28,17 +28,19 @@ static void autofs_put_inode(struct inode *inode) static void autofs_put_super(struct super_block *sb) { - struct autofs_sb_info *sbi; + 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); - 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); @@ -149,6 +151,7 @@ struct super_block *autofs_read_super(struct super_block *s, void *data, s->u.generic_sbp = sbi; sbi->catatonic = 0; + sbi->exp_timeout = 0; sbi->oz_pgrp = current->pgrp; autofs_initialize_hash(&sbi->dirhash); sbi->queues = NULL; diff --git a/fs/autofs/root.c b/fs/autofs/root.c index d9056dcb1..57449e816 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -12,9 +12,8 @@ #include <linux/errno.h> #include <linux/stat.h> -#include <linux/malloc.h> -#include <linux/ioctl.h> -#include <linux/auto_fs.h> +#include <linux/param.h> +#include "autofs_i.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 **); @@ -171,7 +170,8 @@ static int autofs_root_lookup(struct inode *dir, const char *name, int len, } } } while(!res); - + autofs_update_usage(&sbi->dirhash,ent); + *result = res; iput(dir); return 0; @@ -229,7 +229,6 @@ static int autofs_root_symlink(struct inode *dir, const char *name, int len, con 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); @@ -322,7 +321,6 @@ static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int m 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); @@ -330,6 +328,75 @@ static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int m 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 LINUX_VERSION_CODE < kver(2,1,0) + if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(unsigned long))) ) + return rv; + ntimeout = get_user(p); + put_user(sbi->exp_timeout/HZ, p); +#else + if ( (rv = get_user(ntimeout, p)) || + (rv = put_user(sbi->exp_timeout/HZ, p)) ) + return rv; +#endif + + 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) +{ +#if LINUX_VERSION_CODE < kver(2,1,0) + int rv; + if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(int))) ) + return rv; + put_user(AUTOFS_PROTO_VERSION, p); + return 0; +#else + return put_user(AUTOFS_PROTO_VERSION, p); +#endif +} + +/* 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 @@ -337,26 +404,33 @@ static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int m 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; + 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)); + 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 */ - 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; + 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 -ENOTTY; /* Should this be ENOSYS? */ + return -ENOSYS; } } diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c index 0e932c169..46c333103 100644 --- a/fs/autofs/symlink.c +++ b/fs/autofs/symlink.c @@ -12,7 +12,7 @@ #include <linux/string.h> #include <linux/sched.h> -#include <linux/auto_fs.h> +#include "autofs_i.h" static int autofs_follow_link(struct inode *dir, struct inode *inode, int flag, int mode, struct inode **res_inode) diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c index 6dc6d0b13..b37745f19 100644 --- a/fs/autofs/waitq.c +++ b/fs/autofs/waitq.c @@ -11,9 +11,10 @@ * ------------------------------------------------------------------------- */ #include <linux/malloc.h> -#include <linux/signal.h> #include <linux/sched.h> -#include <linux/auto_fs.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 */ @@ -36,6 +37,7 @@ void autofs_catatonic_mode(struct autofs_sb_info *sbi) wake_up(&wq->queue); wq = nwq; } + fput(sbi->pipe, sbi->pipe->f_inode); /* Close the pipe */ } static int autofs_write(struct file *file, const void *addr, int bytes) @@ -43,7 +45,7 @@ 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; + int written = 0; /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/ @@ -75,6 +77,8 @@ static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_ 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; @@ -94,7 +98,7 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name for ( wq = sbi->queues ; wq ; wq = wq->next ) { if ( wq->hash == hash && wq->len == len && - !memcmp(wq->name,name,len) ) + wq->name && !memcmp(wq->name,name,len) ) break; } @@ -113,12 +117,13 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name init_waitqueue(&wq->queue); wq->hash = hash; wq->len = len; + wq->status = -EINTR; /* Status return if interrupted */ memcpy(wq->name, name, len); wq->next = sbi->queues; sbi->queues = wq; /* autofs_notify_daemon() may block */ - wq->wait_ctr++; + wq->wait_ctr = 1; autofs_notify_daemon(sbi,wq); } else wq->wait_ctr++; @@ -130,7 +135,8 @@ int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name DPRINTK(("autofs_wait: skipped sleeping\n")); } - status = (current->signal & ~current->blocked) ? -EINTR : wq->status; + status = wq->status; + if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */ kfree(wq); |