diff -ruN autofs-4.0.0-pre1/COPYRIGHT autofs-4.0.0-pre2/COPYRIGHT --- autofs-4.0.0-pre1/COPYRIGHT Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/COPYRIGHT Sat Jan 29 15:14:06 2000 @@ -12,3 +12,5 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +Portions Copyright (C) 1999-2000 Jeremy Fitzhardinge diff -ruN autofs-4.0.0-pre1/NEWS autofs-4.0.0-pre2/NEWS --- autofs-4.0.0-pre1/NEWS Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/NEWS Sat Jan 29 15:14:06 2000 @@ -1,3 +1,26 @@ +autofs-4.0.0-pre2: Since autofs-4.0.0-pre1: +------------------------------------------- +* Include kernel patches for 2.2.x in source (2.3.41 onwards has it standard) +* Clean up left over scaffolding directories if a tree didn't completely + mount. +* Make rc scripts wait for termination; it can take a long time if there's + lots of filesystems mounted +* Add a daemonoptions variable to make it easy add timeouts, etc + +autofs-4.0.0-pre1: Since autofs-3.1.4: +-------------------------------------- +* Jeremy Fitzhardinge is now maintaining autofs 4 +* Added support for autofs v4 kernel module; as of 2.2.14 and 2.3.40 you + need to patch your kernel. +* Implemented tree mounts in program maps; scripts can now return multiple + filesystems to be mounted. +* Will build any necessary scaffolding directories to create mountpoints. +* samples/auto.net added to implement a Solaris-style /net using the + above features. +* Major reworking of daemon internals to use an explicit state machine. +* "nosymlink" flag added to mount_nfs to inhibit the local symlink + optimization + Since autofs-3.1.3: ------------------- * Merge in documentation changes from RedHat RPM. diff -ruN autofs-4.0.0-pre1/README autofs-4.0.0-pre2/README --- autofs-4.0.0-pre1/README Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/README Sat Jan 29 15:14:06 2000 @@ -1,5 +1,5 @@ -*- text -*- -$Id: //depot/autofs-4.0/README#2 $ +$Id: //depot/autofs-4.0/README#4 $ autofs is a kernel-based automounter for Linux. It performs a job similar to amd(8) but relies on a small stub of kernel code instead of @@ -25,13 +25,14 @@ make ... make the daemon and modules make install ... install the daemon and modules -The kernel code is no longer distributed with the autofs daemon (this -package); it is now only distributed only with the Linux kernel code. +Development kernels 2.3.41 and onwards contain the autofs4 as +standard. If you're using 2.2, you can apply the patch in +the patches directory; it was made from 2.2.14, but it should +work on 2.2.10 onwards. autofs was written by H. Peter Anvin of Transmeta Corporation, please -read the COPYRIGHT file. -autofs 4 is the result of Jeremy Fitzhardinge's work -on autofs 3. +read the COPYRIGHT file. autofs 4 is the result of Jeremy +Fitzhardinge's work on autofs 3. If you use or want to help develop autofs, please join the autofs mailing list: diff -ruN autofs-4.0.0-pre1/README.smbfs autofs-4.0.0-pre2/README.smbfs --- autofs-4.0.0-pre1/README.smbfs Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/README.smbfs Sat Jan 29 15:14:06 2000 @@ -1,6 +1,6 @@ The functionality of converting map options to smbmnt arguments is now -handled by /bin/mount.smbfs, which is shipped with Sakba 2.0.6 or -later. You therefore needs to make sure yopu hae /bin/mount.smbfs +handled by /bin/mount.smbfs, which is shipped with Samba 2.0.6 or +later. You therefore need to make sure you have /bin/mount.smbfs installed on your system if you plan on using smbfs with autofs. WARNING: Some versions of Samba incorrectly installs this as diff -ruN autofs-4.0.0-pre1/TODO autofs-4.0.0-pre2/TODO --- autofs-4.0.0-pre1/TODO Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/TODO Sat Jan 29 15:14:06 2000 @@ -1,5 +1,11 @@ -$Id: //depot/autofs-4.0/TODO#3 $ +$Id: //depot/autofs-4.0/TODO#4 $ autofs v4 TODO list ------------------- * More testing under stress +* Add tree mount support to other lookup types + +autofs v5 TODO list +------------------- +* visible mountpoints +* direct maps? diff -ruN autofs-4.0.0-pre1/daemon/automount.c autofs-4.0.0-pre2/daemon/automount.c --- autofs-4.0.0-pre1/daemon/automount.c Sat Jan 29 15:09:47 2000 +++ autofs-4.0.0-pre2/daemon/automount.c Sat Jan 29 15:14:06 2000 @@ -1,4 +1,4 @@ -#ident "$Id: //depot/autofs-4.0/daemon/automount.c#3 $" +#ident "$Id: //depot/autofs-4.0/daemon/automount.c#5 $" /* ----------------------------------------------------------------------- * * * automount.c - Linux automounter daemon @@ -436,10 +436,8 @@ (unsigned)my_pgrp, AUTOFS_PROTO_VERSION); sprintf(our_name, "automount(pid%u)", (unsigned)my_pid); - if ((spawnl(LOG_DEBUG, PATH_MOUNT, PATH_MOUNT, "-t", "autofs4", "-o", - options, our_name, path, NULL) != 0) && - (spawnl(LOG_DEBUG, PATH_MOUNT, PATH_MOUNT, "-t", "autofs", "-o", - options, our_name, path, NULL) != 0)) { + if (spawnl(LOG_DEBUG, PATH_MOUNT, PATH_MOUNT, "-t", "autofs", "-o", + options, our_name, path, NULL) != 0) { syslog(LOG_CRIT, "cannot find autofs in kernel"); close(pipefd[0]); close(pipefd[1]); @@ -982,7 +980,8 @@ return 1; } else if ( !f ) { int i; - + char buf[PATH_MAX+1]; + ignore_signals(); /* Set up a sensible signal environment */ close(ap.pipefd); close(ap.ioctlfd); @@ -991,6 +990,11 @@ i = ap.lookup->lookup_mount(ap.path, pkt->name, pkt->len, ap.lookup->context); + + /* Clean up after any failed mounts */ + sprintf(buf, "%s/%.*s", ap.path, pkt->len, pkt->name); + rm_unwanted(buf, 1); + _exit(i ? 1 : 0); } else { /* Important: set up data structures while signals still blocked */ diff -ruN autofs-4.0.0-pre1/modules/lookup_program.c autofs-4.0.0-pre2/modules/lookup_program.c --- autofs-4.0.0-pre1/modules/lookup_program.c Sat Jan 29 15:09:48 2000 +++ autofs-4.0.0-pre2/modules/lookup_program.c Sat Jan 29 15:14:06 2000 @@ -1,10 +1,11 @@ -#ident "$Id: lookup_program.c,v 1.3 1999/12/17 19:02:47 hpa Exp $" +#ident "$Id: //depot/autofs-4.0/modules/lookup_program.c#3 $" /* ----------------------------------------------------------------------- * * * lookup_program.c - module for Linux automount to access an * automount map via a query program * * Copyright 1997 Transmeta Corporation - All Rights Reserved + * Copyright 1999-2000 Jeremy Fitzhardinge * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff -ruN autofs-4.0.0-pre1/patches/autofs-2.2.14-20000123.diff autofs-4.0.0-pre2/patches/autofs-2.2.14-20000123.diff --- autofs-4.0.0-pre1/patches/autofs-2.2.14-20000123.diff Wed Dec 31 16:00:00 1969 +++ autofs-4.0.0-pre2/patches/autofs-2.2.14-20000123.diff Sat Jan 29 15:14:06 2000 @@ -0,0 +1,2231 @@ +--- 2.2/include/linux/auto_fs.h Thu Jan 13 06:27:34 2000 ++++ linux/include/linux/auto_fs.h Thu Jan 13 17:47:25 2000 +@@ -20,7 +20,8 @@ + #include + #include + +-#define AUTOFS_PROTO_VERSION 3 ++#define AUTOFS_MIN_PROTO_VERSION 3 /* Min version we support */ ++#define AUTOFS_PROTO_VERSION 4 /* Current version */ + + /* + * Architectures where both 32- and 64-bit binaries can be executed +@@ -46,6 +47,7 @@ + enum autofs_packet_type { + autofs_ptype_missing, /* Missing entry (mount request) */ + autofs_ptype_expire, /* Expire entry (umount request) */ ++ autofs_ptype_expire_multi, /* Expire entry (umount request) */ + }; + + struct autofs_packet_hdr { +@@ -60,18 +62,35 @@ + char name[NAME_MAX+1]; + }; + ++/* v3 expire (via ioctl) */ + struct autofs_packet_expire { + struct autofs_packet_hdr hdr; + int len; + char name[NAME_MAX+1]; + }; + ++/* v4 multi expire (via pipe) */ ++struct autofs_packet_expire_multi { ++ struct autofs_packet_hdr hdr; ++ autofs_wqt_t wait_queue_token; ++ int len; ++ char name[NAME_MAX+1]; ++}; ++ ++union autofs_packet_union { ++ struct autofs_packet_hdr hdr; ++ struct autofs_packet_missing missing; ++ struct autofs_packet_expire expire; ++ struct autofs_packet_expire_multi expire_multi; ++}; ++ + #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) ++#define AUTOFS_IOC_EXPIRE_MULTI _IOW(0x93,0x66,int) + + #ifdef __KERNEL__ + +diff -X ../diffexcl -Nur 2.2/fs/autofs/Makefile abulafia/fs/autofs/Makefile +--- 2.2/fs/autofs/Makefile Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/Makefile Wed Jan 12 22:47:52 2000 +@@ -5,7 +5,7 @@ + # + + O_TARGET := autofs.o +-O_OBJS := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o ++O_OBJS := inohash.o init.o inode.o root.o symlink.o waitq.o expire.o + + M_OBJS := $(O_TARGET) + +diff -X ../diffexcl -Nur 2.2/fs/autofs/autofs_i.h abulafia/fs/autofs/autofs_i.h +--- 2.2/fs/autofs/autofs_i.h Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/autofs_i.h Sun Jan 23 02:05:08 2000 +@@ -13,6 +13,7 @@ + /* Internal header file for autofs */ + + #include ++#include + + /* This is the range of ioctl() numbers we claim as ours */ + #define AUTOFS_IOC_FIRST AUTOFS_IOC_READY +@@ -25,10 +26,12 @@ + #include + #include + ++/* #define DEBUG */ ++ + #ifdef DEBUG +-#define DPRINTK(D) (printk D) ++#define DPRINTK(D) do{ printk("pid %d: ", current->pid); printk D; } while(0) + #else +-#define DPRINTK(D) ((void)0) ++#define DPRINTK(D) do {} while(0) + #endif + + #define AUTOFS_SUPER_MAGIC 0x0187 +@@ -41,28 +44,36 @@ + */ + #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; +- char *name; +- int len; +- ino_t ino; +- struct dentry *dentry; +- /* Linked list of entries */ +- struct autofs_dir_ent *next; +- struct autofs_dir_ent **back; +- /* The following entries are for the expiry system */ +- unsigned long last_usage; +- struct autofs_dir_ent *exp_next; +- struct autofs_dir_ent *exp_prev; ++/* Unified info structure. This is pointed to by both the dentry and ++ inode structures. Each file in the filesystem has an instance of this ++ structure. It holds a reference to the dentry, so dentries are never ++ flushed while the file exists. All name lookups are dealt with at the ++ dentry level, although the filesystem can interfere in the validation ++ process. Readdir is implemented by traversing the dentry lists. */ ++struct autofs_info { ++ struct dentry *dentry; ++ struct inode *inode; ++ ++ int flags; ++ ++ struct autofs_sb_info *sbi; ++ struct list_head ino_hash; ++ unsigned long last_used; ++ ++ ino_t ino; ++ mode_t mode; ++ size_t size; ++ ++ void (*free)(struct autofs_info *); ++ union { ++ const char *symlink; ++ } u; + }; + +-struct autofs_dirhash { +- struct autofs_dir_ent *h[AUTOFS_HASH_SIZE]; +- struct autofs_dir_ent expiry_head; ++#define AUTOFS_INF_EXPIRING (1<<0) /* dentry is in the process of expiring */ ++ ++struct autofs_inohash { ++ struct list_head head; + }; + + struct autofs_wait_queue { +@@ -78,19 +89,8 @@ + int wait_ctr; + }; + +-struct autofs_symlink { +- char *data; +- int len; +- 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) ++#define AUTOFS_ROOT_INO 1 ++#define AUTOFS_FIRST_INO 2 + + #define AUTOFS_SBI_MAGIC 0x6d4a556d + +@@ -99,19 +99,24 @@ + struct file *pipe; + pid_t oz_pgrp; + int catatonic; ++ int version; + unsigned long exp_timeout; +- ino_t next_dir_ino; ++ ino_t next_ino; ++ struct super_block *sb; + 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]; ++ struct autofs_inohash ihash; + }; + +-extern inline struct autofs_sb_info *autofs_sbi(struct super_block *sb) ++static inline struct autofs_sb_info *autofs_sbi(struct super_block *sb) + { + return (struct autofs_sb_info *)(sb->u.generic_sbp); + } + ++static inline struct autofs_info *autofs_dentry_ino(struct dentry *dentry) ++{ ++ return (struct autofs_info *)(dentry->d_fsdata); ++} ++ + /* 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".) */ +@@ -120,39 +125,51 @@ + 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 *,struct autofs_dir_ent *); +-void autofs_hash_dputall(struct autofs_dirhash *); +-void autofs_hash_nuke(struct autofs_dirhash *); ++/* Does a dentry have some pending activity? */ ++static inline int autofs_ispending(struct dentry *dentry) ++{ ++ struct autofs_info *inf = autofs_dentry_ino(dentry); + +-/* Expiration-handling functions */ ++ return (dentry->d_flags & DCACHE_AUTOFS_PENDING) || ++ (inf != NULL && inf->flags & AUTOFS_INF_EXPIRING); ++} + +-void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *); +-struct autofs_dir_ent *autofs_expire(struct super_block *,struct autofs_sb_info *); ++/* Inode hash operations */ ++void autofs_init_ihash(struct autofs_inohash *); ++void autofs_ihash_insert(struct autofs_inohash *, struct autofs_info *ino); ++void autofs_ihash_delete(struct autofs_info *ino); ++void autofs_ihash_nuke(struct autofs_inohash *ih); ++struct autofs_info *autofs_ihash_find(struct autofs_inohash *ih, ino_t ino); ++ ++struct autofs_info *autofs_init_inf(struct autofs_sb_info *, mode_t mode); ++void autofs_free_ino(struct autofs_info *); ++ ++/* Expiration */ ++int is_autofs_dentry(struct dentry *); ++int autofs_expire_run(struct super_block *, struct autofs_sb_info *, ++ struct autofs_packet_expire *); ++int autofs_expire_multi(struct super_block *, struct autofs_sb_info *, int *); + + /* 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; ++extern struct inode_operations autofs_root_inode_operations; + + /* Initializing function */ + + struct super_block *autofs_read_super(struct super_block *, void *,int); ++struct autofs_info *autofs_init_ino(struct autofs_info *, struct autofs_sb_info *sbi, mode_t mode); + + /* Queue management functions */ + +-int autofs_wait(struct autofs_sb_info *,struct qstr *); ++enum autofs_notify ++{ ++ NFY_NONE, ++ NFY_MOUNT, ++ NFY_EXPIRE ++}; ++ ++int autofs_wait(struct autofs_sb_info *,struct qstr *, enum autofs_notify); + int autofs_wait_release(struct autofs_sb_info *,autofs_wqt_t,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 -X ../diffexcl -Nur 2.2/fs/autofs/dir.c abulafia/fs/autofs/dir.c +--- 2.2/fs/autofs/dir.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/dir.c Wed Dec 31 16:00:00 1969 +@@ -1,85 +0,0 @@ +-/* -*- linux-c -*- --------------------------------------------------------- * +- * +- * linux/fs/autofs/dir.c +- * +- * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved +- * +- * This file is part of the Linux kernel and is made available under +- * the terms of the GNU General Public License, version 2, or at your +- * option, any later version, incorporated herein by reference. +- * +- * ------------------------------------------------------------------------- */ +- +-#include "autofs_i.h" +- +-static int autofs_dir_readdir(struct file *filp, +- void *dirent, filldir_t filldir) +-{ +- struct inode *inode=filp->f_dentry->d_inode; +- +- 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 struct dentry *autofs_dir_lookup(struct inode *dir,struct dentry *dentry) +-{ +- d_add(dentry, NULL); +- return NULL; +-} +- +-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, /* flush */ +- 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 -X ../diffexcl -Nur 2.2/fs/autofs/dirhash.c abulafia/fs/autofs/dirhash.c +--- 2.2/fs/autofs/dirhash.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/dirhash.c Wed Dec 31 16:00:00 1969 +@@ -1,240 +0,0 @@ +-/* -*- linux-c -*- --------------------------------------------------------- * +- * +- * linux/fs/autofs/dirhash.c +- * +- * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved +- * +- * This file is part of the Linux kernel and is made available under +- * the terms of the GNU General Public License, version 2, or at your +- * option, any later version, incorporated herein by reference. +- * +- * ------------------------------------------------------------------------- */ +- +-#include "autofs_i.h" +- +-/* 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 super_block *sb, +- struct autofs_sb_info *sbi) +-{ +- struct autofs_dirhash *dh = &sbi->dirhash; +- struct autofs_dir_ent *ent; +- struct dentry *dentry; +- unsigned long timeout = sbi->exp_timeout; +- +- ent = dh->expiry_head.exp_next; +- +- if ( ent == &(dh->expiry_head) || sbi->catatonic ) +- return NULL; /* No entries */ +- +- while ( jiffies - ent->last_usage >= timeout ) { +- /* Move to end of list in case expiry isn't desirable */ +- autofs_update_usage(dh, ent); +- +- /* Check to see that entry is expirable */ +- if ( ent->ino < AUTOFS_FIRST_DIR_INO ) +- return ent; /* Symlinks are always expirable */ +- +- /* Get the dentry for the autofs subdirectory */ +- dentry = ent->dentry; +- +- if ( !dentry ) { +- /* Should only happen in catatonic mode */ +- printk("autofs: dentry == NULL but inode range is directory, entry %s\n", ent->name); +- autofs_delete_usage(ent); +- continue; +- } +- +- if ( !dentry->d_inode ) { +- dput(dentry); +- printk("autofs: negative dentry on expiry queue: %s\n", +- ent->name); +- autofs_delete_usage(ent); +- continue; +- } +- +- /* Make sure entry is mounted and unused; note that dentry will +- point to the mounted-on-top root. */ +- if ( !S_ISDIR(dentry->d_inode->i_mode) +- || dentry->d_mounts == dentry ) { +- DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name)); +- continue; +- } +- +- if ( !is_root_busy(dentry->d_mounts) ) { +- DPRINTK(("autofs: signaling expire on %s\n", ent->name)); +- return ent; /* Expirable! */ +- } +- DPRINTK(("autofs: didn't expire due to is_root_busy: %s\n", ent->name)); +- } +- return NULL; /* No expirable entries */ +-} +- +-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); +- if ( ent->dentry ) +- ent->dentry->d_count++; +- +- dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE]; +- ent->next = *dhnp; +- ent->back = dhnp; +- *dhnp = ent; +- if ( ent->next ) +- ent->next->back = &(ent->next); +-} +- +-void autofs_hash_delete(struct autofs_dir_ent *ent) +-{ +- *(ent->back) = ent->next; +- if ( ent->next ) +- ent->next->back = ent->back; +- +- autofs_delete_usage(ent); +- +- if ( ent->dentry ) +- dput(ent->dentry); +- 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. +- * +- * "last" can be NULL or the value returned by the last search *if* we +- * want the next sequential entry. +- */ +-struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, +- off_t *ptr, struct autofs_dir_ent *last) +-{ +- 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 = last ? last->next : NULL; +- +- if ( ent ) { +- ecount++; +- } else { +- 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; +-} +- +-/* Iterate over all the ents, and remove all dentry pointers. Used on +- entering catatonic mode, in order to make the filesystem unmountable. */ +-void autofs_hash_dputall(struct autofs_dirhash *dh) +-{ +- int i; +- struct autofs_dir_ent *ent; +- +- for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) { +- for ( ent = dh->h[i] ; ent ; ent = ent->next ) { +- if ( ent->dentry ) { +- dput(ent->dentry); +- ent->dentry = NULL; +- } +- } +- } +-} +- +-/* 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; +- if ( ent->dentry ) +- dput(ent->dentry); +- kfree(ent->name); +- kfree(ent); +- } +- } +-} +diff -X ../diffexcl -Nur 2.2/fs/autofs/expire.c abulafia/fs/autofs/expire.c +--- 2.2/fs/autofs/expire.c Wed Dec 31 16:00:00 1969 ++++ linux/fs/autofs/expire.c Sun Jan 23 02:44:26 2000 +@@ -0,0 +1,233 @@ ++/* -*- linux-c -*- --------------------------------------------------------- * ++ * ++ * linux/fs/autofs/expire.c ++ * ++ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved ++ * Copyright 1999 Jeremy Fitzhardinge ++ * ++ * 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" ++ ++/* ++ * Determine if a dentry tree is in use. This is much the ++ * same as the standard is_root_busy() function, except ++ * that :- ++ * - the extra dentry reference in autofs dentries is not ++ * considered to be busy ++ * - mountpoints within the tree are not busy ++ * - it traverses across mountpoints ++ * XXX doesn't consider children of covered dentries at mountpoints ++ */ ++static int is_tree_busy(struct dentry *root) ++{ ++ struct dentry *this_parent; ++ struct list_head *next; ++ int count; ++ ++ root = root->d_mounts; ++ ++ count = root->d_count; ++ this_parent = root; ++ ++ DPRINTK(("is_tree_busy: starting at %.*s/%.*s, d_count=%d\n", ++ root->d_covers->d_parent->d_name.len, ++ root->d_covers->d_parent->d_name.name, ++ root->d_name.len, root->d_name.name, ++ root->d_count)); ++ ++ /* Ignore autofs's extra reference */ ++ if (is_autofs_dentry(root)) { ++ DPRINTK(("is_tree_busy: autofs\n")); ++ count--; ++ } ++ ++ /* Mountpoints don't count */ ++ if (root->d_mounts != root || ++ root->d_covers != root) { ++ DPRINTK(("is_tree_busy: mountpoint\n")); ++ count--; ++ } ++ ++repeat: ++ next = this_parent->d_mounts->d_subdirs.next; ++resume: ++ while (next != &this_parent->d_mounts->d_subdirs) { ++ int adj = 0; ++ struct list_head *tmp = next; ++ struct dentry *dentry = list_entry(tmp, struct dentry, ++ d_child); ++ ++ next = tmp->next; ++ ++ dentry = dentry->d_mounts; ++ ++ DPRINTK(("is_tree_busy: considering %.*s/%.*s, d_count=%d, count=%d\n", ++ this_parent->d_name.len, ++ this_parent->d_name.name, ++ dentry->d_covers->d_name.len, ++ dentry->d_covers->d_name.name, ++ dentry->d_count, count)); ++ ++ /* Decrement count for unused children */ ++ count += (dentry->d_count - 1); ++ ++ /* Mountpoints don't count */ ++ if (dentry->d_mounts != dentry || ++ dentry->d_covers != dentry) { ++ DPRINTK(("is_tree_busy: mountpoint\n")); ++ adj++; ++ } ++ ++ /* Ignore autofs's extra reference */ ++ if (is_autofs_dentry(dentry)) { ++ DPRINTK(("is_tree_busy: autofs\n")); ++ adj++; ++ } ++ ++ count -= adj; ++ ++ if (!list_empty(&dentry->d_mounts->d_subdirs)) { ++ this_parent = dentry->d_mounts; ++ goto repeat; ++ } ++ ++ /* root is busy if any leaf is busy */ ++ if (dentry->d_count != adj) { ++ DPRINTK(("is_tree_busy: busy leaf (d_count=%d adj=%d)\n", ++ dentry->d_count, adj)); ++ return 1; ++ } ++ } ++ /* ++ * All done at this level ... ascend and resume the search. ++ */ ++ if (this_parent != root) { ++ next = this_parent->d_covers->d_child.next; ++ this_parent = this_parent->d_covers->d_parent; ++ goto resume; ++ } ++ ++ DPRINTK(("is_tree_busy: count=%d\n", count)); ++ return count != 0; /* remaining users? */ ++} ++ ++/* ++ * Find an eligible tree to time-out ++ * A tree is eligible if :- ++ * - it is unused by any user process ++ * - it has been unused for exp_timeout time ++ */ ++static struct dentry *autofs_expire(struct super_block *sb, ++ struct autofs_sb_info *sbi, ++ int do_now) ++{ ++ unsigned long now = jiffies; /* snapshot of now */ ++ unsigned long timeout; ++ struct dentry *root = sb->s_root; ++ struct list_head *tmp; ++ ++ if (!sbi->exp_timeout || !root) ++ return NULL; ++ ++ timeout = sbi->exp_timeout; ++ ++ for(tmp = root->d_subdirs.next; ++ tmp != &root->d_subdirs; ++ tmp = tmp->next) { ++ struct autofs_info *ino; ++ struct dentry *dentry = list_entry(tmp, struct dentry, d_child); ++ ++ if (dentry->d_inode == NULL) ++ continue; ++ ++ ino = autofs_dentry_ino(dentry); ++ ++ if (ino == NULL) { ++ /* dentry in the process of being deleted */ ++ continue; ++ } ++ ++ /* No point expiring a pending mount */ ++ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) ++ continue; ++ ++ if (!do_now) { ++ /* Too young to die */ ++ if (time_after(ino->last_used+timeout, now)) ++ continue; ++ ++ /* update last_used here :- ++ - obviously makes sense if it is in use now ++ - less obviously, prevents rapid-fire expire ++ attempts if expire fails the first time */ ++ ino->last_used = now; ++ } ++ ++ if (!is_tree_busy(dentry)) { ++ DPRINTK(("autofs_expire: returning %p %.*s\n", ++ dentry, dentry->d_name.len, dentry->d_name.name)); ++ /* Start from here next time */ ++ list_del(&root->d_subdirs); ++ list_add(&root->d_subdirs, &dentry->d_child); ++ return dentry; ++ } ++ } ++ ++ return NULL; ++} ++ ++/* Perform an expiry operation */ ++int autofs_expire_run(struct super_block *sb, ++ struct autofs_sb_info *sbi, ++ struct autofs_packet_expire *pkt_p) ++{ ++ struct autofs_packet_expire pkt; ++ struct dentry *dentry; ++ ++ memset(&pkt,0,sizeof pkt); ++ ++ pkt.hdr.proto_version = sbi->version; ++ pkt.hdr.type = autofs_ptype_expire; ++ ++ if ((dentry = autofs_expire(sb, sbi, 0)) == NULL) ++ return -EAGAIN; ++ ++ pkt.len = dentry->d_name.len; ++ memcpy(pkt.name, dentry->d_name.name, pkt.len); ++ pkt.name[pkt.len] = '\0'; ++ ++ if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) ) ++ return -EFAULT; ++ ++ return 0; ++} ++ ++/* Call repeatedly until it returns -EAGAIN, meaning there's nothing ++ more to be done */ ++int autofs_expire_multi(struct super_block *sb, ++ struct autofs_sb_info *sbi, int *arg) ++{ ++ struct dentry *dentry; ++ int ret = -EAGAIN; ++ int do_now = 0; ++ ++ if (arg && get_user(do_now, arg)) ++ return -EFAULT; ++ ++ if ((dentry = autofs_expire(sb, sbi, do_now)) != NULL) { ++ struct autofs_info *de_info = autofs_dentry_ino(dentry); ++ ++ /* This is synchronous because it makes the daemon a ++ little easier */ ++ de_info->flags |= AUTOFS_INF_EXPIRING; ++ ret = autofs_wait(sbi, &dentry->d_name, NFY_EXPIRE); ++ de_info->flags &= ~AUTOFS_INF_EXPIRING; ++ } ++ ++ return ret; ++} +diff -X ../diffexcl -Nur 2.2/fs/autofs/init.c abulafia/fs/autofs/init.c +--- 2.2/fs/autofs/init.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/init.c Wed Jan 12 22:47:52 2000 +@@ -41,12 +41,3 @@ + + #endif /* !MODULE */ + +-#ifdef DEBUG +-void autofs_say(const char *name, int len) +-{ +- printk("(%d: ", len); +- while ( len-- ) +- printk("%c", *name++); +- printk(")\n"); +-} +-#endif +diff -X ../diffexcl -Nur 2.2/fs/autofs/inode.c abulafia/fs/autofs/inode.c +--- 2.2/fs/autofs/inode.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/inode.c Sun Jan 23 02:46:50 2000 +@@ -19,6 +19,66 @@ + #define __NO_VERSION__ + #include + ++static void ino_lnkfree(struct autofs_info *ino) ++{ ++ if (ino->u.symlink) { ++ kfree(ino->u.symlink); ++ ino->u.symlink = NULL; ++ } ++} ++ ++struct autofs_info *autofs_init_ino(struct autofs_info *ino, ++ struct autofs_sb_info *sbi, mode_t mode) ++{ ++ int reinit = 1; ++ ++ if (ino == NULL) { ++ reinit = 0; ++ ino = kmalloc(sizeof(*ino), GFP_KERNEL); ++ } ++ ++ if (ino == NULL) ++ return NULL; ++ ++ ino->flags = 0; ++ ino->ino = sbi->next_ino++; ++ ino->mode = mode; ++ ino->inode = NULL; ++ ino->dentry = NULL; ++ ino->size = 0; ++ ++ ino->last_used = jiffies; ++ ++ ino->sbi = sbi; ++ INIT_LIST_HEAD(&ino->ino_hash); ++ ++ if (reinit && ino->free) ++ (ino->free)(ino); ++ ++ memset(&ino->u, 0, sizeof(ino->u)); ++ ++ ino->free = NULL; ++ ++ if (S_ISLNK(mode)) ++ ino->free = ino_lnkfree; ++ ++ return ino; ++} ++ ++void autofs_free_ino(struct autofs_info *ino) ++{ ++ autofs_ihash_delete(ino); ++ if (ino->dentry) { ++ ino->dentry->d_fsdata = NULL; ++ if (ino->dentry->d_inode) ++ dput(ino->dentry); ++ ino->dentry = NULL; ++ } ++ if (ino->free) ++ (ino->free)(ino); ++ kfree(ino); ++} ++ + /* + * Dummy functions - do we ever actually want to do + * something here? +@@ -27,26 +87,20 @@ + { + } + +-static void autofs_delete_inode(struct inode *inode) ++static void autofs_clear_inode(struct inode *inode) + { +- inode->i_size = 0; + } + + static void autofs_put_super(struct super_block *sb) + { + struct autofs_sb_info *sbi = autofs_sbi(sb); +- unsigned int n; ++ ++ sb->u.generic_sbp = NULL; + + if ( !sbi->catatonic ) + autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */ + +- 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); +- } +- +- kfree(sb->u.generic_sbp); ++ kfree(sbi); + + DPRINTK(("autofs: shutting down\n")); + +@@ -55,23 +109,30 @@ + #endif + } + ++static void autofs_umount_begin(struct super_block *sb) ++{ ++ struct autofs_sb_info *sbi = autofs_sbi(sb); ++ ++ if (!sbi->catatonic) ++ autofs_catatonic_mode(sbi); ++} ++ + 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 ++ read_inode: autofs_read_inode, ++ write_inode: autofs_write_inode, ++ put_inode: autofs_put_inode, ++ clear_inode: autofs_clear_inode, ++ put_super: autofs_put_super, ++ statfs: autofs_statfs, ++ umount_begin: autofs_umount_begin, + }; + +-static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto) ++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; + +@@ -134,6 +195,19 @@ + return (*pipefd < 0); + } + ++static struct autofs_info *autofs_mkroot(struct autofs_sb_info *sbi) ++{ ++ struct autofs_info *ino; ++ ++ ino = autofs_init_ino(NULL, sbi, S_IFDIR | 0755); ++ if (!ino) ++ return NULL; ++ ++ ino->ino = AUTOFS_ROOT_INO; ++ ++ return ino; ++} ++ + struct super_block *autofs_read_super(struct super_block *s, void *data, + int silent) + { +@@ -151,20 +225,23 @@ + if (s->s_root) + goto out_unlock; + +- sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL); ++ sbi = (struct autofs_sb_info *) kmalloc(sizeof(*sbi), GFP_KERNEL); + if ( !sbi ) + goto fail_unlock; + DPRINTK(("autofs: starting up, sbi = %p\n",sbi)); + ++ memset(sbi, 0, sizeof(*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->sb = s; ++ sbi->version = 0; ++ autofs_init_ihash(&sbi->ihash); + sbi->queues = NULL; +- memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN); +- sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO; ++ sbi->next_ino = AUTOFS_FIRST_INO; + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = AUTOFS_SUPER_MAGIC; +@@ -175,6 +252,8 @@ + /* + * Get the root inode and dentry, but defer checking for errors. + */ ++ autofs_ihash_insert(&sbi->ihash, autofs_mkroot(sbi)); ++ + root_inode = iget(s, AUTOFS_ROOT_INO); + root = d_alloc_root(root_inode, NULL); + pipe = NULL; +@@ -189,18 +268,26 @@ + 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) ) { ++ 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"); ++ if (maxproto < AUTOFS_MIN_PROTO_VERSION || ++ minproto > AUTOFS_PROTO_VERSION) { ++ printk("autofs: kernel does not match daemon version " ++ "daemon (%d, %d) kernel (%d, %d)\n", ++ minproto, maxproto, ++ AUTOFS_MIN_PROTO_VERSION, AUTOFS_PROTO_VERSION); + goto fail_dput; + } + ++ sbi->version = maxproto > AUTOFS_PROTO_VERSION ? AUTOFS_PROTO_VERSION : maxproto; ++ + DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp)); + pipe = fget(pipefd); + /* +@@ -294,48 +381,40 @@ + + static void autofs_read_inode(struct inode *inode) + { +- ino_t ino = inode->i_ino; +- unsigned int n; + struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); ++ struct autofs_info *inf; ++ ++ inf = autofs_ihash_find(&sbi->ihash, inode->i_ino); + +- /* Initialize to the default case (stub directory) */ ++ if (inf == NULL || inf->inode != NULL) ++ return; ++ ++ inode->i_mode = inf->mode; ++ inode->i_mtime = inode->i_ctime = inode->i_atime = CURRENT_TIME; ++ inode->i_size = inf->size; + +- inode->i_op = NULL; +- inode->i_op = &autofs_dir_inode_operations; +- inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; +- 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; ++ inode->i_blksize = 0; ++ inode->i_nlink = 1; + +- 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 %u\n", (unsigned int) ino); +- return; +- } +- ++ if (inode->i_sb->s_root) { ++ inode->i_uid = inode->i_sb->s_root->d_inode->i_uid; ++ inode->i_gid = inode->i_sb->s_root->d_inode->i_gid; ++ } else { ++ inode->i_uid = 0; ++ inode->i_gid = 0; ++ } ++ ++ inf->inode = inode; ++ ++ if (S_ISDIR(inf->mode)) { ++ inode->i_nlink = 2; ++ if (inode->i_ino == AUTOFS_ROOT_INO) ++ inode->i_op = &autofs_root_inode_operations; ++ else ++ inode->i_op = &autofs_dir_inode_operations; ++ } else if (S_ISLNK(inf->mode)) { + 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; + } + } + +diff -X ../diffexcl -Nur 2.2/fs/autofs/inohash.c abulafia/fs/autofs/inohash.c +--- 2.2/fs/autofs/inohash.c Wed Dec 31 16:00:00 1969 ++++ linux/fs/autofs/inohash.c Sun Jan 23 02:05:31 2000 +@@ -0,0 +1,67 @@ ++/* ++ * "inohash" is a misnomer. Inodes are just stored in a single list, ++ * since this code is only ever asked for the most recently inserted ++ * inode. ++ * ++ * Copyright 1999 Jeremy Fitzhardinge ++ */ ++ ++#include "autofs_i.h" ++ ++void autofs_init_ihash(struct autofs_inohash *ih) ++{ ++ INIT_LIST_HEAD(&ih->head); ++} ++ ++void autofs_ihash_insert(struct autofs_inohash *ih, ++ struct autofs_info *ino) ++{ ++ DPRINTK(("autofs_ihash_insert: adding ino %ld\n", ino->ino)); ++ ++ list_add(&ino->ino_hash, &ih->head); ++} ++ ++void autofs_ihash_delete(struct autofs_info *inf) ++{ ++ DPRINTK(("autofs_ihash_delete: deleting ino %ld\n", inf->ino)); ++ ++ if (!list_empty(&inf->ino_hash)) ++ list_del(&inf->ino_hash); ++} ++ ++struct autofs_info *autofs_ihash_find(struct autofs_inohash *ih, ++ ino_t inum) ++{ ++ struct list_head *tmp; ++ ++ for(tmp = ih->head.next; ++ tmp != &ih->head; ++ tmp = tmp->next) { ++ struct autofs_info *ino = list_entry(tmp, struct autofs_info, ino_hash); ++ if (ino->ino == inum) { ++ DPRINTK(("autofs_ihash_find: found %ld -> %p\n", ++ inum, ino)); ++ return ino; ++ } ++ } ++ DPRINTK(("autofs_ihash_find: didn't find %ld\n", inum)); ++ return NULL; ++} ++ ++void autofs_ihash_nuke(struct autofs_inohash *ih) ++{ ++ struct list_head *tmp = ih->head.next; ++ struct list_head *next; ++ ++ for(; tmp != &ih->head; tmp = next) { ++ struct autofs_info *ino; ++ ++ next = tmp->next; ++ ++ ino = list_entry(tmp, struct autofs_info, ino_hash); ++ ++ DPRINTK(("autofs_ihash_nuke: nuking %ld\n", ino->ino)); ++ autofs_free_ino(ino); ++ } ++ INIT_LIST_HEAD(&ih->head); ++} +diff -X ../diffexcl -Nur 2.2/fs/autofs/root.c abulafia/fs/autofs/root.c +--- 2.2/fs/autofs/root.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/root.c Sun Jan 23 02:50:47 2000 +@@ -3,6 +3,7 @@ + * linux/fs/autofs/root.c + * + * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved ++ * Copyright 1999 Jeremy Fitzhardinge + * + * 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 +@@ -15,86 +16,98 @@ + #include + #include "autofs_i.h" + +-static int autofs_root_readdir(struct file *,void *,filldir_t); +-static struct dentry *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_dir_readdir(struct file *,void *,filldir_t); ++static struct dentry *autofs_dir_lookup(struct inode *,struct dentry *); ++static int autofs_dir_symlink(struct inode *,struct dentry *,const char *); ++static int autofs_dir_unlink(struct inode *,struct dentry *); ++static int autofs_dir_rmdir(struct inode *,struct dentry *); ++static int autofs_dir_mkdir(struct inode *,struct dentry *,int); + static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); ++static struct dentry *autofs_root_lookup(struct inode *,struct dentry *); + + 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, /* flush */ +- NULL, /* release */ +- NULL, /* fsync */ +- NULL, /* fasync */ +- NULL, /* check_media_change */ +- NULL, /* revalidate */ +- NULL /* lock */ ++ readdir: autofs_dir_readdir, /* readdir */ ++ ioctl: autofs_root_ioctl, /* ioctl */ + }; + + 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 */ ++ &autofs_root_operations, /* file operations */ ++ ++ lookup: autofs_root_lookup, /* lookup */ ++ unlink: autofs_dir_unlink, /* unlink */ ++ symlink: autofs_dir_symlink, /* symlink */ ++ mkdir: autofs_dir_mkdir, /* mkdir */ ++ rmdir: autofs_dir_rmdir, /* rmdir */ ++}; ++ ++static struct file_operations autofs_dir_operations = { ++ readdir: autofs_dir_readdir, /* readdir */ ++}; ++ ++struct inode_operations autofs_dir_inode_operations = { ++ &autofs_dir_operations, /* file operations */ ++ ++ lookup: autofs_dir_lookup, /* lookup */ ++ unlink: autofs_dir_unlink, /* unlink */ ++ symlink: autofs_dir_symlink, /* symlink */ ++ mkdir: autofs_dir_mkdir, /* mkdir */ ++ rmdir: autofs_dir_rmdir, /* rmdir */ + }; + +-static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) ++static inline struct dentry *nth_child(struct dentry *dir, int nr) ++{ ++ struct list_head *tmp = dir->d_subdirs.next; ++ ++ while(tmp != &dir->d_subdirs) { ++ if (nr-- == 0) ++ return list_entry(tmp, struct dentry, d_child); ++ tmp = tmp->next; ++ } ++ return NULL; ++} ++ ++static int autofs_dir_readdir(struct file *filp, void *dirent, ++ filldir_t filldir) + { +- struct autofs_dir_ent *ent = NULL; +- struct autofs_dirhash *dirhash; + struct autofs_sb_info *sbi; +- struct inode * inode = filp->f_dentry->d_inode; +- off_t onr, nr; ++ struct autofs_info *ino; ++ struct dentry *dentry = filp->f_dentry; ++ struct dentry *dent_ptr; ++ struct inode *dir = dentry->d_inode; ++ struct list_head *cursor; ++ off_t nr; + +- sbi = autofs_sbi(inode->i_sb); +- dirhash = &sbi->dirhash; ++ sbi = autofs_sbi(dir->i_sb); ++ ino = autofs_dentry_ino(dentry); + nr = filp->f_pos; + + switch(nr) + { + case 0: +- if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0) ++ if (filldir(dirent, ".", 1, nr, dir->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + case 1: +- if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0) ++ if (filldir(dirent, "..", 2, nr, dentry->d_covers->d_parent->d_inode->i_ino) < 0) + return 0; + filp->f_pos = ++nr; + /* fall through */ + default: +- while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr,ent) ) { +- if ( !ent->dentry || ent->dentry->d_mounts != ent->dentry ) { +- if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0) +- return 0; +- filp->f_pos = nr; +- } ++ dent_ptr = nth_child(dentry, nr-2); ++ if (dent_ptr == NULL) ++ break; ++ ++ cursor = &dent_ptr->d_child; ++ ++ while(cursor != &dentry->d_subdirs) { ++ dent_ptr = list_entry(cursor, struct dentry, d_child); ++ if (dent_ptr->d_inode && ++ filldir(dirent, dent_ptr->d_name.name, dent_ptr->d_name.len, nr, ++ dent_ptr->d_inode->i_ino) < 0) ++ return 0; ++ filp->f_pos = ++nr; ++ cursor = cursor->next; + } + break; + } +@@ -102,57 +115,83 @@ + return 0; + } + +-static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi) ++/* Update usage from here to top of tree, so that scan of ++ top-level directories will give a useful result */ ++static void autofs_update_usage(struct dentry *dentry) ++{ ++ struct dentry *top = dentry->d_sb->s_root; ++ ++ for(; dentry != top; dentry = dentry->d_parent) { ++ struct autofs_info *ino = autofs_dentry_ino(dentry->d_covers); ++ ++ if (ino) { ++ update_atime(dentry->d_inode); ++ ino->last_used = jiffies; ++ } ++ } ++} ++ ++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; ++ struct autofs_info *de_info = autofs_dentry_ino(dentry); + int status = 0; + +- if ( !(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ) { +- do { +- if ( status && dentry->d_inode ) { +- if ( status != -ENOENT ) +- printk("autofs warning: lookup failure on positive dentry, status = %d, name = %s\n", status, dentry->d_name.name); +- return 0; /* Try to get the kernel to invalidate this dentry */ +- } +- +- /* Turn this into a real negative dentry? */ +- if (status == -ENOENT) { +- dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; +- dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; +- return 1; +- } else if (status) { +- /* Return a negative dentry, but leave it "pending" */ +- return 1; +- } +- status = autofs_wait(sbi, &dentry->d_name); +- } while (!(ent = autofs_hash_lookup(&sbi->dirhash, &dentry->d_name)) ); +- } +- +- /* 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 */ ++ /* Block on any pending expiry here; invalidate the dentry ++ when expiration is done to trigger mount request with a new ++ dentry */ ++ if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { ++ DPRINTK(("try_to_fill_entry: waiting for expire %p name=%.*s, flags&PENDING=%s de_info=%p de_info->flags=%x\n", ++ dentry, dentry->d_name.len, dentry->d_name.name, ++ dentry->d_flags & DCACHE_AUTOFS_PENDING?"t":"f", ++ de_info, de_info?de_info->flags:0)); ++ status = autofs_wait(sbi, &dentry->d_name, NFY_NONE); ++ ++ DPRINTK(("try_to_fill_entry: expire done status=%d\n", status)); ++ ++ return 0; ++ } ++ ++ DPRINTK(("try_to_fill_entry: dentry=%p %.*s ino=%p\n", ++ dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode)); ++ ++ /* Wait for a pending mount, triggering one if there isn't one already */ ++ while(dentry->d_inode == NULL) { ++ DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s, de_info=%p de_info->flags=%x\n", ++ dentry->d_name.len, dentry->d_name.name, ++ de_info, de_info?de_info->flags:0)); ++ status = autofs_wait(sbi, &dentry->d_name, NFY_MOUNT); ++ ++ DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); ++ ++ if (status && dentry->d_inode) ++ return 0; /* Try to get the kernel to invalidate this dentry */ ++ ++ /* Turn this into a real negative dentry? */ ++ if (status == -ENOENT) { ++ dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; ++ dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; ++ return 1; ++ } else if (status) { ++ /* Return a negative dentry, but leave it "pending" */ + return 1; + } +- dentry->d_inode = inode; + } + +- /* If this is a directory that isn't a mount point, bitch at the +- daemon and fix it in user space */ +- if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) { +- return !autofs_wait(sbi, &dentry->d_name); ++ /* If this is an unused directory that isn't a mount point, ++ bitch at the daemon and fix it in user space */ ++ if (S_ISDIR(dentry->d_inode->i_mode) && ++ dentry->d_mounts == dentry && ++ list_empty(&dentry->d_subdirs)) { ++ DPRINTK(("try_to_fill_entry: mounting existing dir\n")); ++ return autofs_wait(sbi, &dentry->d_name, NFY_MOUNT) == 0; + } + + /* We don't update the usages for the autofs daemon itself, this + is necessary for recursive autofs mounts */ +- if ( !autofs_oz_mode(sbi) ) { +- autofs_update_usage(&sbi->dirhash,ent); +- } ++ if (!autofs_oz_mode(sbi)) ++ autofs_update_usage(dentry); + + dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; + return 1; +@@ -165,14 +204,15 @@ + * yet completely filled in, and revalidate has to delay such + * lookups.. + */ +-static int autofs_revalidate(struct dentry * dentry, int flags) ++static int autofs_root_revalidate(struct dentry * dentry, int flags) + { + struct inode * dir = dentry->d_parent->d_inode; + struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); +- struct autofs_dir_ent *ent; ++ struct autofs_info *ino; ++ int oz_mode = autofs_oz_mode(sbi); + + /* Pending dentry */ +- if ( dentry->d_flags & DCACHE_AUTOFS_PENDING ) { ++ if (autofs_ispending(dentry)) { + if (autofs_oz_mode(sbi)) + return 1; + else +@@ -180,11 +220,17 @@ + } + + /* Negative dentry.. invalidate if "old" */ +- if (!dentry->d_inode) ++ if (dentry->d_inode == NULL) + return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT); +- +- /* Check for a non-mountpoint directory */ +- if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) { ++ ++ ino = autofs_dentry_ino(dentry); ++ ++ /* Check for a non-mountpoint directory with no contents */ ++ if (S_ISDIR(dentry->d_inode->i_mode) && ++ dentry->d_mounts == dentry && ++ list_empty(&dentry->d_subdirs)) { ++ DPRINTK(("autofs_root_revalidate: dentry=%p %.*s, emptydir\n", ++ dentry, dentry->d_name.len, dentry->d_name.name)); + if (autofs_oz_mode(sbi)) + return 1; + else +@@ -192,27 +238,72 @@ + } + + /* Update the usage list */ +- if ( !autofs_oz_mode(sbi) ) { +- ent = (struct autofs_dir_ent *) dentry->d_time; +- if ( ent ) +- autofs_update_usage(&sbi->dirhash,ent); +- } ++ if (!oz_mode) ++ autofs_update_usage(dentry); ++ ++ return 1; ++} ++ ++static int autofs_revalidate(struct dentry *dentry, int flags) ++{ ++ struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb); ++ ++ if (!autofs_oz_mode(sbi)) ++ autofs_update_usage(dentry); ++ + return 1; + } + ++static void autofs_dentry_release(struct dentry *de) ++{ ++ struct autofs_info *inf = autofs_dentry_ino(de); ++ ++ DPRINTK(("autofs_dentry_release: releasing %p\n", de)); ++ ++ de->d_fsdata = NULL; ++ if (inf) { ++ inf->dentry = NULL; ++ inf->inode = NULL; ++ ++ autofs_free_ino(inf); ++ } ++} ++ ++/* For dentries of directories in the root dir */ ++static struct dentry_operations autofs_root_dentry_operations = { ++ d_revalidate: autofs_root_revalidate, /* d_revalidate */ ++ d_release: autofs_dentry_release, ++}; ++ ++/* For other dentries */ + static struct dentry_operations autofs_dentry_operations = { +- autofs_revalidate, /* d_revalidate */ +- NULL, /* d_hash */ +- NULL, /* d_compare */ ++ d_revalidate: autofs_revalidate, /* d_revalidate */ ++ d_release: autofs_dentry_release, + }; + ++/* Lookups in non-root dirs never find anything - if it's there, it's ++ already in the dcache */ ++static struct dentry *autofs_dir_lookup(struct inode *dir, struct dentry *dentry) ++{ ++#if 0 ++ DPRINTK(("autofs_dir_lookup: ignoring lookup of %.*s/%.*s\n", ++ dentry->d_parent->d_name.len, dentry->d_parent->d_name.name, ++ dentry->d_name.len, dentry->d_name.name)); ++#endif ++ ++ dentry->d_fsdata = NULL; ++ d_add(dentry, NULL); ++ return NULL; ++} ++ ++/* Lookups in the root directory */ + static struct dentry *autofs_root_lookup(struct inode *dir, struct dentry *dentry) + { + struct autofs_sb_info *sbi; + int oz_mode; + +- DPRINTK(("autofs_root_lookup: name = ")); +- autofs_say(dentry->d_name.name,dentry->d_name.len); ++ DPRINTK(("autofs_root_lookup: name = %.*s\n", ++ dentry->d_name.len, dentry->d_name.name)); + + if (dentry->d_name.len > NAME_MAX) + return ERR_PTR(-ENOENT);/* File name too long to exist */ +@@ -233,13 +324,20 @@ + * + * We need to do this before we release the directory semaphore. + */ +- dentry->d_op = &autofs_dentry_operations; ++ if (dir->i_ino == AUTOFS_ROOT_INO) ++ dentry->d_op = &autofs_root_dentry_operations; ++ else ++ dentry->d_op = &autofs_dentry_operations; ++ + dentry->d_flags |= DCACHE_AUTOFS_PENDING; ++ dentry->d_fsdata = NULL; + d_add(dentry, NULL); + +- up(&dir->i_sem); +- autofs_revalidate(dentry, 0); +- down(&dir->i_sem); ++ if (dentry->d_op && dentry->d_op->d_revalidate) { ++ up(&dir->i_sem); ++ (dentry->d_op->d_revalidate)(dentry, 0); ++ down(&dir->i_sem); ++ } + + /* + * If we are still pending, check if we had to handle +@@ -262,65 +360,58 @@ + return NULL; + } + +-static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname) ++static int autofs_dir_symlink(struct inode *dir, ++ struct dentry *dentry, ++ const char *symname) + { + struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); +- struct autofs_dirhash *dh = &sbi->dirhash; +- struct autofs_dir_ent *ent; +- unsigned int n; +- int slsize; +- struct autofs_symlink *sl; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); ++ struct inode *inode; ++ char *cp; + +- DPRINTK(("autofs_root_symlink: %s <- ", symname)); +- autofs_say(dentry->d_name.name,dentry->d_name.len); ++ DPRINTK(("autofs_dir_symlink: %s <- %.*s\n", symname, ++ dentry->d_name.len, dentry->d_name.name)); + +- if ( !autofs_oz_mode(sbi) ) ++ if (!S_ISDIR(dir->i_mode)) ++ return -ENOTDIR; ++ ++ if (!autofs_oz_mode(sbi)) + return -EACCES; + +- if ( dentry->d_name.len > NAME_MAX ) ++ if (dentry->d_name.len > NAME_MAX) + return -ENAMETOOLONG; + +- if ( autofs_hash_lookup(dh, &dentry->d_name) ) ++ if (dentry->d_inode != NULL) + return -EEXIST; + +- n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS); +- if ( n >= AUTOFS_MAX_SYMLINKS ) ++ ino = autofs_init_ino(ino, sbi, S_IFLNK | 0555); ++ if (ino == NULL) + 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; +- } ++ ino->size = strlen(symname); ++ ino->u.symlink = cp = kmalloc(ino->size + 1, GFP_KERNEL); + +- ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); +- if ( !ent ) { +- kfree(sl->data); +- clear_bit(n,sbi->symlink_bitmap); ++ if (cp == NULL) { ++ kfree(ino); + return -ENOSPC; + } + +- ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); +- if ( !ent->name ) { +- kfree(sl->data); +- kfree(ent); +- clear_bit(n,sbi->symlink_bitmap); +- return -ENOSPC; +- } ++ strcpy(cp, symname); + +- memcpy(sl->data,symname,slsize); +- sl->mtime = CURRENT_TIME; ++ autofs_ihash_insert(&sbi->ihash, ino); ++ inode = iget(dir->i_sb,ino->ino); ++ d_instantiate(dentry, inode); + +- ent->ino = AUTOFS_FIRST_SYMLINK + n; +- ent->hash = dentry->d_name.hash; +- memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); +- ent->dentry = NULL; /* We don't keep the dentry for symlinks */ ++ if (dir->i_ino == AUTOFS_ROOT_INO) ++ dentry->d_op = &autofs_root_dentry_operations; ++ else ++ dentry->d_op = &autofs_dentry_operations; ++ ++ dentry->d_fsdata = ino; ++ ino->dentry = dget(dentry); ++ ino->inode = inode; + +- autofs_hash_insert(dh,ent); +- d_instantiate(dentry, iget(dir->i_sb,ent->ino)); ++ dir->i_mtime = CURRENT_TIME; + + return 0; + } +@@ -330,111 +421,121 @@ + * + * 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). ++ * VFS layer can turn the dentry into a negative dentry. We don't want ++ * this, because since the unlink is probably the result of an expire. ++ * We simply d_drop it, which allows the dentry lookup to remount it ++ * if necessary. + * +- * Also see autofs_root_rmdir().. ++ * If a process is blocked on the dentry waiting for the expire to finish, ++ * it will invalidate the dentry and try to mount with a new one. ++ * ++ * Also see autofs_dir_rmdir().. + */ +-static int autofs_root_unlink(struct inode *dir, struct dentry *dentry) ++static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry) + { + struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); +- struct autofs_dirhash *dh = &sbi->dirhash; +- struct autofs_dir_ent *ent; +- unsigned int n; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); ++ ++ if (!S_ISDIR(dir->i_mode)) ++ return -ENOTDIR; + ++ if (dentry->d_inode == NULL) ++ return -ENOENT; ++ + /* This allows root to remove symlinks */ + if ( !autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN) ) + return -EACCES; + +- ent = autofs_hash_lookup(dh, &dentry->d_name); +- if ( !ent ) +- return -ENOENT; ++ dput(ino->dentry); ++ ++ dentry->d_inode->i_size = 0; ++ dentry->d_inode->i_nlink = 0; ++ ++ dir->i_mtime = CURRENT_TIME; ++ ++ DPRINTK(("autofs_dir_unlink: unlinking %p %.*s, count=%d\n", ++ dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count)); + +- n = ent->ino - AUTOFS_FIRST_SYMLINK; +- if ( n >= AUTOFS_MAX_SYMLINKS ) +- return -EISDIR; /* It's a directory, dummy */ +- if ( !test_bit(n,sbi->symlink_bitmap) ) +- return -EINVAL; /* Nonexistent symlink? Shouldn't happen */ +- +- dentry->d_time = (unsigned long)(struct autofs_dirhash *)NULL; +- 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) ++static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry) + { + struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); +- struct autofs_dirhash *dh = &sbi->dirhash; +- struct autofs_dir_ent *ent; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); + +- if ( !autofs_oz_mode(sbi) ) +- return -EACCES; ++ if (!S_ISDIR(dir->i_mode)) ++ return -ENOTDIR; + +- ent = autofs_hash_lookup(dh, &dentry->d_name); +- if ( !ent ) ++ if (dentry->d_inode == NULL) + return -ENOENT; ++ ++ if (!autofs_oz_mode(sbi)) ++ return -EACCES; + +- if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) +- return -ENOTDIR; /* Not a directory */ ++ if (!list_empty(&dentry->d_subdirs)) ++ return -ENOTEMPTY; + +- if ( ent->dentry != dentry ) { +- printk("autofs_rmdir: odentry != dentry for entry %s\n", dentry->d_name.name); +- } ++ dput(ino->dentry); ++ ++ dentry->d_inode->i_size = 0; ++ dentry->d_inode->i_nlink = 0; ++ ++ if (dir->i_nlink) ++ dir->i_nlink--; ++ ++ DPRINTK(("autofs_dir_rmdir: rmdir %p %.*s, count=%d\n", ++ dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_count)); + +- dentry->d_time = (unsigned long)(struct autofs_dir_ent *)NULL; +- 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) ++ ++ ++static int autofs_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) + { + struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb); +- struct autofs_dirhash *dh = &sbi->dirhash; +- struct autofs_dir_ent *ent; +- ino_t ino; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); ++ struct inode *inode; ++ ++ if (!S_ISDIR(dir->i_mode)) ++ return -ENOTDIR; + + if ( !autofs_oz_mode(sbi) ) + return -EACCES; + ++ if ( dentry->d_inode != NULL ) ++ return -EEXIST; ++ + if ( dentry->d_name.len > NAME_MAX ) + return -ENAMETOOLONG; + +- ent = autofs_hash_lookup(dh, &dentry->d_name); +- if ( ent ) +- return -EEXIST; ++ DPRINTK(("autofs_dir_mkdir: dentry %p, creating %.*s\n", ++ dentry, dentry->d_name.len, dentry->d_name.name)); + +- if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) { +- printk("autofs: Out of inode numbers -- what the heck did you do??\n"); ++ ino = autofs_init_ino(ino, sbi, S_IFDIR | 0555); ++ if (ino == NULL) + return -ENOSPC; +- } +- ino = sbi->next_dir_ino++; + +- ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL); +- if ( !ent ) +- return -ENOSPC; ++ autofs_ihash_insert(&sbi->ihash, ino); + +- ent->name = kmalloc(dentry->d_name.len+1, GFP_KERNEL); +- if ( !ent->name ) { +- kfree(ent); +- return -ENOSPC; +- } ++ inode = iget(dir->i_sb, ino->ino); ++ d_instantiate(dentry, inode); + +- ent->hash = dentry->d_name.hash; +- memcpy(ent->name, dentry->d_name.name, 1+(ent->len = dentry->d_name.len)); +- ent->ino = ino; +- ent->dentry = dentry; +- autofs_hash_insert(dh,ent); ++ if (dir->i_ino == AUTOFS_ROOT_INO) ++ dentry->d_op = &autofs_root_dentry_operations; ++ else ++ dentry->d_op = &autofs_dentry_operations; + ++ dentry->d_fsdata = ino; ++ ino->dentry = dget(dentry); ++ ino->inode = inode; + dir->i_nlink++; +- d_instantiate(dentry, iget(dir->i_sb,ino)); ++ dir->i_mtime = CURRENT_TIME; + + return 0; + } +@@ -459,36 +560,21 @@ + } + + /* Return protocol version */ +-static inline int autofs_get_protover(int *p) ++static inline int autofs_get_protover(struct autofs_sb_info *sbi, int *p) + { +- return put_user(AUTOFS_PROTO_VERSION, p); ++ return put_user(sbi->version, p); + } + +-/* Perform an expiry operation */ +-static inline int autofs_expire_run(struct super_block *sb, +- struct autofs_sb_info *sbi, +- struct autofs_packet_expire *pkt_p) ++/* Identify autofs_dentries - this is so we can tell if there's ++ an extra dentry refcount or not. We only hold a refcount on the ++ dentry if its non-negative (ie, d_inode != NULL) ++*/ ++int is_autofs_dentry(struct dentry *dentry) + { +- struct autofs_dir_ent *ent; +- struct autofs_packet_expire pkt; +- +- 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(sb,sbi)) ) +- 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; +- +- return 0; ++ return dentry && dentry->d_inode && ++ (dentry->d_op == &autofs_root_dentry_operations || ++ dentry->d_op == &autofs_dentry_operations) && ++ dentry->d_fsdata != NULL; + } + + /* +@@ -500,7 +586,8 @@ + { + struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); + +- DPRINTK(("autofs_ioctl: cmd = 0x%08x, 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 ) +@@ -518,12 +605,18 @@ + autofs_catatonic_mode(sbi); + return 0; + case AUTOFS_IOC_PROTOVER: /* Get protocol version */ +- return autofs_get_protover((int *)arg); ++ return autofs_get_protover(sbi, (int *)arg); + case AUTOFS_IOC_SETTIMEOUT: + return autofs_get_set_timeout(sbi,(unsigned long *)arg); ++ ++ /* return a single thing to expire */ + case AUTOFS_IOC_EXPIRE: + return autofs_expire_run(inode->i_sb,sbi, + (struct autofs_packet_expire *)arg); ++ /* same as above, but can send multiple expires through pipe */ ++ case AUTOFS_IOC_EXPIRE_MULTI: ++ return autofs_expire_multi(inode->i_sb, sbi, (int *)arg); ++ + default: + return -ENOSYS; + } +diff -X ../diffexcl -Nur 2.2/fs/autofs/symlink.c abulafia/fs/autofs/symlink.c +--- 2.2/fs/autofs/symlink.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/symlink.c Sun Jan 23 02:05:38 2000 +@@ -16,45 +16,26 @@ + + static int autofs_readlink(struct dentry *dentry, char *buffer, int buflen) + { +- struct autofs_symlink *sl; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); + int len; + +- sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip; +- len = sl->len; +- if (len > buflen) len = buflen; +- copy_to_user(buffer, sl->data, len); ++ len = ino->size; ++ if (len > buflen) ++ len = buflen; ++ copy_to_user(buffer, ino->u.symlink, len); + return len; + } + + static struct dentry * autofs_follow_link(struct dentry *dentry, +- struct dentry *base, +- unsigned int follow) ++ struct dentry *base, ++ unsigned int follow) + { +- struct autofs_symlink *sl; ++ struct autofs_info *ino = autofs_dentry_ino(dentry); + +- sl = (struct autofs_symlink *)dentry->d_inode->u.generic_ip; +- return lookup_dentry(sl->data, base, follow); ++ return lookup_dentry(ino->u.symlink, base, follow); + } + + 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 */ ++ readlink: autofs_readlink, ++ follow_link: autofs_follow_link + }; +diff -X ../diffexcl -Nur 2.2/fs/autofs/waitq.c abulafia/fs/autofs/waitq.c +--- 2.2/fs/autofs/waitq.c Thu Jan 13 06:27:09 2000 ++++ linux/fs/autofs/waitq.c Sun Jan 23 02:05:41 2000 +@@ -40,8 +40,13 @@ + wake_up(&wq->queue); + wq = nwq; + } +- fput(sbi->pipe); /* Close the pipe */ +- autofs_hash_dputall(&sbi->dirhash); /* Remove all dentry pointers */ ++ if (sbi->pipe) { ++ fput(sbi->pipe); /* Close the pipe */ ++ sbi->pipe = NULL; ++ } ++ ++ autofs_ihash_nuke(&sbi->ihash); ++ shrink_dcache_sb(sbi->sb); + } + + static int autofs_write(struct file *file, const void *addr, int bytes) +@@ -79,27 +84,49 @@ + return (bytes > 0); + } + +-static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq) ++static void autofs_notify_daemon(struct autofs_sb_info *sbi, ++ struct autofs_wait_queue *wq, ++ enum autofs_packet_type type) + { +- struct autofs_packet_missing pkt; ++ union autofs_packet_union pkt; ++ size_t pktsz; + +- DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token)); +- autofs_say(wq->name,wq->len); ++ DPRINTK(("autofs_notify: wait id = 0x%08lx, name = %.*s, type=%d\n", ++ wq->wait_queue_token, wq->len, wq->name, type)); + + 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'; ++ pkt.hdr.proto_version = sbi->version; ++ pkt.hdr.type = type; ++ if (type == autofs_ptype_missing) { ++ struct autofs_packet_missing *mp = &pkt.missing; ++ ++ pktsz = sizeof(*mp); ++ ++ mp->wait_queue_token = wq->wait_queue_token; ++ mp->len = wq->len; ++ memcpy(mp->name, wq->name, wq->len); ++ mp->name[wq->len] = '\0'; ++ } else if (type == autofs_ptype_expire_multi) { ++ struct autofs_packet_expire_multi *ep = &pkt.expire_multi; ++ ++ pktsz = sizeof(*ep); ++ ++ ep->wait_queue_token = wq->wait_queue_token; ++ ep->len = wq->len; ++ memcpy(ep->name, wq->name, wq->len); ++ ep->name[wq->len] = '\0'; ++ } else { ++ printk("autofs_notify_daemon: bad type %d!\n", type); ++ return; ++ } + +- if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) ) ++ if (autofs_write(sbi->pipe, &pkt, pktsz)) + autofs_catatonic_mode(sbi); + } + +-int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name) ++int autofs_wait(struct autofs_sb_info *sbi, struct qstr *name, ++ enum autofs_notify notify) + { + struct autofs_wait_queue *wq; + int status; +@@ -130,7 +157,9 @@ + kfree(wq); + return -ENOMEM; + } +- wq->wait_queue_token = autofs_next_wait_queue++; ++ wq->wait_queue_token = autofs_next_wait_queue; ++ if (++autofs_next_wait_queue == 0) ++ autofs_next_wait_queue = 1; + init_waitqueue(&wq->queue); + wq->hash = name->hash; + wq->len = name->len; +@@ -139,11 +168,20 @@ + wq->next = sbi->queues; + sbi->queues = wq; + ++ DPRINTK(("autofs_wait: new wait id = 0x%08lx, name = %.*s, nfy=%d\n", ++ wq->wait_queue_token, wq->len, wq->name, notify)); + /* autofs_notify_daemon() may block */ + wq->wait_ctr = 2; +- autofs_notify_daemon(sbi,wq); +- } else ++ if (notify != NFY_NONE) { ++ autofs_notify_daemon(sbi,wq, ++ notify == NFY_MOUNT ? autofs_ptype_missing : ++ autofs_ptype_expire_multi); ++ } ++ } else { + wq->wait_ctr++; ++ DPRINTK(("autofs_wait: existing wait id = 0x%08lx, name = %.*s, nfy=%d\n", ++ wq->wait_queue_token, wq->len, wq->name, notify)); ++ } + + /* wq->name is NULL if and only if the lock is already released */ + +@@ -179,7 +217,7 @@ + + status = wq->status; + +- if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */ ++ if (--wq->wait_ctr == 0) /* Are we the last process to need status? */ + kfree(wq); + + return status; +@@ -203,7 +241,7 @@ + + wq->status = status; + +- if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */ ++ if (--wq->wait_ctr == 0) /* Is anyone still waiting for this guy? */ + kfree(wq); + else + wake_up(&wq->queue); diff -ruN autofs-4.0.0-pre1/samples/auto.net autofs-4.0.0-pre2/samples/auto.net --- autofs-4.0.0-pre1/samples/auto.net Sat Jan 29 15:09:48 2000 +++ autofs-4.0.0-pre2/samples/auto.net Sat Jan 29 15:14:06 2000 @@ -1,8 +1,9 @@ #!/bin/sh +# $Id: //depot/autofs-4.0/samples/auto.net#2 $ + # Look at what a host is exporting to determine what we can mount. -# This is wildly simplistic - no attempt is made to see whether we can mount -# each filesystem, or whether its RO or RW. For the future... +# This is very simple, but it appears to work surprisingly well key="$1" opts="-fstype=nfs,hard,intr,nodev,nosuid" @@ -13,6 +14,7 @@ # which both accepts and acts on the '--no-headers' flag. #SHOWMOUNT="kshowmount --no-headers -e $key" #SHOWMOUNT="showmount -e $key | tail +2" + # Newer distributions get this right SHOWMOUNT="showmount --no-headers -e $key" diff -ruN autofs-4.0.0-pre1/samples/rc.autofs.in autofs-4.0.0-pre2/samples/rc.autofs.in --- autofs-4.0.0-pre1/samples/rc.autofs.in Sat Jan 29 15:09:48 2000 +++ autofs-4.0.0-pre2/samples/rc.autofs.in Sat Jan 29 15:14:06 2000 @@ -1,6 +1,6 @@ #! /bin/bash # -# $Id: rc.autofs.in,v 1.3 1999/03/07 22:59:55 hpa Exp $ +# $Id: //depot/autofs-4.0/samples/rc.autofs.in#2 $ # # rc file for automount using a Sun-style "master map". # We first look for a local /etc/auto.master, then a YP @@ -64,6 +64,12 @@ localoptions='' # +# Daemon options +# e.g. --timeout 60 +# +daemonoptions='' + +# # This function will build a list of automount commands to execute in # order to activate all the mount points. It is used to figure out # the difference of automount points in case of a reload @@ -84,11 +90,11 @@ map=`echo "/etc/$map" | sed -e 's:^/etc//:/:'` options=`echo "$options" | sed -e 's/\(^\|[ \t]\)-/\1/g'` if [ -x $map ]; then - echo "$DAEMON $dir program $map $options $localoptions" + echo "$DAEMON $daemonoptions $dir program $map $options $localoptions" elif [ -f $map ]; then - echo "$DAEMON $dir file $map $options $localoptions" + echo "$DAEMON $daemonoptions $dir file $map $options $localoptions" else - echo "$DAEMON $dir `basename $map` $options $localoptions" + echo "$DAEMON $daemonoptions $dir `basename $map` $options $localoptions" fi fi done @@ -114,7 +120,7 @@ options=`echo "$options" | sed -e ' s/--*t\(imeout\)*[ \t]*[0-9][0-9]*//g s/\(^\|[ \t]\)-/\1/g'` - echo "$DAEMON $dir yp $map $options $localoptions" + echo "$DAEMON $daemonoptions $mountoptions $dir yp $map $options $localoptions" fi done ) @@ -137,6 +143,20 @@ ) } +# return true if at least one pid is alive +function alive() +{ + if [ -z "$*" ]; then + return 1 + fi + for i in $*; do + if kill -0 $i 2> /dev/null; then + return 0 + fi + done + + return 1 +} # # Redhat start/stop function. @@ -157,7 +177,21 @@ fi ;; stop) - kill -TERM $(/sbin/pidof /usr/sbin/automount) + pids=$(/sbin/pidof $DAEMON) + kill -TERM $pids 2> /dev/null && sleep 1 + count=1 + while alive $pids; do + sleep 5 + count=$(expr $count + 1) + if [ $count -gt 10 ]; then + echo "Giving up on automounter" + break; + fi + echo "Automounter not stopped yet: retrying... (attempt $count)" + done + if [ $count -gt 1 -a $count -le 10 ]; then + echo "Automounter stopped" + fi rm -f /var/lock/subsys/autofs ;; reload|restart) @@ -169,21 +203,23 @@ TMP1=`mktemp /tmp/autofs.XXXXXX` || { echo "could not make temp file" >& 2; exit 1; } TMP2=`mktemp /tmp/autofs.XXXXXX` || { echo "could not make temp file" >& 2; exit 1; } getmounts >$TMP1 - ps ax|grep "[0-9]:[0-9][0-9] automount " | ( + ps ax|grep "[0-9]:[0-9][0-9] $DAEMON " | ( while read pid tt stat time command; do echo "$command" >>$TMP2 if ! grep -q "^$command" $TMP2; then - kill -USR2 $pid + while kill -USR2 $pid; do + sleep 3 + done echo "Stop $command" fi done ) - cat $TMP1 | ( while read x; do + ( while read x; do if ! grep -q "^$x" $TMP2; then $x echo "Start $x" fi - done ) + done ) < $TMP1 rm -f $TMP1 $TMP2 ;; status) @@ -211,16 +247,7 @@ echo -n " $mnt" pidfile=/var/run/autofs`echo $mnt | sed 's/\//./'`.pid start-stop-daemon --start --pidfile $pidfile --quiet \ - --exec $DAEMON -- $mnt $rest - # - # Automount needs a '--pidfile' or '-p' option. - # For now we look for the pid ourself. - # - ps ax | grep "[0-9]:[0-9][0-9] $DAEMON $mnt" | ( - read pid rest - echo $pid > $pidfile - echo "$mnt $rest" >> $pidfile - ) + --exec $DAEMON $daemonoptions -- $mnt $rest done echo "." ;;