diff -u --recursive --new-file v2.1.57/linux/CREDITS linux/CREDITS --- v2.1.57/linux/CREDITS Wed Sep 24 20:05:45 1997 +++ linux/CREDITS Mon Oct 13 11:46:49 1997 @@ -632,8 +632,9 @@ N: Nick Holloway E: Nick.Holloway@alfie.demon.co.uk E: Nick.Holloway@parallax.co.uk -D: Small patches for kernel, libc -D: MAKEDEV +W: http://www.alfie.demon.co.uk/ +P: 1024/75C49395 3A F0 E3 4E B7 9F E0 7E 47 A3 B0 D5 68 6A C2 FB +D: Occasional Linux hacker... S: 15 Duke Street S: Chapelfields S: Coventry diff -u --recursive --new-file v2.1.57/linux/Makefile linux/Makefile --- v2.1.57/linux/Makefile Wed Sep 24 20:05:45 1997 +++ linux/Makefile Mon Oct 13 16:50:49 1997 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 1 -SUBLEVEL = 57 +SUBLEVEL = 58 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/) diff -u --recursive --new-file v2.1.57/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c --- v2.1.57/linux/arch/i386/mm/fault.c Mon Aug 18 18:19:42 1997 +++ linux/arch/i386/mm/fault.c Tue Oct 14 18:11:47 1997 @@ -172,9 +172,10 @@ /* Are we prepared to handle this kernel fault? */ if ((fixup = search_exception_table(regs->eip)) != 0) { - printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n", + printk(KERN_DEBUG "%s: Exception at [<%lx>] cr2=%lx (fixup: %lx)\n", tsk->comm, regs->eip, + address, fixup); regs->eip = fixup; goto out; diff -u --recursive --new-file v2.1.57/linux/fs/bad_inode.c linux/fs/bad_inode.c --- v2.1.57/linux/fs/bad_inode.c Tue Sep 23 16:48:48 1997 +++ linux/fs/bad_inode.c Sun Oct 12 09:45:12 1997 @@ -19,7 +19,7 @@ return ERR_PTR(-EIO); } -static int return_EIO() +static int return_EIO(void) { return -EIO; } @@ -81,3 +81,12 @@ inode->i_op = &bad_inode_ops; } +/* + * This tests whether an inode has been flagged as bad. The test uses + * &bad_inode_ops to cover the case of invalidated inodes as well as + * those created by make_bad_inode() above. + */ +int is_bad_inode(struct inode * inode) +{ + return (inode->i_op == &bad_inode_ops); +} diff -u --recursive --new-file v2.1.57/linux/fs/binfmt_misc.c linux/fs/binfmt_misc.c --- v2.1.57/linux/fs/binfmt_misc.c Wed Sep 3 20:52:43 1997 +++ linux/fs/binfmt_misc.c Sun Oct 12 10:16:38 1997 @@ -281,21 +281,19 @@ const char *sp; char del, *dp; struct binfmt_entry *e; - int memsize, cnt = count - 1, err = 0; + int memsize, cnt = count - 1, err; - MOD_INC_USE_COUNT; /* some sanity checks */ - if ((count < 11) || (count > 256)) { - err = -EINVAL; + err = -EINVAL; + if ((count < 11) || (count > 256)) goto _err; - } + err = -ENOMEM; memsize = sizeof(struct binfmt_entry) + count; - if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) { - err = -ENOMEM; + if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) goto _err; - } + err = 0; sp = buffer + 1; del = buffer[0]; dp = (char *)e + sizeof(struct binfmt_entry); @@ -327,12 +325,8 @@ /* more sanity checks */ if (err || !(!cnt || (!(--cnt) && (*sp == '\n'))) || (e->size < 1) || ((e->size + e->offset) > 127) || - !(e->proc_name) || !(e->interpreter) || - entry_proc_setup(e)) { - kfree(e); - err = -EINVAL; - goto _err; - } + !(e->proc_name) || !(e->interpreter) || entry_proc_setup(e)) + goto free_err; write_lock(&entries_lock); e->next = entries; @@ -341,8 +335,11 @@ err = count; _err: - MOD_DEC_USE_COUNT; return err; +free_err: + kfree(e); + err = -EINVAL; + goto _err; } /* @@ -357,7 +354,6 @@ char *dp; int elen, i, err; - MOD_INC_USE_COUNT; #ifndef VERBOSE_STATUS if (data) { if (!(e = get_entry((int) data))) { @@ -415,7 +411,6 @@ err = elen; _err: - MOD_DEC_USE_COUNT; return err; } @@ -429,7 +424,6 @@ struct binfmt_entry *e; int res = count; - MOD_INC_USE_COUNT; if (buffer[count-1] == '\n') count--; if ((count == 1) && !(buffer[0] & ~('0' | '1'))) { @@ -449,7 +443,6 @@ } else { res = -EINVAL; } - MOD_DEC_USE_COUNT; return res; } @@ -477,29 +470,57 @@ return 0; } +#ifdef MODULE +/* + * This is called as the fill_inode function when an inode + * is going into (fill = 1) or out of service (fill = 0). + * We use it here to manage the module use counts. + * + * Note: only the top-level directory needs to do this; if + * a lower level is referenced, the parent will be as well. + */ +static void bm_modcount(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} +#endif __initfunc(int init_misc_binfmt(void)) { struct proc_dir_entry *status = NULL, *reg; + int error = -ENOMEM; - if (!(bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, - NULL)) || - !(status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR, - bm_dir)) || - !(reg = create_proc_entry("register", S_IFREG | S_IWUSR, - bm_dir))) { - if (status) - remove_proc_entry("status", bm_dir); - if (bm_dir) - remove_proc_entry("sys/fs/binfmt_misc", NULL); - return -ENOMEM; - } + bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL); + if (!bm_dir) + goto out; +#ifdef MODULE + bm_dir->fill_inode = bm_modcount; +#endif + + status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR, + bm_dir); + if (!status) + goto cleanup_bm; status->read_proc = proc_read_status; status->write_proc = proc_write_status; + reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir); + if (!reg) + goto cleanup_status; reg->write_proc = proc_write_register; - return register_binfmt(&misc_format); + error = register_binfmt(&misc_format); +out: + return error; + +cleanup_status: + remove_proc_entry("status", bm_dir); +cleanup_bm: + remove_proc_entry("sys/fs/binfmt_misc", NULL); + goto out; } #ifdef MODULE diff -u --recursive --new-file v2.1.57/linux/fs/dcache.c linux/fs/dcache.c --- v2.1.57/linux/fs/dcache.c Tue Sep 23 16:48:48 1997 +++ linux/fs/dcache.c Sun Oct 12 11:31:24 1997 @@ -61,37 +61,54 @@ */ void dput(struct dentry *dentry) { - if (dentry) { - int count; + int count; + + if (!dentry) + return; + repeat: - count = dentry->d_count-1; - if (count < 0) { - printk("Negative d_count (%d) for %s/%s\n", - count, - dentry->d_parent->d_name.name, - dentry->d_name.name); - *(int *)0 = 0; - } + count = dentry->d_count - 1; + if (count != 0) + goto out; + + /* + * Note that if d_op->d_delete blocks, + * the dentry could go back in use. + * Each fs will have to watch for this. + */ + if (dentry->d_op && dentry->d_op->d_delete) { + dentry->d_op->d_delete(dentry); + + count = dentry->d_count - 1; + if (count != 0) + goto out; + } + + list_del(&dentry->d_lru); + if (list_empty(&dentry->d_hash)) { + struct inode *inode = dentry->d_inode; + struct dentry * parent; + if (inode) + iput(inode); + parent = dentry->d_parent; + d_free(dentry); + if (dentry == parent) + return; + dentry = parent; + goto repeat; + } + list_add(&dentry->d_lru, &dentry_unused); +out: + if (count >= 0) { dentry->d_count = count; - if (!count) { - list_del(&dentry->d_lru); - if (dentry->d_op && dentry->d_op->d_delete) - dentry->d_op->d_delete(dentry); - if (list_empty(&dentry->d_hash)) { - struct inode *inode = dentry->d_inode; - struct dentry * parent; - if (inode) - iput(inode); - parent = dentry->d_parent; - d_free(dentry); - if (dentry == parent) - return; - dentry = parent; - goto repeat; - } - list_add(&dentry->d_lru, &dentry_unused); - } + return; } + + printk("Negative d_count (%d) for %s/%s\n", + count, + dentry->d_parent->d_name.name, + dentry->d_name.name); + *(int *)0 = 0; } /* @@ -99,13 +116,14 @@ * possible. If there are other users of the dentry we * can't invalidate it. * - * This is currently incorrect. We should try to see if - * we can invalidate any unused children - right now we - * refuse to invalidate way too much. + * We should probably try to see if we can invalidate + * any unused children - right now we refuse to invalidate + * too much. That would require a better child list + * data structure, though. */ int d_invalidate(struct dentry * dentry) { - /* We should do a partial shrink_dcache here */ + /* We might want to do a partial shrink_dcache here */ if (dentry->d_count != 1) return -EBUSY; @@ -114,6 +132,27 @@ } /* + * Throw away a dentry - free the inode, dput the parent. + * This requires that the LRU list has already been + * removed. + */ +static inline void prune_one_dentry(struct dentry * dentry) +{ + struct dentry * parent; + + list_del(&dentry->d_hash); + if (dentry->d_inode) { + struct inode * inode = dentry->d_inode; + + dentry->d_inode = NULL; + iput(inode); + } + parent = dentry->d_parent; + d_free(dentry); + dput(parent); +} + +/* * Shrink the dcache. This is done when we need * more memory, or simply when we need to unmount * something (at which point we need to unuse @@ -131,21 +170,62 @@ INIT_LIST_HEAD(tmp); dentry = list_entry(tmp, struct dentry, d_lru); if (!dentry->d_count) { - struct dentry * parent; - - list_del(&dentry->d_hash); - if (dentry->d_inode) { - struct inode * inode = dentry->d_inode; - - dentry->d_inode = NULL; - iput(inode); - } - parent = dentry->d_parent; - d_free(dentry); - dput(parent); + prune_one_dentry(dentry); if (!--count) break; } + } +} + +/* + * Shrink the dcache for the specified super block. + * This allows us to unmount a device without disturbing + * the dcache for the other devices. + * + * This implementation makes just two traversals of the + * unused list. On the first pass we move the selected + * dentries to the most recent end, and on the second + * pass we free them. The second pass must restart after + * each dput(), but since the target dentries are all at + * the end, it's really just a single traversal. + */ +void shrink_dcache_sb(struct super_block * sb) +{ + struct list_head *tmp, *next; + struct dentry *dentry; + + /* + * Pass one ... move the dentries for the specified + * superblock to the most recent end of the unused list. + */ + next = dentry_unused.next; + while (next != &dentry_unused) { + tmp = next; + next = tmp->next; + dentry = list_entry(tmp, struct dentry, d_lru); + if (dentry->d_sb != sb) + continue; + list_del(tmp); + list_add(tmp, &dentry_unused); + } + + /* + * Pass two ... free the dentries for this superblock. + */ +repeat: + next = dentry_unused.next; + while (next != &dentry_unused) { + tmp = next; + next = tmp->next; + dentry = list_entry(tmp, struct dentry, d_lru); + if (dentry->d_sb != sb) + continue; + if (dentry->d_count) + continue; + list_del(tmp); + INIT_LIST_HEAD(tmp); + prune_one_dentry(dentry); + goto repeat; } } diff -u --recursive --new-file v2.1.57/linux/fs/file_table.c linux/fs/file_table.c --- v2.1.57/linux/fs/file_table.c Wed Sep 3 20:52:43 1997 +++ linux/fs/file_table.c Sun Oct 12 10:10:40 1997 @@ -18,8 +18,9 @@ static kmem_cache_t *filp_cache; /* sysctl tunables... */ -int nr_files = 0; -int max_files = NR_FILE; +int nr_files = 0; /* read only */ +int nr_free_files = 0; /* read only */ +int max_files = NR_FILE;/* tunable */ /* Free list management, if you are here you must have f_count == 0 */ static struct file * free_filps = NULL; @@ -30,6 +31,7 @@ free_filps->f_pprev = &file->f_next; free_filps = file; file->f_pprev = &free_filps; + nr_free_files++; } /* The list of in-use filp's must be exported (ugh...) */ @@ -43,6 +45,7 @@ file->f_pprev = &inuse_filps; } +/* N.B. This should be an __initfunc ... */ void file_table_init(void) { filp_cache = kmem_cache_create("filp", sizeof(struct file), @@ -50,6 +53,11 @@ SLAB_HWCACHE_ALIGN, NULL, NULL); if(!filp_cache) panic("VFS: Cannot alloc filp SLAB cache."); + /* + * We could allocate the reserved files here, but really + * shouldn't need to: the normal boot process will create + * plenty of free files. + */ } /* Find an unused file structure and return a pointer to it. @@ -61,24 +69,31 @@ static int old_max = 0; struct file * f; - f = free_filps; - if (!f) - goto get_more; - remove_filp(f); -got_one: - memset(f, 0, sizeof(*f)); - f->f_count = 1; - f->f_version = ++event; - put_inuse(f); - return f; - -get_more: - /* Reserve a few files for the super-user.. */ - if (nr_files < (current->euid ? max_files - 10 : max_files)) { + if (nr_free_files > NR_RESERVED_FILES) { + used_one: + f = free_filps; + remove_filp(f); + nr_free_files--; + new_one: + memset(f, 0, sizeof(*f)); + f->f_count = 1; + f->f_version = ++event; + put_inuse(f); + return f; + } + /* + * Use a reserved one if we're the superuser + */ + if (nr_free_files && !current->euid) + goto used_one; + /* + * Allocate a new one if we're below the limit. + */ + if (nr_files < max_files) { f = kmem_cache_alloc(filp_cache, SLAB_KERNEL); if (f) { nr_files++; - goto got_one; + goto new_one; } /* Big problems... */ printk("VFS: filp allocation failed\n"); diff -u --recursive --new-file v2.1.57/linux/fs/lockd/clntproc.c linux/fs/lockd/clntproc.c --- v2.1.57/linux/fs/lockd/clntproc.c Thu Jul 17 10:06:07 1997 +++ linux/fs/lockd/clntproc.c Sun Oct 12 10:17:46 1997 @@ -160,8 +160,9 @@ nlmclnt_grace_wait(struct nlm_host *host) { if (!host->h_reclaiming) - current->timeout = 10 * HZ; + current->timeout = jiffies + 10 * HZ; interruptible_sleep_on(&host->h_gracewait); + current->timeout = 0; return signalled()? -ERESTARTSYS : 0; } @@ -178,9 +179,11 @@ sizeof(struct nlm_rqst)); if (call) return call; - current->timeout = 5 * HZ; + printk("nlmclnt_alloc_call: failed, waiting for memory\n"); + current->timeout = jiffies + 5 * HZ; current->state = TASK_INTERRUPTIBLE; schedule(); + current->timeout = 0; } return NULL; } @@ -232,6 +235,7 @@ /* Back off a little and try again */ current->timeout = jiffies + 15 * HZ; interruptible_sleep_on(&host->h_gracewait); + current->timeout = 0; } while (!signalled()); return -ERESTARTSYS; diff -u --recursive --new-file v2.1.57/linux/fs/lockd/svc.c linux/fs/lockd/svc.c --- v2.1.57/linux/fs/lockd/svc.c Wed Apr 23 19:01:26 1997 +++ linux/fs/lockd/svc.c Sun Oct 12 10:17:46 1997 @@ -42,11 +42,15 @@ extern struct svc_program nlmsvc_program; struct nlmsvc_binding * nlmsvc_ops = NULL; -static int nlmsvc_sema = 0; -static int nlmsvc_pid = 0; +static struct semaphore nlmsvc_sema = MUTEX; +static unsigned int nlmsvc_users = 0; +static pid_t nlmsvc_pid = 0; unsigned long nlmsvc_grace_period = 0; unsigned long nlmsvc_timeout = 0; +static struct wait_queue * lockd_start = NULL; +static struct wait_queue * lockd_exit = NULL; + /* * Currently the following can be set only at insmod time. * Ideally, they would be accessible through the sysctl interface. @@ -64,10 +68,16 @@ sigset_t oldsigmask; int err = 0; - lock_kernel(); /* Lock module and set up kernel thread */ MOD_INC_USE_COUNT; - /* exit_files(current); */ + lock_kernel(); + + /* + * Let our maker know we're running. + */ + nlmsvc_pid = current->pid; + wake_up(&lockd_start); + exit_mm(current); current->session = 1; current->pgrp = 1; @@ -76,6 +86,12 @@ /* kick rpciod */ rpciod_up(); + /* + * N.B. current do_fork() doesn't like NULL task->files, + * so we defer closing files until forking rpciod. + */ + exit_files(current); + dprintk("NFS locking service started (ver " LOCKD_VERSION ").\n"); if (!nlm_timeout) @@ -94,14 +110,14 @@ nlmsvc_grace_period += jiffies; nlmsvc_timeout = nlm_timeout * HZ; - nlmsvc_pid = current->pid; /* * The main request loop. We don't terminate until the last * NFS mount or NFS daemon has gone away, and we've been sent a - * signal. + * signal, or else another process has taken over our job. */ - while (nlmsvc_sema || !signalled()) { + while ((nlmsvc_users || !signalled()) && nlmsvc_pid == current->pid) + { if (signalled()) current->signal = 0; @@ -156,8 +172,17 @@ nlmsvc_ops->exp_unlock(); } - nlm_shutdown_hosts(); - + /* + * Check whether there's a new lockd process before + * shutting down the hosts and clearing the slot. + */ + if (!nlmsvc_pid || current->pid == nlmsvc_pid) { + nlm_shutdown_hosts(); + nlmsvc_pid = 0; + } else + printk("lockd: new process, skipping host shutdown\n"); + wake_up(&lockd_exit); + /* Exit the RPC thread */ svc_exit_thread(rqstp); @@ -166,7 +191,6 @@ /* Release module */ MOD_DEC_USE_COUNT; - nlmsvc_pid = 0; } /* @@ -185,42 +209,100 @@ return svc_create_socket(serv, protocol, &sin); } +/* + * Bring up the lockd process if it's not already up. + */ int lockd_up(void) { struct svc_serv * serv; - int error; + int error = 0; - if (nlmsvc_pid || nlmsvc_sema++) - return 0; + down(&nlmsvc_sema); + /* + * Unconditionally increment the user count ... this is + * the number of clients who _want_ a lockd process. + */ + nlmsvc_users++; + /* + * Check whether we're already up and running. + */ + if (nlmsvc_pid) + goto out; + + /* + * Sanity check: if there's no pid, + * we should be the first user ... + */ + if (nlmsvc_users > 1) + printk("lockd_up: no pid, %d users??\n", nlmsvc_users); - dprintk("lockd: creating service\n"); - if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL) - return -ENOMEM; + error = -ENOMEM; + serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE); + if (!serv) { + printk("lockd_up: create service failed\n"); + goto out; + } - if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0 + if ((error = lockd_makesock(serv, IPPROTO_UDP, 0)) < 0 || (error = lockd_makesock(serv, IPPROTO_TCP, 0)) < 0) { - svc_destroy(serv); - return error; + printk("lockd_up: makesock failed, error=%d\n", error); + goto destroy_and_out; } - if ((error = svc_create_thread(lockd, serv)) < 0) - nlmsvc_sema--; + /* + * Create the kernel thread and wait for it to start. + */ + error = svc_create_thread(lockd, serv); + if (error) { + printk("lockd_up: create thread failed, error=%d\n", error); + goto destroy_and_out; + } + sleep_on(&lockd_start); - /* Release server */ + /* + * Note: svc_serv structures have an initial use count of 1, + * so we exit through here on both success and failure. + */ +destroy_and_out: svc_destroy(serv); - return 0; +out: + up(&nlmsvc_sema); + return error; } +/* + * Decrement the user count and bring down lockd if we're the last. + */ void lockd_down(void) { - if (!nlmsvc_pid || --nlmsvc_sema > 0) - return; + down(&nlmsvc_sema); + if (nlmsvc_users) { + if (--nlmsvc_users) + goto out; + } else + printk("lockd_down: no users! pid=%d\n", nlmsvc_pid); + + if (!nlmsvc_pid) { + printk("lockd_down: nothing to do!\n"); + goto out; + } kill_proc(nlmsvc_pid, SIGKILL, 1); - nlmsvc_sema = 0; - nlmsvc_pid = 0; + /* + * Wait for the lockd process to exit, but since we're holding + * the lockd semaphore, we can't wait around forever ... + */ + current->timeout = jiffies + 5 * HZ; + interruptible_sleep_on(&lockd_exit); + current->timeout = 0; + if (nlmsvc_pid) { + printk("lockd_down: lockd failed to exit, clearing pid\n"); + nlmsvc_pid = 0; + } +out: + up(&nlmsvc_sema); } #ifdef MODULE @@ -235,6 +317,11 @@ int init_module(void) { + /* Init the static variables */ + nlmsvc_sema = MUTEX; + nlmsvc_users = 0; + nlmsvc_pid = 0; + lockd_exit = NULL; nlmxdr_init(); return 0; } diff -u --recursive --new-file v2.1.57/linux/fs/locks.c linux/fs/locks.c --- v2.1.57/linux/fs/locks.c Wed Sep 24 20:05:48 1997 +++ linux/fs/locks.c Tue Oct 14 11:33:41 1997 @@ -881,9 +881,6 @@ if (caller->fl_type != F_UNLCK) { repeat: - error = -ERESTARTSYS; - if (signal_pending(current)) - goto out; for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) continue; @@ -894,6 +891,9 @@ goto out; error = -EDEADLK; if (posix_locks_deadlock(caller, fl)) + goto out; + error = -ERESTARTSYS; + if (signal_pending(current)) goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); diff -u --recursive --new-file v2.1.57/linux/fs/namei.c linux/fs/namei.c --- v2.1.57/linux/fs/namei.c Tue Sep 23 16:48:49 1997 +++ linux/fs/namei.c Sun Oct 12 10:13:51 1997 @@ -232,11 +232,14 @@ result = d_lookup(parent, name); if (!result) { struct dentry * dentry = d_alloc(parent, name); - int error = dir->i_op->lookup(dir, dentry); - result = ERR_PTR(error); - if (!error) - result = dget(dentry->d_mounts); - dput(dentry); + result = ERR_PTR(-ENOMEM); + if (dentry) { + int error = dir->i_op->lookup(dir, dentry); + result = ERR_PTR(error); + if (!error) + result = dget(dentry->d_mounts); + dput(dentry); + } } up(&dir->i_sem); return result; @@ -1170,7 +1173,8 @@ if (new_dir->d_inode->i_sb && new_dir->d_inode->i_sb->dq_op) new_dir->d_inode->i_sb->dq_op->initialize(new_dir->d_inode, -1); - error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry, new_dir->d_inode, new_dentry); + error = old_dir->d_inode->i_op->rename(old_dir->d_inode, old_dentry, + new_dir->d_inode, new_dentry); exit_lock: double_unlock(new_dir, old_dir); diff -u --recursive --new-file v2.1.57/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.57/linux/fs/nfs/dir.c Wed Sep 24 20:05:48 1997 +++ linux/fs/nfs/dir.c Sun Oct 12 10:15:56 1997 @@ -29,6 +29,11 @@ #include /* for fs functions */ +/* needed by smbfs as well ... move to dcache? */ +extern void nfs_renew_times(struct dentry *); +extern void nfs_invalidate_dircache_sb(struct super_block *); +#define NFS_PARANOIA 1 + /* * Head for a dircache entry. Currently still very simple; when * the cache grows larger, we will need a LRU list. @@ -36,14 +41,14 @@ struct nfs_dirent { dev_t dev; /* device number */ ino_t ino; /* inode number */ - u32 cookie; /* cooke of first entry */ + u32 cookie; /* cookie of first entry */ unsigned short valid : 1, /* data is valid */ locked : 1; /* entry locked */ unsigned int size; /* # of entries */ unsigned long age; /* last used */ unsigned long mtime; /* last attr stamp */ struct wait_queue * wait; - struct nfs_entry * entry; + __u32 * entry; /* three __u32's per entry */ }; static int nfs_dir_open(struct inode * inode, struct file * file); @@ -123,15 +128,16 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) { + struct inode *inode = filp->f_dentry->d_inode; static struct wait_queue *readdir_wait = NULL; struct wait_queue **waitp = NULL; struct nfs_dirent *cache, *free; - struct nfs_entry *entry; unsigned long age, dead; u32 cookie; int ismydir, result; int i, j, index = 0; - struct inode *inode = filp->f_dentry->d_inode; + __u32 *entry; + char *name, *start; dfprintk(VFS, "NFS: nfs_readdir(%x/%ld)\n", inode->i_dev, inode->i_ino); if (!inode || !S_ISDIR(inode->i_mode)) { @@ -194,17 +200,15 @@ break; } for (j = 0; j < cache->size; j++) { - /* - dprintk("NFS: examing entry %.*s @%d\n", - (int) cache->entry[j].length, - cache->entry[j].name, - cache->entry[j].cookie); - */ - if (cache->entry[j].cookie != cookie) + __u32 *this_ent = cache->entry + j*3; + + if (*(this_ent+1) != cookie) continue; if (j < cache->size - 1) { - entry = cache->entry + (index = j + 1); - } else if (cache->entry[j].eof) { + index = j + 1; + entry = this_ent + 3; + } else if (*(this_ent+2) >> 16) { + /* eof */ return 0; } break; @@ -235,12 +239,10 @@ cache->dev = inode->i_dev; cache->ino = inode->i_ino; if (!cache->entry) { - cache->entry = (struct nfs_entry *) - get_free_page(GFP_KERNEL); - if (!cache->entry) { - result = -ENOMEM; + result = -ENOMEM; + cache->entry = (__u32 *) get_free_page(GFP_KERNEL); + if (!cache->entry) goto done; - } } result = nfs_proc_readdir(NFS_SERVER(inode), NFS_FH(inode), @@ -257,25 +259,29 @@ /* * Yowza! We have a cache entry... */ + start = (char *) cache->entry; while (index < cache->size) { - int nextpos = entry->cookie; + __u32 fileid = *entry++; + __u32 nextpos = *entry++; /* cookie */ + __u32 length = *entry++; /* + * Unpack the eof flag, offset, and length + */ + result = length & (1 << 15); /* eof flag */ + name = start + ((length >> 16) & 0xFFFF); + length &= 0x7FFF; + /* dprintk("NFS: filldir(%p, %.*s, %d, %d, %x, eof %x)\n", entry, - (int) entry->length, entry->name, entry->length, + (int) length, name, length, (unsigned int) filp->f_pos, - entry->fileid, entry->eof); + fileid, result); */ - if (filldir(dirent, entry->name, entry->length, cookie, entry->fileid) < 0) + if (filldir(dirent, name, length, cookie, fileid) < 0) break; cookie = nextpos; - if (nextpos != entry->cookie) { - printk("nfs_readdir: shouldn't happen!\n"); - break; - } index++; - entry++; } filp->f_pos = cookie; result = 0; @@ -293,44 +299,73 @@ } /* - * Invalidate dircache entries for inode + * Invalidate dircache entries for an inode. */ void nfs_invalidate_dircache(struct inode *inode) { - struct nfs_dirent *cache; + struct nfs_dirent *cache = dircache; dev_t dev = inode->i_dev; ino_t ino = inode->i_ino; int i; dfprintk(DIRCACHE, "NFS: invalidate dircache for %x/%ld\n", dev, (long)ino); - for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { - if (!cache->locked && cache->dev == dev && cache->ino == ino) - cache->valid = 0; /* brute force */ + for (i = NFS_MAX_DIRCACHE; i--; cache++) { + if (cache->ino != ino) + continue; + if (cache->dev != dev) + continue; + if (cache->locked) { + printk("NFS: cache locked for %s/%ld\n", + kdevname(dev), ino); + continue; + } + cache->valid = 0; /* brute force */ } } /* - * Free directory cache memory - * Called from cleanup_module + * Invalidate the dircache for a super block (or all caches), + * and release the cache memory. */ void -nfs_free_dircache(void) +nfs_invalidate_dircache_sb(struct super_block *sb) { - struct nfs_dirent *cache; + struct nfs_dirent *cache = dircache; int i; + int freed = 0; - dfprintk(DIRCACHE, "NFS: freeing dircache\n"); - for (i = 0, cache = dircache; i < NFS_MAX_DIRCACHE; i++, cache++) { - cache->valid = 0; + for (i = NFS_MAX_DIRCACHE; i--; cache++) { + if (sb && sb->s_dev != cache->dev) + continue; if (cache->locked) { - printk("nfs_kfree_cache: locked entry in dircache!\n"); + printk("NFS: cache locked at umount %s\n", + (cache->entry ? "(lost a page!)" : "")); continue; } - if (cache->entry) + cache->valid = 0; /* brute force */ + if (cache->entry) { free_page((unsigned long) cache->entry); - cache->entry = NULL; + cache->entry = NULL; + freed++; + } } +#ifdef NFS_PARANOIA +if (freed) +printk("nfs_invalidate_dircache_sb: freed %d pages from %s\n", +freed, kdevname(sb->s_dev)); +#endif +} + +/* + * Free directory cache memory + * Called from cleanup_module + */ +void +nfs_free_dircache(void) +{ + dfprintk(DIRCACHE, "NFS: freeing dircache\n"); + nfs_invalidate_dircache_sb(NULL); } /* @@ -350,20 +385,46 @@ unsigned long time = jiffies - dentry->d_time; unsigned long max = 5*HZ; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) - max = 10*HZ; - return time < max; + if (dentry->d_inode) { + if (is_bad_inode(dentry->d_inode)) { +#ifdef NFS_PARANOIA +printk("nfs_lookup_validate: %s/%s has dud inode\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + goto bad; + } + if (S_ISDIR(dentry->d_inode->i_mode)) + max = 10*HZ; + } + + return (time < max) || IS_ROOT(dentry); +bad: + return 0; } static void nfs_silly_delete(struct dentry *); static struct dentry_operations nfs_dentry_operations = { - nfs_lookup_revalidate, + nfs_lookup_revalidate, /* d_validate(struct dentry *) */ 0, /* d_hash */ 0, /* d_compare */ - nfs_silly_delete, + nfs_silly_delete, /* d_delete(struct dentry *) */ }; +/* + * Whenever a lookup succeeds, we know the parent directories + * are all valid, so we want to update the dentry timestamps. + */ +void nfs_renew_times(struct dentry * dentry) +{ + for (;;) { + dentry->d_time = jiffies; + if (dentry == dentry->d_parent) + break; + dentry = dentry->d_parent; + } +} + static int nfs_lookup(struct inode *dir, struct dentry * dentry) { struct inode *inode; @@ -383,20 +444,42 @@ if (len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, &fhandle, &fattr); - + error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir), + dentry->d_name.name, &fhandle, &fattr); inode = NULL; + if (error == -ENOENT) + goto no_entry; if (!error) { + error = -EACCES; inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (!inode) - return -EACCES; - } else if (error != -ENOENT) - return error; - - dentry->d_time = jiffies; - dentry->d_op = &nfs_dentry_operations; - d_add(dentry, inode); - return 0; + if (inode) { + no_entry: + dentry->d_op = &nfs_dentry_operations; + d_add(dentry, inode); + nfs_renew_times(dentry); + error = 0; + } + } + return error; +} + +/* + * Code common to create, mkdir, and mknod. + */ +static int nfs_instantiate(struct inode *dir, struct dentry *dentry, + struct nfs_fattr *fattr, struct nfs_fh *fhandle) +{ + struct inode *inode; + int error = -EACCES; + + inode = nfs_fhget(dir->i_sb, fhandle, fattr); + if (inode) { + nfs_invalidate_dircache(dir); + d_instantiate(dentry, inode); + nfs_renew_times(dentry); + error = 0; + } + return error; } static int nfs_create(struct inode *dir, struct dentry * dentry, int mode) @@ -404,7 +487,6 @@ struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - struct inode *inode; int error; dfprintk(VFS, "NFS: create(%x/%ld, %s\n", @@ -422,18 +504,10 @@ sattr.uid = sattr.gid = sattr.size = (unsigned) -1; sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - dentry->d_name.name, &sattr, &fhandle, &fattr); - - if (error) - return error; - - inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (!inode) - return -EACCES; - - nfs_invalidate_dircache(dir); - d_instantiate(dentry, inode); - return 0; + dentry->d_name.name, &sattr, &fhandle, &fattr); + if (!error) + error = nfs_instantiate(dir, dentry, &fattr, &fhandle); + return error; } static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev) @@ -441,7 +515,6 @@ struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - struct inode *inode; int error; dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n", @@ -462,18 +535,10 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir), - dentry->d_name.name, &sattr, &fhandle, &fattr); - - if (error) - return error; - - inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (!inode) - return -EACCES; - - nfs_invalidate_dircache(dir); - d_instantiate(dentry, inode); - return 0; + dentry->d_name.name, &sattr, &fhandle, &fattr); + if (!error) + error = nfs_instantiate(dir, dentry, &fattr, &fhandle); + return error; } static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) @@ -481,7 +546,6 @@ struct nfs_sattr sattr; struct nfs_fattr fattr; struct nfs_fh fhandle; - struct inode * inode; int error; dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n", @@ -500,20 +564,16 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir), - dentry->d_name.name, &sattr, &fhandle, &fattr); - - if (error) - return error; - - inode = nfs_fhget(dir->i_sb, &fhandle, &fattr); - if (!inode) - return -EACCES; - - nfs_invalidate_dircache(dir); - d_instantiate(dentry, inode); - return 0; + dentry->d_name.name, &sattr, &fhandle, &fattr); + if (!error) + error = nfs_instantiate(dir, dentry, &fattr, &fhandle); + return error; } +/* + * Update inode->i_nlink immediately after a successful operation. + * (See comments for nfs_unlink.) + */ static int nfs_rmdir(struct inode *dir, struct dentry *dentry) { int error; @@ -530,12 +590,14 @@ return -ENAMETOOLONG; error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); - if (error) - return error; - - nfs_invalidate_dircache(dir); - d_delete(dentry); - return 0; + if (!error) { + if (dentry->d_inode->i_nlink) + dentry->d_inode->i_nlink --; + nfs_invalidate_dircache(dir); + nfs_renew_times(dentry); + d_delete(dentry); + } + return error; } @@ -642,16 +704,15 @@ error = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name, NFS_FH(dir), silly); - if (error) { - dput(sdentry); - return error; + if (!error) { + nfs_invalidate_dircache(dir); + nfs_renew_times(dentry); + d_move(dentry, sdentry); + dentry->d_flags |= DCACHE_NFSFS_RENAMED; + /* If we return 0 we don't unlink */ } - nfs_invalidate_dircache(dir); - d_move(dentry, sdentry); dput(sdentry); - dentry->d_flags |= DCACHE_NFSFS_RENAMED; - - return 0; /* don't unlink */ + return error; } static void nfs_silly_delete(struct dentry *dentry) @@ -670,8 +731,18 @@ if (error < 0) printk("NFS " __FUNCTION__ " failed (err = %d)\n", -error); - dentry->d_inode->i_nlink --; + if (dentry->d_inode) { + if (dentry->d_inode->i_nlink) + dentry->d_inode->i_nlink --; + } else + printk("nfs_silly_delete: negative dentry %s/%s\n", + dentry->d_parent->d_name.name, + dentry->d_name.name); nfs_invalidate_dircache(dir); + /* + * The dentry is unhashed, but we want to make it negative. + */ + d_delete(dentry); } } @@ -680,8 +751,11 @@ * * If sillyrename() returns 0, we do nothing, otherwise we unlink. * - * inode->i_nlink is updated here rather than waiting for the next - * nfs_refresh_inode() for cosmetic reasons only. + * Updating inode->i_nlink here rather than waiting for the next + * nfs_refresh_inode() is not merely cosmetic; once an object has + * been deleted, we want to get rid of the inode locally. The NFS + * server may reuse the fileid for a new inode, and we don't want + * that to be confused with this inode. */ static int nfs_unlink(struct inode *dir, struct dentry *dentry) { @@ -700,20 +774,19 @@ error = nfs_sillyrename(dir, dentry); - if (error == -EBUSY) { - return -EBUSY; - } else if (error < 0) { + if (error && error != -EBUSY) { error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name); - if (error < 0) - return error; - - dentry->d_inode->i_nlink --; - nfs_invalidate_dircache(dir); - d_delete(dentry); + if (!error) { + if (dentry->d_inode->i_nlink) + dentry->d_inode->i_nlink --; + nfs_invalidate_dircache(dir); + nfs_renew_times(dentry); + d_delete(dentry); + } } - return 0; + return error; } static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) @@ -740,22 +813,21 @@ sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1; error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir), - dentry->d_name.name, symname, &sattr); - - if (error) - return error; - - nfs_invalidate_dircache(dir); - /* this looks _funny_ doesn't it? But: nfs_proc_symlink() - * only fills in sattr, not fattr. Thus nfs_fhget() cannot be - * called, it would be pointless, without a valid fattr - * argument. Other possibility: call nfs_proc_lookup() - * HERE. But why? If somebody wants to reference this - * symlink, the cached_lookup() will fail, and - * nfs_proc_symlink() will be called anyway. - */ - d_drop(dentry); - return 0; + dentry->d_name.name, symname, &sattr); + if (!error) { + nfs_invalidate_dircache(dir); + nfs_renew_times(dentry->d_parent); + /* this looks _funny_ doesn't it? But: nfs_proc_symlink() + * only fills in sattr, not fattr. Thus nfs_fhget() cannot be + * called, it would be pointless, without a valid fattr + * argument. Other possibility: call nfs_proc_lookup() + * HERE. But why? If somebody wants to reference this + * symlink, the cached_lookup() will fail, and + * nfs_proc_symlink() will be called anyway. + */ + d_drop(dentry); + } + return error; } static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry) @@ -774,17 +846,16 @@ if (dentry->d_name.len > NFS_MAXNAMLEN) return -ENAMETOOLONG; - error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), - NFS_FH(dir), dentry->d_name.name); - - if (error) - return error; - - nfs_invalidate_dircache(dir); - inode->i_count ++; - inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */ - d_instantiate(dentry, inode); - return 0; + error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir), + dentry->d_name.name); + if (!error) { + nfs_invalidate_dircache(dir); + inode->i_count ++; + inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */ + d_instantiate(dentry, inode); + error = 0; + } + return error; } /* @@ -843,15 +914,19 @@ error = nfs_proc_rename(NFS_SERVER(old_dir), NFS_FH(old_dir), old_dentry->d_name.name, NFS_FH(new_dir), new_dentry->d_name.name); - if (error) - return error; - - nfs_invalidate_dircache(old_dir); - nfs_invalidate_dircache(new_dir); + if (!error) { + nfs_invalidate_dircache(old_dir); + nfs_invalidate_dircache(new_dir); + /* + * We know these paths are still valid ... + */ + nfs_renew_times(old_dentry); + nfs_renew_times(new_dentry->d_parent); - /* Update the dcache */ - d_move(old_dentry, new_dentry); - return 0; + /* Update the dcache */ + d_move(old_dentry, new_dentry); + } + return error; } /* @@ -860,23 +935,68 @@ * of the server's inode. */ -void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) +int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr) { - int was_empty; + int error = -EIO; dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d)\n", inode->i_dev, inode->i_ino, inode->i_count); if (!inode || !fattr) { printk("nfs_refresh_inode: inode or fattr is NULL\n"); - return; + goto out; } if (inode->i_ino != fattr->fileid) { printk("nfs_refresh_inode: inode number mismatch\n"); - return; + goto out; } - was_empty = (inode->i_mode == 0); - inode->i_mode = fattr->mode; + /* + * Check whether the mode has been set, as we only want to + * do this once. (We don't allow inodes to change types.) + */ + if (inode->i_mode == 0) { + inode->i_mode = fattr->mode; + if (S_ISREG(inode->i_mode)) + inode->i_op = &nfs_file_inode_operations; + else if (S_ISDIR(inode->i_mode)) + inode->i_op = &nfs_dir_inode_operations; + else if (S_ISLNK(inode->i_mode)) + inode->i_op = &nfs_symlink_inode_operations; + else if (S_ISCHR(inode->i_mode)) { + inode->i_op = &chrdev_inode_operations; + inode->i_rdev = to_kdev_t(fattr->rdev); + } else if (S_ISBLK(inode->i_mode)) { + inode->i_op = &blkdev_inode_operations; + inode->i_rdev = to_kdev_t(fattr->rdev); + } else if (S_ISFIFO(inode->i_mode)) + init_fifo(inode); + else + inode->i_op = NULL; + } else if ((inode->i_mode & S_IFMT) == (fattr->mode & S_IFMT)) { + inode->i_mode = fattr->mode; + } else { + mode_t old_mode; + /* + * Big trouble! The inode has become a different object. + */ +#if 1 +printk("nfs_refresh_inode: mode changed, %07o to %07o\n", +inode->i_mode, fattr->mode); +#endif + old_mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = old_mode; /* restore mode */ + inode->i_nlink = 0; + /* + * No need to worry about unhashing the dentry, as the + * lookup validation will know that the inode is bad. + * (But we do want to invalidate the caches.) + */ + invalidate_inode_pages(inode); + nfs_invalidate_dircache(inode); + goto out; + } + inode->i_nlink = fattr->nlink; inode->i_uid = fattr->uid; inode->i_gid = fattr->gid; @@ -893,29 +1013,13 @@ NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode); } inode->i_size = fattr->size; - if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) - inode->i_rdev = to_kdev_t(fattr->rdev); - else - inode->i_rdev = 0; inode->i_blocks = fattr->blocks; inode->i_atime = fattr->atime.seconds; inode->i_mtime = fattr->mtime.seconds; inode->i_ctime = fattr->ctime.seconds; - if (S_ISREG(inode->i_mode)) - inode->i_op = &nfs_file_inode_operations; - else if (S_ISDIR(inode->i_mode)) - inode->i_op = &nfs_dir_inode_operations; - else if (S_ISLNK(inode->i_mode)) - inode->i_op = &nfs_symlink_inode_operations; - else if (S_ISCHR(inode->i_mode)) - inode->i_op = &chrdev_inode_operations; - else if (S_ISBLK(inode->i_mode)) - inode->i_op = &blkdev_inode_operations; - else if (S_ISFIFO(inode->i_mode)) { - if (was_empty) - init_fifo(inode); - } else - inode->i_op = NULL; + error = 0; +out: + return error; } /* diff -u --recursive --new-file v2.1.57/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.57/linux/fs/nfs/inode.c Sun Sep 7 13:10:43 1997 +++ linux/fs/nfs/inode.c Sun Oct 12 10:15:56 1997 @@ -34,6 +34,8 @@ #define NFSDBG_FACILITY NFSDBG_VFS +extern void nfs_invalidate_dircache_sb(struct super_block *); + static int nfs_notify_change(struct inode *, struct iattr *); static void nfs_put_inode(struct inode *); static void nfs_delete_inode(struct inode *); @@ -67,6 +69,7 @@ { inode->i_blksize = inode->i_sb->s_blocksize; inode->i_mode = 0; + inode->i_rdev = 0; inode->i_op = NULL; NFS_CACHEINV(inode); } @@ -75,6 +78,11 @@ nfs_put_inode(struct inode * inode) { dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino); + /* + * We want to get rid of unused inodes ... + */ + if (inode->i_count == 1) + inode->i_nlink = 0; } static void @@ -90,13 +98,21 @@ struct nfs_server *server = &sb->u.nfs_sb.s_server; struct rpc_clnt *rpc; + /* + * Lock the super block while we bring down the daemons. + */ + lock_super(sb); if ((rpc = server->client) != NULL) rpc_shutdown_client(rpc); if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ rpciod_down(); /* release rpciod */ - lock_super(sb); + /* + * Invalidate the dircache for this superblock. + */ + nfs_invalidate_dircache_sb(sb); + sb->s_dev = 0; unlock_super(sb); MOD_DEC_USE_COUNT; @@ -147,14 +163,12 @@ unsigned int authflavor; int tcp; kdev_t dev = sb->s_dev; + struct inode *root_inode; MOD_INC_USE_COUNT; - if (!data) { - printk("nfs_read_super: missing data argument\n"); - sb->s_dev = 0; - MOD_DEC_USE_COUNT; - return NULL; - } + if (!data) + goto out_miss_args; + if (data->version != NFS_MOUNT_VERSION) { printk("nfs warning: mount version %s than kernel\n", data->version < NFS_MOUNT_VERSION ? "older" : "newer"); @@ -164,13 +178,19 @@ data->bsize = 0; } + /* We now require that the mount process passes the remote address */ + memcpy(&srvaddr, &data->addr, sizeof(srvaddr)); + if (srvaddr.sin_addr.s_addr == INADDR_ANY) + goto out_no_remote; + lock_super(sb); - server = &sb->u.nfs_sb.s_server; sb->s_magic = NFS_SUPER_MAGIC; sb->s_dev = dev; sb->s_op = &nfs_sops; sb->s_blocksize = nfs_block_size(data->bsize, &sb->s_blocksize_bits); + sb->u.nfs_sb.s_root = data->root; + server = &sb->u.nfs_sb.s_server; server->rsize = nfs_block_size(data->rsize, NULL); server->wsize = nfs_block_size(data->wsize, NULL); server->flags = data->flags; @@ -179,15 +199,6 @@ server->acdirmin = data->acdirmin*HZ; server->acdirmax = data->acdirmax*HZ; strcpy(server->hostname, data->hostname); - sb->u.nfs_sb.s_root = data->root; - - /* We now require that the mount process passes the remote address */ - memcpy(&srvaddr, &data->addr, sizeof(srvaddr)); - if (srvaddr.sin_addr.s_addr == INADDR_ANY) { - printk("NFS: mount program didn't pass remote address!\n"); - MOD_DEC_USE_COUNT; - return NULL; - } /* Which protocol do we use? */ tcp = (data->flags & NFS_MOUNT_TCP); @@ -210,18 +221,13 @@ /* Now create transport and client */ xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, &srvaddr, &timeparms); - if (xprt == NULL) { - printk("NFS: cannot create RPC transport.\n"); - goto failure; - } + if (xprt == NULL) + goto out_no_xprt; clnt = rpc_create_client(xprt, server->hostname, &nfs_program, NFS_VERSION, authflavor); - if (clnt == NULL) { - printk("NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); - goto failure; - } + if (clnt == NULL) + goto out_no_client; clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; @@ -229,29 +235,67 @@ server->client = clnt; /* Fire up rpciod if not yet running */ +#ifdef RPCIOD_RESULT + if (rpciod_up()) + goto out_no_iod; +#else rpciod_up(); +#endif - /* Unlock super block and try to get root fh attributes */ + /* + * Keep the super block locked while we try to get + * the root fh attributes. + */ + root_inode = nfs_fhget(sb, &data->root, NULL); + if (!root_inode) + goto out_no_root; + sb->s_root = d_alloc_root(root_inode, NULL); + if (!sb->s_root) + goto out_no_root; + /* We're airborne */ unlock_super(sb); - sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL); - if (sb->s_root != NULL) { - /* We're airborne */ - if (!(server->flags & NFS_MOUNT_NONLM)) - lockd_up(); - return sb; - } + /* Check whether to start the lockd process */ + if (!(server->flags & NFS_MOUNT_NONLM)) + lockd_up(); + return sb; /* Yargs. It didn't work out. */ +out_no_root: printk("nfs_read_super: get root inode failed\n"); - rpc_shutdown_client(server->client); + iput(root_inode); rpciod_down(); +#ifdef RPCIOD_RESULT + goto out_shutdown; -failure: - MOD_DEC_USE_COUNT; - if (sb->s_lock) - unlock_super(sb); +out_no_iod: + printk("nfs_read_super: couldn't start rpciod!\n"); +out_shutdown: +#endif + rpc_shutdown_client(server->client); + goto out_unlock; + +out_no_client: + printk("NFS: cannot create RPC client.\n"); + xprt_destroy(xprt); + goto out_unlock; + +out_no_xprt: + printk("NFS: cannot create RPC transport.\n"); +out_unlock: + unlock_super(sb); + goto out_fail; + +out_no_remote: + printk("NFS: mount program didn't pass remote address!\n"); + goto out_fail; + +out_miss_args: + printk("nfs_read_super: missing data argument\n"); + +out_fail: sb->s_dev = 0; + MOD_DEC_USE_COUNT; return NULL; } diff -u --recursive --new-file v2.1.57/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c --- v2.1.57/linux/fs/nfs/nfs2xdr.c Wed Apr 23 19:01:26 1997 +++ linux/fs/nfs/nfs2xdr.c Sun Oct 12 10:15:56 1997 @@ -23,6 +23,7 @@ #include #define NFSDBG_FACILITY NFSDBG_XDR +/* #define NFS_PARANOIA 1 */ #define QUADLEN(len) (((len) + 3) >> 2) static int nfs_stat_to_errno(int stat); @@ -371,17 +372,18 @@ * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. * After decoding, the layout in memory looks like this: * entry1 entry2 ... entryN stringN ... string2 string1 + * Each entry consists of three __u32 values, the same space as NFS uses. * Note that the strings are not null-terminated so that the entire number * of entries returned by the server should fit into the buffer. */ static int nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) { - struct nfs_entry *entry; struct iovec *iov = req->rq_rvec; int status, nr, len; - char *string; + char *string, *start; u32 *end; + __u32 fileid, cookie, *entry; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); @@ -396,10 +398,11 @@ end = (u32 *) ((u8 *) p + iov[1].iov_len); /* Get start and end of dirent buffer */ - entry = (struct nfs_entry *) res->buffer; + entry = (__u32 *) res->buffer; + start = (char *) res->buffer; string = (char *) res->buffer + res->bufsiz; - for (nr = 0; *p++; nr++, entry++) { - entry->fileid = ntohl(*p++); + for (nr = 0; *p++; nr++) { + fileid = ntohl(*p++); len = ntohl(*p++); if ((p + QUADLEN(len) + 3) > end) { @@ -413,27 +416,36 @@ return -errno_NFSERR_IO; } string -= len; - if ((void *) (entry+1) > (void *) string) { - /* This may actually happen because an nfs_entry - * will take up more space than the XDR data. On - * 32bit machines that's due to 8byte alignment, - * on 64bit machines that's because the char * takes - * up 2 longs. - * - * THIS IS BAD! + if ((void *) (entry+3) > (void *) string) { + /* + * This error is impossible as long as the temp + * buffer is no larger than the user buffer. The + * current packing algorithm uses the same amount + * of space in the user buffer as in the XDR data, + * so it's guaranteed to fit. */ - printk(KERN_NOTICE "NFS: should not happen in %s!\n", + printk("NFS: incorrect buffer size in %s!\n", __FUNCTION__); break; } - entry->name = string; - entry->length = len; memmove(string, p, len); p += QUADLEN(len); - entry->cookie = ntohl(*p++); - entry->eof = !p[0] && p[1]; + cookie = ntohl(*p++); + /* + * To make everything fit, we encode the length, offset, + * and eof flag into 32 bits. This works for filenames + * up to 32K and PAGE_SIZE up to 64K. + */ + status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */ + *entry++ = fileid; + *entry++ = cookie; + *entry++ = ((string - start) << 16) | status | (len & 0x7FFF); } +#ifdef NFS_PARANOIA +printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n", +nr, ((char *) entry - start), (start + res->bufsiz - string)); +#endif return nr; } diff -u --recursive --new-file v2.1.57/linux/fs/nfs/nfs3xdr.c linux/fs/nfs/nfs3xdr.c --- v2.1.57/linux/fs/nfs/nfs3xdr.c Mon Apr 7 11:35:30 1997 +++ linux/fs/nfs/nfs3xdr.c Sun Oct 12 10:15:56 1997 @@ -384,17 +384,18 @@ * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name. * After decoding, the layout in memory looks like this: * entry1 entry2 ... entryN stringN ... string2 string1 + * Each entry consists of three __u32 values, the same space as NFS uses. * Note that the strings are not null-terminated so that the entire number * of entries returned by the server should fit into the buffer. */ static int nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res) { - struct nfs_entry *entry; struct iovec *iov = req->rq_rvec; int status, nr, len; - char *string; + char *string, *start; u32 *end; + __u32 fileid, cookie, *entry; if ((status = ntohl(*p++))) return -nfs_stat_to_errno(status); @@ -413,10 +414,11 @@ return -errno_NFSERR_IO; } - string = (char *) res->buffer + res->bufsiz; - entry = (struct nfs_entry *) res->buffer; - for (nr = 0; *p++; nr++, entry++) { - entry->fileid = ntohl(*p++); + entry = (__u32 *) res->buffer; + start = (char *) res->buffer; + string = start + res->bufsiz; + for (nr = 0; *p++; nr++) { + fileid = ntohl(*p++); len = ntohl(*p++); if ((p + QUADLEN(len) + 3) > end) { @@ -430,22 +432,40 @@ return -errno_NFSERR_IO; } string -= len; - if ((void *) (entry+1) > (void *) string) { - dprintk("NFS: shouldnothappen in readdirres_decode!\n"); - break; /* should not happen */ + if ((void *) (entry+3) > (void *) string) { + /* + * This error is impossible as long as the temp + * buffer is no larger than the user buffer. The + * current packing algorithm uses the same amount + * of space in the user buffer as in the XDR data, + * so it's guaranteed to fit. + */ + printk("NFS: incorrect buffer size in %s!\n", + __FUNCTION__); + break; } - entry->name = string; - entry->length = len; memmove(string, p, len); p += QUADLEN(len); - entry->cookie = ntohl(*p++); - entry->eof = !p[0] && p[1]; + cookie = ntohl(*p++); + /* + * To make everything fit, we encode the length, offset, + * and eof flag into 32 bits. This works for filenames + * up to 32K and PAGE_SIZE up to 64K. + */ + status = !p[0] && p[1] ? (1 << 15) : 0; /* eof flag */ + *entry++ = fileid; + *entry++ = cookie; + *entry++ = ((string - start) << 16) | status | (len & 0x7FFF); /* dprintk("NFS: decoded dirent %.*s cookie %d eof %d\n", - len, string, entry->cookie, entry->eof); + len, string, cookie, status); */ } +#ifdef NFS_PARANOIA +printk("nfs_xdr_readdirres: %d entries, ent sp=%d, str sp=%d\n", +nr, ((char *) entry - start), (start + res->bufsiz - string)); +#endif return nr; } diff -u --recursive --new-file v2.1.57/linux/fs/nfs/proc.c linux/fs/nfs/proc.c --- v2.1.57/linux/fs/nfs/proc.c Mon Jun 16 16:35:58 1997 +++ linux/fs/nfs/proc.c Sun Oct 12 10:15:56 1997 @@ -250,25 +250,43 @@ */ int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, - u32 cookie, unsigned int size, struct nfs_entry *entry) + u32 cookie, unsigned int size, __u32 *entry) { struct nfs_readdirargs arg; struct nfs_readdirres res; void * buffer; + unsigned int buf_size = PAGE_SIZE; int status; /* First get a temp buffer for the readdir reply */ - while (!(buffer = (void *) get_free_page(GFP_USER))) { - need_resched = 1; - schedule(); - if (signalled()) - return -ERESTARTSYS; - } + /* N.B. does this really need to be cleared? */ + status = -ENOMEM; + buffer = (void *) get_free_page(GFP_KERNEL); + if (!buffer) + goto out; + + /* + * Calculate the effective size the buffer. To make sure + * that the returned data will fit into the user's buffer, + * we decrease the buffer size as necessary. + * + * Note: NFS returns three __u32 values for each entry, + * and we assume that the data is packed into the user + * buffer with the same efficiency. + */ + if (size < buf_size) + buf_size = size; + if (server->rsize < buf_size) + buf_size = server->rsize; +#if 0 +printk("nfs_proc_readdir: user size=%d, rsize=%d, buf_size=%d\n", +size, server->rsize, buf_size); +#endif arg.fh = fhandle; arg.cookie = cookie; arg.buffer = buffer; - arg.bufsiz = server->rsize < PAGE_SIZE? server->rsize : PAGE_SIZE; + arg.bufsiz = buf_size; res.buffer = entry; res.bufsiz = size; @@ -276,6 +294,7 @@ status = rpc_call(server->client, NFSPROC_READDIR, &arg, &res, 0); dprintk("NFS reply readdir: %d\n", status); free_page((unsigned long) buffer); +out: return status; } diff -u --recursive --new-file v2.1.57/linux/fs/proc/array.c linux/fs/proc/array.c --- v2.1.57/linux/fs/proc/array.c Wed Sep 24 20:05:48 1997 +++ linux/fs/proc/array.c Sun Oct 12 10:16:38 1997 @@ -348,6 +348,12 @@ if (!p || !p->mm || ptr >= TASK_SIZE) return 0; + /* Check for NULL pgd .. shouldn't happen! */ + if (!p->mm->pgd) { + printk("get_phys_addr: pid %d has NULL pgd!\n", p->pid); + return 0; + } + page_dir = pgd_offset(p->mm,ptr); if (pgd_none(*page_dir)) return 0; @@ -917,24 +923,34 @@ #define MAPS_LINE_MAX MAPS_LINE_MAX8 -static long read_maps (int pid, struct file * file, - char * buf, unsigned long count) +static long read_maps (int pid, struct file * file, char * buf, + unsigned long count) { - struct task_struct *p = find_task_by_pid(pid); - char * destptr; + struct task_struct *p; + struct vm_area_struct * map, * next; + char * destptr = buf, * buffer; loff_t lineno; - int column; - struct vm_area_struct * map; - int i; - char * buffer; + int column, i, volatile_task; + long retval; + /* + * We might sleep getting the page, so get it first. + */ + retval = -ENOMEM; + buffer = (char*)__get_free_page(GFP_KERNEL); + if (!buffer) + goto out; + + retval = -EINVAL; + p = find_task_by_pid(pid); if (!p) - return -EINVAL; + goto freepage_out; if (!p->mm || p->mm == &init_mm || count == 0) - return 0; + goto getlen_out; - buffer = (char*)__get_free_page(GFP_KERNEL); + /* Check whether the mmaps could change if we sleep */ + volatile_task = (p != current || p->mm->count > 1); /* decode f_pos */ lineno = file->f_pos >> MAPS_LINE_SHIFT; @@ -944,9 +960,7 @@ for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) continue; - destptr = buf; - - for ( ; map ; ) { + for ( ; map ; map = next ) { /* produce the next line */ char *line; char str[5], *cp = str; @@ -957,6 +971,10 @@ MAPS_LINE_MAX4 : MAPS_LINE_MAX8; int len; + /* + * Get the next vma now (but it won't be used if we sleep). + */ + next = map->vm_next; flags = map->vm_flags; *cp++ = flags & VM_READ ? 'r' : '-'; @@ -993,20 +1011,19 @@ if (column >= len) { column = 0; /* continue with next line at column 0 */ lineno++; - map = map->vm_next; - continue; + continue; /* we haven't slept */ } i = len-column; if (i > count) i = count; - copy_to_user(destptr, line+column, i); - destptr += i; count -= i; - column += i; + copy_to_user(destptr, line+column, i); /* may have slept */ + destptr += i; + count -= i; + column += i; if (column >= len) { column = 0; /* next time: next line at column 0 */ lineno++; - map = map->vm_next; } /* done? */ @@ -1016,15 +1033,20 @@ /* By writing to user space, we might have slept. * Stop the loop, to avoid a race condition. */ - if (p != current) + if (volatile_task) break; } /* encode f_pos */ file->f_pos = (lineno << MAPS_LINE_SHIFT) + column; +getlen_out: + retval = destptr - buf; + +freepage_out: free_page((unsigned long)buffer); - return destptr-buf; +out: + return retval; } #ifdef CONFIG_MODULES diff -u --recursive --new-file v2.1.57/linux/fs/proc/base.c linux/fs/proc/base.c --- v2.1.57/linux/fs/proc/base.c Sat Jul 5 20:53:22 1997 +++ linux/fs/proc/base.c Sun Oct 12 10:16:38 1997 @@ -50,13 +50,17 @@ NULL /* permission */ }; -static void proc_pid_fill_inode(struct inode * inode) +/* + * The fill argument is non-zero when the inode is being filled ... + * we don't need to do anything when it's being deleted. + */ +static void proc_pid_fill_inode(struct inode * inode, int fill) { struct task_struct *p; int pid = inode->i_ino >> 16; int ino = inode->i_ino & 0xffff; - if ((p = find_task_by_pid(pid)) != NULL) { + if (fill && (p = find_task_by_pid(pid)) != NULL) { if (p->dumpable || ino == PROC_PID_INO) { inode->i_uid = p->euid; inode->i_gid = p->gid; diff -u --recursive --new-file v2.1.57/linux/fs/proc/generic.c linux/fs/proc/generic.c --- v2.1.57/linux/fs/proc/generic.c Tue Sep 23 16:48:49 1997 +++ linux/fs/proc/generic.c Sun Oct 12 10:16:38 1997 @@ -16,11 +16,13 @@ #include #include +extern struct inode_operations proc_dyna_dir_inode_operations; + static long proc_file_read(struct inode * inode, struct file * file, char * buf, unsigned long nbytes); static long proc_file_write(struct inode * inode, struct file * file, const char * buffer, unsigned long count); -static long long proc_file_lseek(struct file * file, long long offset, int orig); +static long long proc_file_lseek(struct file *, long long, int); int proc_match(int len, const char *name,struct proc_dir_entry * de) { @@ -44,17 +46,14 @@ NULL /* can't fsync */ }; -/* - * proc files can do almost nothing.. - */ struct inode_operations proc_file_inode_operations = { - &proc_file_operations, /* default proc file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ + &proc_file_operations, /* default proc file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ @@ -240,57 +239,77 @@ struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode, struct proc_dir_entry *parent) { - struct proc_dir_entry *ent; - const char *fn; + struct proc_dir_entry *ent = NULL; + const char *fn = name; + int len; - if (parent) - fn = name; - else { - if (xlate_proc_name(name, &parent, &fn)) - return NULL; - } + if (!parent && xlate_proc_name(name, &parent, &fn) != 0) + goto out; + len = strlen(fn); - ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL); + ent = kmalloc(sizeof(struct proc_dir_entry) + len + 1, GFP_KERNEL); if (!ent) - return NULL; + goto out; memset(ent, 0, sizeof(struct proc_dir_entry)); + memcpy(((char *) ent) + sizeof(*ent), fn, len + 1); + ent->name = ((char *) ent) + sizeof(*ent); + ent->namelen = len; - if (mode == S_IFDIR) + if (mode == S_IFDIR) { mode |= S_IRUGO | S_IXUGO; - else if (mode == 0) - mode = S_IFREG | S_IRUGO; - - ent->name = fn; - ent->namelen = strlen(fn); - ent->mode = mode; - if (S_ISDIR(mode)) + ent->ops = &proc_dyna_dir_inode_operations; ent->nlink = 2; - else + } + else if (mode == 0) { + mode = S_IFREG | S_IRUGO; ent->nlink = 1; + } + ent->mode = mode; proc_register(parent, ent); +out: return ent; } +extern void free_proc_entry(struct proc_dir_entry *); +void free_proc_entry(struct proc_dir_entry *de) +{ + kfree(de); +} + +/* + * Remove a /proc entry and free it if it's not currently in use. + * If it is in use, we set the 'deleted' flag. + */ void remove_proc_entry(const char *name, struct proc_dir_entry *parent) { struct proc_dir_entry *de; - const char *fn; + const char *fn = name; int len; - if (parent) - fn = name; - else - if (xlate_proc_name(name, &parent, &fn)) - return; + if (!parent && xlate_proc_name(name, &parent, &fn) != 0) + goto out; len = strlen(fn); for (de = parent->subdir; de ; de = de->next) { if (proc_match(len, fn, de)) break; } - if (de) + + if (de) { +printk("remove_proc_entry: parent nlink=%d, file nlink=%d\n", +parent->nlink, de->nlink); proc_unregister(parent, de->low_ino); - kfree(de); + de->nlink = 0; + de->deleted = 1; + if (!de->count) + free_proc_entry(de); + else { + printk("remove_proc_entry: %s/%s busy, count=%d\n", + parent->name, de->name, de->count); + } + } +out: + return; } diff -u --recursive --new-file v2.1.57/linux/fs/proc/inode.c linux/fs/proc/inode.c --- v2.1.57/linux/fs/proc/inode.c Thu Jul 17 10:06:07 1997 +++ linux/fs/proc/inode.c Sun Oct 12 10:16:38 1997 @@ -17,23 +17,66 @@ #include #include +extern void free_proc_entry(struct proc_dir_entry *); + +struct proc_dir_entry * de_get(struct proc_dir_entry *de) +{ + if (de) + de->count++; + return de; +} + +/* + * Decrements the use count and checks for deferred deletion. + */ +void de_put(struct proc_dir_entry *de) +{ + if (de) { + if (!de->count) { + printk("de_put: entry %s already free!\n", de->name); + return; + } + + if (!--de->count) { + if (de->deleted) { + printk("de_put: deferred delete of %s\n", + de->name); + free_proc_entry(de); + } + } + } +} + static void proc_put_inode(struct inode *inode) { #ifdef CONFIG_SUN_OPENPROMFS_MODULE - if ((inode->i_ino >= PROC_OPENPROM_FIRST) - && (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) - && proc_openprom_use) + if ((inode->i_ino >= PROC_OPENPROM_FIRST) && + (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) && + proc_openprom_use) (*proc_openprom_use)(inode, 0); #endif + /* + * Kill off unused inodes ... VFS will unhash and + * delete the inode if we set i_nlink to zero. + */ + if (inode->i_count == 1) + inode->i_nlink = 0; } /* - * Does this ever happen? + * Decrement the use count of the proc_dir_entry. */ static void proc_delete_inode(struct inode *inode) { - printk("proc_delete_inode()?\n"); - inode->i_size = 0; + struct proc_dir_entry *de = inode->u.generic_ip; + if (de) { + /* + * Call the fill_inode hook to release module counts. + */ + if (de->fill_inode) + de->fill_inode(inode, 0); + de_put(de); + } } static void proc_put_super(struct super_block *sb) @@ -47,7 +90,7 @@ proc_read_inode, proc_write_inode, proc_put_inode, - proc_delete_inode, + proc_delete_inode, /* delete_inode(struct inode *) */ NULL, proc_put_super, NULL, @@ -85,9 +128,24 @@ return 1; } -struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de) +struct inode * proc_get_inode(struct super_block * sb, int ino, + struct proc_dir_entry * de) { - struct inode * inode = iget(s, ino); + struct inode * inode; + + /* + * Increment the use count so the dir entry can't disappear. + */ + de_get(de); +#if 1 +/* shouldn't ever happen */ +if (de && de->deleted) +printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count); +#endif + + inode = iget(sb, ino); + if (!inode) + goto out_fail; #ifdef CONFIG_SUN_OPENPROMFS_MODULE if ((inode->i_ino >= PROC_OPENPROM_FIRST) @@ -95,23 +153,29 @@ && proc_openprom_use) (*proc_openprom_use)(inode, 1); #endif - if (inode && inode->i_sb == s) { - inode->u.generic_ip = (void *) de; - if (de) { - if (de->mode) { - inode->i_mode = de->mode; - inode->i_uid = de->uid; - inode->i_gid = de->gid; - } - if (de->size) - inode->i_size = de->size; - if (de->ops) - inode->i_op = de->ops; - if (de->nlink) - inode->i_nlink = de->nlink; - if (de->fill_inode) - de->fill_inode(inode); + /* N.B. How can this test ever fail?? */ + if (inode->i_sb != sb) + printk("proc_get_inode: inode fubar\n"); + + inode->u.generic_ip = (void *) de; + if (de) { + if (de->mode) { + inode->i_mode = de->mode; + inode->i_uid = de->uid; + inode->i_gid = de->gid; } + if (de->size) + inode->i_size = de->size; + if (de->ops) + inode->i_op = de->ops; + if (de->nlink) + inode->i_nlink = de->nlink; + /* + * The fill_inode routine should use this call + * to increment module counts, if necessary. + */ + if (de->fill_inode) + de->fill_inode(inode, 1); } /* * Fixup the root inode's nlink value @@ -126,26 +190,40 @@ } read_unlock(&tasklist_lock); } +out: return inode; + +out_fail: + de_put(de); + goto out; } struct super_block *proc_read_super(struct super_block *s,void *data, int silent) { + struct inode * root_inode; + lock_super(s); s->s_blocksize = 1024; s->s_blocksize_bits = 10; s->s_magic = PROC_SUPER_MAGIC; s->s_op = &proc_sops; + root_inode = proc_get_inode(s, PROC_ROOT_INO, &proc_root); + if (!root_inode) + goto out_no_root; + s->s_root = d_alloc_root(root_inode, NULL); + if (!s->s_root) + goto out_no_root; + parse_options(data, &root_inode->i_uid, &root_inode->i_gid); unlock_super(s); - s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL); - if (!s->s_root) { - s->s_dev = 0; - printk("get root inode failed\n"); - return NULL; - } - parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid); return s; + +out_no_root: + printk("proc_read_super: get root inode failed\n"); + iput(root_inode); + s->s_dev = 0; + unlock_super(s); + return NULL; } int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) diff -u --recursive --new-file v2.1.57/linux/fs/proc/root.c linux/fs/proc/root.c --- v2.1.57/linux/fs/proc/root.c Thu Sep 11 09:02:24 1997 +++ linux/fs/proc/root.c Sun Oct 12 10:16:37 1997 @@ -25,6 +25,7 @@ static int proc_root_readdir(struct file *, void *, filldir_t); static int proc_root_lookup(struct inode *,struct dentry *); +static int proc_unlink(struct inode *, struct dentry *); static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; @@ -73,6 +74,29 @@ }; /* + * /proc dynamic directories now support unlinking + */ +struct inode_operations proc_dyna_dir_inode_operations = { + &proc_dir_operations, /* default proc dir ops */ + NULL, /* create */ + proc_lookup, /* lookup */ + NULL, /* link */ + proc_unlink, /* unlink(struct inode *, struct dentry *) */ + 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 */ +}; + +/* * The root /proc directory is special, as it has the * directories. Thus we don't use the generic * directory handling functions for that.. @@ -173,7 +197,8 @@ int proc_openprom_regdev(struct openpromfs_dev *d) { - if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD) return -1; + if (proc_openpromdev_ino == PROC_OPENPROMD_FIRST + PROC_NOPENPROMD) + return -1; d->next = proc_openprom_devices; d->inode = proc_openpromdev_ino++; proc_openprom_devices = d; @@ -218,6 +243,7 @@ (inode, filp, dirent, filldir); return -EINVAL; } +#define OPENPROM_DEFREADDIR proc_openprom_defreaddir static int proc_openprom_deflookup(struct inode * dir, struct dentry *dentry) @@ -229,17 +255,17 @@ (dir, dentry); return -ENOENT; } +#define OPENPROM_DEFLOOKUP proc_openprom_deflookup +#else +#define OPENPROM_DEFREADDIR NULL +#define OPENPROM_DEFLOOKUP NULL #endif static struct file_operations proc_openprom_operations = { NULL, /* lseek - default */ NULL, /* read - bad */ NULL, /* write - bad */ -#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD) - proc_openprom_defreaddir,/* readdir */ -#else - NULL, /* readdir */ -#endif + OPENPROM_DEFREADDIR, /* readdir */ NULL, /* poll - default */ NULL, /* ioctl - default */ NULL, /* mmap */ @@ -251,11 +277,7 @@ struct inode_operations proc_openprom_inode_operations = { &proc_openprom_operations,/* default net directory file-ops */ NULL, /* create */ -#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD) - proc_openprom_deflookup,/* lookup */ -#else - NULL, /* lookup */ -#endif + OPENPROM_DEFLOOKUP, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ @@ -639,6 +661,26 @@ } /* + * As some entries in /proc are volatile, we want to + * get rid of unused dentries. This could be made + * smarter: we could keep a "volatile" flag in the + * inode to indicate which ones to keep. + */ +static void +proc_delete_dentry(struct dentry * dentry) +{ + d_drop(dentry); +} + +static struct dentry_operations proc_dentry_operations = +{ + NULL, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + proc_delete_dentry /* d_delete(struct dentry *) */ +}; + +/* * Don't create negative dentries here, return -ENOENT by hand * instead. */ @@ -646,12 +688,15 @@ { struct inode *inode; struct proc_dir_entry * de; + int error; + error = -ENOTDIR; if (!dir || !S_ISDIR(dir->i_mode)) - return -ENOTDIR; + goto out; - de = (struct proc_dir_entry *) dir->u.generic_ip; + error = -ENOENT; inode = NULL; + de = (struct proc_dir_entry *) dir->u.generic_ip; if (de) { for (de = de->subdir; de ; de = de->next) { if (!de || !de->low_ino) @@ -660,18 +705,20 @@ continue; if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { int ino = de->low_ino | (dir->i_ino & ~(0xffff)); + error = -EINVAL; inode = proc_get_inode(dir->i_sb, ino, de); - if (!inode) - return -EINVAL; break; } } } - if (!inode) - return -ENOENT; - d_add(dentry, inode); - return 0; + if (inode) { + dentry->d_op = &proc_dentry_operations; + d_add(dentry, inode); + error = 0; + } +out: + return error; } static int proc_root_lookup(struct inode * dir, struct dentry * dentry) @@ -721,6 +768,8 @@ if (!inode) return -EINVAL; } + + dentry->d_op = &proc_dentry_operations; d_add(dentry, inode); return 0; } @@ -825,5 +874,17 @@ filp->f_pos++; } read_unlock(&tasklist_lock); + return 0; +} + +static int proc_unlink(struct inode *dir, struct dentry *dentry) +{ + struct proc_dir_entry * dp = dir->u.generic_ip; + +printk("proc_file_unlink: deleting %s/%s\n", dp->name, dentry->d_name.name); + + remove_proc_entry(dentry->d_name.name, dp); + dentry->d_inode->i_nlink = 0; + d_delete(dentry); return 0; } diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/Makefile linux/fs/smbfs/Makefile --- v2.1.57/linux/fs/smbfs/Makefile Wed Sep 3 20:52:43 1997 +++ linux/fs/smbfs/Makefile Sun Oct 12 10:17:05 1997 @@ -8,7 +8,7 @@ # Note 2! The CFLAGS definitions are now in the main makefile... O_TARGET := smbfs.o -O_OBJS := proc.o dir.o sock.o inode.o file.o ioctl.o +O_OBJS := proc.o dir.o cache.o sock.o inode.o file.o ioctl.o M_OBJS := $(O_TARGET) # If you want debugging output, please uncomment the following line diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/cache.c linux/fs/smbfs/cache.c --- v2.1.57/linux/fs/smbfs/cache.c Wed Dec 31 16:00:00 1969 +++ linux/fs/smbfs/cache.c Sun Oct 12 10:17:05 1997 @@ -0,0 +1,314 @@ +/* + * cache.c + * + * Copyright (C) 1997 by Bill Hawes + * + * Routines to support directory cacheing using the page cache. + * Right now this only works for smbfs, but will be generalized + * for use with other filesystems. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ + +static inline struct inode * +get_cache_inode(struct cache_head *cachep) +{ + return (mem_map + MAP_NR((unsigned long) cachep))->inode; +} + +/* + * Get a pointer to the cache_head structure, + * mapped as the page at offset 0. The page is + * kept locked while we're using the cache. + */ +struct cache_head * +smb_get_dircache(struct dentry * dentry) +{ + struct inode * inode = dentry->d_inode; + struct cache_head * cachep; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_get_dircache: finding cache for %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + cachep = (struct cache_head *) get_cached_page(inode, 0, 1); + if (!cachep) + goto out; + if (cachep->valid) + { + struct cache_index * index = cachep->index; + struct cache_block * block; + unsigned long offset; + int i; + + cachep->valid = 0; + /* + * Here we only want to find existing cache blocks, + * not add new ones. + */ + for (i = 0; i < cachep->pages; i++, index++) { +#ifdef SMBFS_PARANOIA +if (index->block) +printk("smb_get_dircache: cache %s/%s has existing block!\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + offset = PAGE_SIZE + (i << PAGE_SHIFT); + block = (struct cache_block *) get_cached_page(inode, + offset, 0); + if (!block) + goto out; + index->block = block; + } + cachep->valid = 1; + } +out: + return cachep; +} + +/* + * Unlock and release the data blocks. + */ +static void +smb_free_cache_blocks(struct cache_head * cachep) +{ + struct cache_index * index = cachep->index; + int i; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_free_cache_blocks: freeing %d blocks\n", cachep->pages); +#endif + for (i = 0; i < cachep->pages; i++, index++) + { + if (index->block) + { + put_cached_page((unsigned long) index->block); + index->block = NULL; + } + } +} + +/* + * Unlocks and releases the dircache. + */ +void +smb_free_dircache(struct cache_head * cachep) +{ +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_free_dircache: freeing cache\n"); +#endif + smb_free_cache_blocks(cachep); + put_cached_page((unsigned long) cachep); +} + +/* + * Initializes the dircache. We release any existing data blocks, + * and then clear the cache_head structure. + */ +void +smb_init_dircache(struct cache_head * cachep) +{ +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_init_dircache: initializing cache, %d blocks\n", cachep->pages); +#endif + smb_free_cache_blocks(cachep); + memset(cachep, 0, sizeof(struct cache_head)); +} + +/* + * Add a new entry to the cache. This assumes that the + * entries are coming in order and are added to the end. + */ +void +smb_add_to_cache(struct cache_head * cachep, struct dirent *entry, off_t fpos) +{ + struct inode * inode = get_cache_inode(cachep); + struct cache_index * index; + struct cache_block * block; + unsigned long page_off; + unsigned int nent, offset, len = entry->d_reclen; + unsigned int needed = len + sizeof(struct cache_entry); + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_add_to_cache: cache inode %p, status %d, adding %s at %ld\n", +inode, cachep->status, entry->d_name, fpos); +#endif + /* + * Don't do anything if we've had an error ... + */ + if (cachep->status) + goto out; + + index = &cachep->index[cachep->idx]; + if (!index->block) + goto get_block; + + /* space available? */ + if (needed < index->space) + { + add_entry: + nent = index->num_entries; + index->num_entries++; + index->space -= needed; + offset = index->space + + index->num_entries * sizeof(struct cache_entry); + block = index->block; + memcpy(&block->cb_data.names[offset], entry->d_name, len); + block->cb_data.table[nent].namelen = len; + block->cb_data.table[nent].offset = offset; + block->cb_data.table[nent].ino = entry->d_ino; + cachep->entries++; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_add_to_cache: added entry %s, len=%d, pos=%ld, entries=%d\n", +entry->d_name, len, fpos, cachep->entries); +#endif + return; + } + /* + * This block is full ... advance the index. + */ + cachep->idx++; + if (cachep->idx > NINDEX) /* not likely */ + goto out_full; + index++; +#ifdef SMBFS_PARANOIA +if (index->block) +printk("smb_add_to_cache: new index already has block!\n"); +#endif + + /* + * Get the next cache block + */ +get_block: + cachep->pages++; + page_off = PAGE_SIZE + (cachep->idx << PAGE_SHIFT); + block = (struct cache_block *) get_cached_page(inode, page_off, 1); + if (block) + { + index->block = block; + index->space = PAGE_SIZE; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_add_to_cache: inode=%p, pages=%d, block at %ld\n", +inode, cachep->pages, page_off); +#endif + goto add_entry; + } + /* + * On failure, just set the return status ... + */ +out_full: + cachep->status = -ENOMEM; +out: + return; +} + +int +smb_find_in_cache(struct cache_head * cachep, off_t pos, + struct cache_dirent *entry) +{ + struct cache_index * index = cachep->index; + struct cache_block * block; + unsigned int i, nent, offset = 0; + off_t next_pos = 2; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_find_in_cache: cache %p, looking for pos=%ld\n", cachep, pos); +#endif + for (i = 0; i < cachep->pages; i++, index++) + { + if (pos < next_pos) + break; + nent = pos - next_pos; + next_pos += index->num_entries; + if (pos >= next_pos) + continue; + /* + * The entry is in this block. Note: we return + * then name as a reference with _no_ null byte. + */ + block = index->block; + entry->ino = block->cb_data.table[nent].ino; + entry->len = block->cb_data.table[nent].namelen; + offset = block->cb_data.table[nent].offset; + entry->name = &block->cb_data.names[offset]; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_find_in_cache: found %s, len=%d, pos=%ld\n", +entry->name, entry->len, pos); +#endif + break; + } + return offset; +} + +int +smb_refill_dircache(struct cache_head * cachep, struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + int result; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_refill_dircache: cache %s/%s, blocks=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, cachep->pages); +#endif + /* + * Fill the cache, starting at position 2. + */ +retry: + inode->u.smbfs_i.cache_valid = 1; + result = smb_proc_readdir(dentry, 2, cachep); + if (result < 0) + { +#ifdef SMBFS_PARANOIA +printk("smb_refill_dircache: readdir failed, result=%d\n", result); +#endif + goto out; + } + + /* + * Check whether the cache was invalidated while + * we were doing the scan ... + */ + if (!inode->u.smbfs_i.cache_valid) + { +#ifdef SMBFS_PARANOIA +printk("smb_refill_dircache: cache invalidated, retrying\n"); +#endif + goto retry; + } + + result = cachep->status; + if (!result) + { + cachep->valid = 1; + } +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_refill_cache: cache %s/%s status=%d, entries=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +cachep->status, cachep->entries); +#endif + +out: + return result; +} + +void +smb_invalid_dir_cache(struct inode * dir) +{ + /* + * Get rid of any unlocked pages, and clear the + * 'valid' flag in case a scan is in progress. + */ + invalidate_inode_pages(dir); + dir->u.smbfs_i.cache_valid = 0; +} + diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c --- v2.1.57/linux/fs/smbfs/dir.c Tue Sep 23 16:48:49 1997 +++ linux/fs/smbfs/dir.c Sun Oct 12 10:17:05 1997 @@ -23,21 +23,17 @@ /* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define pr_debug printk */ -#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino)) - -static long -smb_dir_read(struct inode *inode, struct file *filp, - char *buf, unsigned long count); - -static int -smb_readdir(struct file *filp, void *dirent, filldir_t filldir); +static long smb_dir_read(struct inode *, struct file *, char *, unsigned long); +static int smb_readdir(struct file *, void *, filldir_t); +static int smb_dir_open(struct inode *, struct file *); static int smb_lookup(struct inode *, struct dentry *); static int smb_create(struct inode *, struct dentry *, int); static int smb_mkdir(struct inode *, struct dentry *, int); static int smb_rmdir(struct inode *, struct dentry *); static int smb_unlink(struct inode *, struct dentry *); -static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); +static int smb_rename(struct inode *, struct dentry *, + struct inode *, struct dentry *); static struct file_operations smb_dir_operations = { @@ -48,7 +44,7 @@ NULL, /* poll - default */ smb_ioctl, /* ioctl */ NULL, /* mmap */ - NULL, /* no special open code */ + smb_dir_open, /* open(struct inode *, struct file *) */ NULL, /* no special release code */ NULL /* fsync */ }; @@ -75,15 +71,6 @@ NULL /* smap */ }; -static void smb_put_dentry(struct dentry *); -static struct dentry_operations smbfs_dentry_operations = -{ - NULL, /* revalidate */ - NULL, /* d_hash */ - NULL, /* d_compare */ - smb_put_dentry /* d_delete */ -}; - static long smb_dir_read(struct inode *inode, struct file *filp, char *buf, unsigned long count) @@ -92,120 +79,43 @@ } /* - * This is the callback from dput(). We close the file so that - * cached dentries don't keep the file open. + * Compute the hash for a qstr. + * N.B. Move to include/linux/dcache.h? */ -void -smb_put_dentry(struct dentry *dentry) -{ - struct inode *ino = dentry->d_inode; - if (ino) - smb_close(ino); -} - -/* Static variables for the dir cache */ -static struct smb_dirent *c_entry = NULL; -static struct super_block * c_sb = NULL; -static unsigned long c_ino = 0; -static int c_seen_eof; -static int c_size; -static int c_last_returned_index; - -static struct smb_dirent * -smb_search_in_cache(struct inode *dir, unsigned long f_pos) +static unsigned int +hash_it(const char * name, unsigned int len) { - int i; - - if (this_dir_cached(dir)) - for (i = 0; i < c_size; i++) - { - if (c_entry[i].f_pos < f_pos) - continue; - if (c_entry[i].f_pos == f_pos) - { - c_last_returned_index = i; - return &(c_entry[i]); - } - break; - } - return NULL; -} - -/* - * Compute the hash for a qstr ... move to include/linux/dcache.h? - */ -static unsigned int hash_it(const char * name, unsigned int len) -{ - unsigned long hash; - hash = init_name_hash(); + unsigned long hash = init_name_hash(); while (len--) hash = partial_name_hash(*name++, hash); return end_name_hash(hash); } -static struct semaphore refill_cache_sem = MUTEX; /* - * Called with the refill semaphore held. + * If a dentry already exists, we have to give the cache entry + * the correct inode number. This is needed for getcwd(). */ -static int -smb_refill_dir_cache(struct dentry *dentry, unsigned long f_pos) +static unsigned long +smb_find_ino(struct dentry *dentry, struct cache_dirent *entry) { - struct inode *dir = dentry->d_inode; - ino_t ino_start; - int i, result; - - result = smb_proc_readdir(dentry, f_pos, - SMB_READDIR_CACHE_SIZE, c_entry); -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_refill_dir_cache: dir=%s/%s, pos=%lu, result=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, f_pos, result); -#endif - - if (result <= 0) + struct dentry * new_dentry; + struct qstr qname; + unsigned long ino = 0; + + qname.name = entry->name; + qname.len = entry->len; + qname.hash = hash_it(qname.name, qname.len); + new_dentry = d_lookup(dentry, &qname); + if (new_dentry) { - /* - * If an error occurred, the cache may have been partially - * filled prior to failing, so we must invalidate. - * N.B. Might not need to for 0 return ... save cache? - */ - c_sb = NULL; - c_ino = 0; - c_seen_eof = 0; - goto out; - } - - /* Suppose there are a multiple of cache entries? */ - c_seen_eof = (result < SMB_READDIR_CACHE_SIZE); - c_sb = dir->i_sb; - c_ino = dir->i_ino; - c_size = result; - c_last_returned_index = 0; /* is this used? */ - - ino_start = smb_invent_inos(c_size); - /* - * If a dentry already exists, we have to give the cache entry - * the correct inode number. This is needed for getcwd(). - */ - for (i = 0; i < c_size; i++) - { - struct dentry * new_dentry; - struct qstr qname; - - c_entry[i].attr.f_ino = ino_start++; - qname.name = c_entry[i].name; - qname.len = c_entry[i].len; - qname.hash = hash_it(qname.name, qname.len); - new_dentry = d_lookup(dentry, &qname); - if (new_dentry) - { - struct inode * inode = new_dentry->d_inode; - if (inode) - c_entry[i].attr.f_ino = inode->i_ino; - dput(new_dentry); - } + struct inode * inode = new_dentry->d_inode; + if (inode) + ino = inode->i_ino; + dput(new_dentry); } -out: - return result; + if (!ino) + ino = smb_invent_inos(1); + return ino; } static int @@ -213,150 +123,195 @@ { struct dentry *dentry = filp->f_dentry; struct inode *dir = dentry->d_inode; - struct smb_dirent *entry; + struct cache_head *cachep; int result; pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos); pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n", dir->i_ino, c_ino); - - result = -EBADF; - if ((dir == NULL) || !S_ISDIR(dir->i_mode)) + /* + * Make sure our inode is up-to-date. + */ + result = smb_revalidate_inode(dir); + if (result) + goto out; + /* + * Get the cache pointer ... + */ + cachep = smb_get_dircache(dentry); + if (!cachep) goto out; - /* - * Check whether the directory cache exists yet + * Make sure the cache is up-to-date. */ - if (c_entry == NULL) + if (!cachep->valid) { - int size = sizeof(struct smb_dirent) * SMB_READDIR_CACHE_SIZE; - result = -ENOMEM; - entry = (struct smb_dirent *) smb_vmalloc(size); - /* - * Somebody else may have allocated the cache, - * so we check again to avoid a memory leak. - */ - if (!c_entry) - { - if (!entry) - goto out; - c_entry = entry; - } else if (entry) { - printk("smb_readdir: cache already alloced!\n"); - smb_vfree(entry); - } + result = smb_refill_dircache(cachep, dentry); + if (result) + goto up_and_out; } - result = 0; switch ((unsigned int) filp->f_pos) { case 0: if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0) - goto out; + goto up_and_out; filp->f_pos = 1; case 1: if (filldir(dirent, "..", 2, 1, dentry->d_parent->d_inode->i_ino) < 0) - goto out; + goto up_and_out; filp->f_pos = 2; } - /* - * Since filldir() could block if dirent is paged out, - * we hold the refill semaphore while using the cache. - * N.B. It's possible that the directory could change - * between calls to readdir ... what to do?? - */ - down(&refill_cache_sem); - entry = smb_search_in_cache(dir, filp->f_pos); - if (entry == NULL) - { - /* Past the end of _this_ directory? */ - if (this_dir_cached(dir) && c_seen_eof && - filp->f_pos == c_entry[c_size-1].f_pos + 1) + while (1) + { + struct cache_dirent this_dirent, *entry = &this_dirent; + + if (!smb_find_in_cache(cachep, filp->f_pos, entry)) + break; + /* + * Check whether to look up the inode number. + */ + if (!entry->ino) { -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_readdir: eof reached for %s/%s, c_size=%d, pos=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, c_size, (int) filp->f_pos); -#endif - goto up_and_out; + entry->ino = smb_find_ino(dentry, entry); } - result = smb_refill_dir_cache(dentry, filp->f_pos); - if (result <= 0) - goto up_and_out; - entry = c_entry; - } - - while (entry < &(c_entry[c_size])) - { - pr_debug("smb_readdir: entry->name = %s\n", entry->name); if (filldir(dirent, entry->name, entry->len, - entry->f_pos, entry->attr.f_ino) < 0) + filp->f_pos, entry->ino) < 0) break; -#if SMBFS_PARANOIA -/* should never happen */ -if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos)) -printk("smb_readdir: cache changed!\n"); -#endif filp->f_pos += 1; - entry += 1; } result = 0; + /* + * Release the dircache. + */ up_and_out: - up(&refill_cache_sem); + smb_free_dircache(cachep); out: return result; } -void -smb_init_dir_cache(void) +static int +smb_dir_open(struct inode *dir, struct file *file) { - c_entry = NULL; - c_sb = NULL; - c_ino = 0; - c_seen_eof = 0; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_dir_open: (%s/%s)\n", file->f_dentry->d_parent->d_name.name, +file->f_dentry->d_name.name); +#endif + return smb_revalidate_inode(dir); } -void -smb_invalid_dir_cache(struct inode * dir) +/* + * Dentry operations routines + */ +static int smb_lookup_validate(struct dentry *); +static void smb_delete_dentry(struct dentry *); + +static struct dentry_operations smbfs_dentry_operations = +{ + smb_lookup_validate, /* d_validate(struct dentry *) */ + NULL, /* d_hash */ + NULL, /* d_compare */ + smb_delete_dentry /* d_delete(struct dentry *) */ +}; + +/* + * This is the callback when the dcache has a lookup hit. + */ +static int smb_lookup_validate(struct dentry * dentry) { - if (this_dir_cached(dir)) + struct inode * inode = dentry->d_inode; + unsigned long age = jiffies - dentry->d_time; + int valid; + + /* + * The default validation is based on dentry age: + * we believe in dentries for 5 seconds. (But each + * successful server lookup renews the timestamp.) + */ + valid = age < 5 * HZ || IS_ROOT(dentry); +#ifdef SMBFS_DEBUG_VERBOSE +if (!valid) +printk("smb_lookup_validate: %s/%s not valid, age=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, age) +#endif + + if (inode) { - c_sb = NULL; - c_ino = 0; - c_seen_eof = 0; + if (is_bad_inode(inode)) + { +#ifdef SMBFS_PARANOIA +printk("smb_lookup_validate: %s/%s has dud inode\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + valid = 0; + } + } else + { + /* + * What should we do for negative dentries? + */ } + return valid; } -void -smb_free_dir_cache(void) +/* + * This is the callback from dput() when d_count is going to 0. + * We use this to close files and unhash dentries with bad inodes. + */ +static void smb_delete_dentry(struct dentry * dentry) { - if (c_entry != NULL) + if (dentry->d_inode) + { + if (is_bad_inode(dentry->d_inode)) + { +#ifdef SMBFS_PARANOIA +printk("smb_delete_dentry: bad inode, unhashing %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif + d_drop(dentry); + } + smb_close_dentry(dentry); + } else { - /* N.B. can this block?? */ - smb_vfree(c_entry); + /* N.B. Unhash negative dentries? */ + } +} + +/* + * Whenever a lookup succeeds, we know the parent directories + * are all valid, so we want to update the dentry timestamps. + * N.B. Move this to dcache? + */ +void smb_renew_times(struct dentry * dentry) +{ + for (;;) { + dentry->d_time = jiffies; + if (dentry == dentry->d_parent) + break; + dentry = dentry->d_parent; } - c_entry = NULL; } static int -smb_lookup(struct inode *dir, struct dentry *d_entry) +smb_lookup(struct inode *dir, struct dentry *dentry) { struct smb_fattr finfo; struct inode *inode; int error; error = -ENAMETOOLONG; - if (d_entry->d_name.len > SMB_MAXNAMELEN) + if (dentry->d_name.len > SMB_MAXNAMELEN) goto out; - error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo); -#if SMBFS_PARANOIA + error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &finfo); +#ifdef SMBFS_PARANOIA if (error && error != -ENOENT) printk("smb_lookup: find %s/%s failed, error=%d\n", -d_entry->d_parent->d_name.name, d_entry->d_name.name, error); +dentry->d_parent->d_name.name, dentry->d_name.name, error); #endif inode = NULL; @@ -370,10 +325,11 @@ if (inode) { /* cache the dentry pointer */ - inode->u.smbfs_i.dentry = d_entry; + inode->u.smbfs_i.dentry = dentry; add_entry: - d_entry->d_op = &smbfs_dentry_operations; - d_add(d_entry, inode); + dentry->d_op = &smbfs_dentry_operations; + d_add(dentry, inode); + smb_renew_times(dentry); error = 0; } } @@ -385,25 +341,24 @@ * This code is common to all routines creating a new inode. */ static int -smb_instantiate(struct inode *dir, struct dentry *dentry) +smb_instantiate(struct dentry *dentry) { struct smb_fattr fattr; int error; - smb_invalid_dir_cache(dir); - error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); if (!error) { struct inode *inode; error = -EACCES; fattr.f_ino = smb_invent_inos(1); - inode = smb_iget(dir->i_sb, &fattr); + inode = smb_iget(dentry->d_sb, &fattr); if (inode) { /* cache the dentry pointer */ inode->u.smbfs_i.dentry = dentry; d_instantiate(dentry, inode); + smb_renew_times(dentry); error = 0; } } @@ -424,11 +379,12 @@ * state. Currently we close it directly again, although this * is not necessary anymore. */ + smb_invalid_dir_cache(dir); error = smb_proc_create(dentry->d_parent, &(dentry->d_name), 0, CURRENT_TIME); if (!error) { - error = smb_instantiate(dir, dentry); + error = smb_instantiate(dentry); } out: return error; @@ -444,10 +400,11 @@ if (dentry->d_name.len > SMB_MAXNAMELEN) goto out; + smb_invalid_dir_cache(dir); error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name)); if (!error) { - error = smb_instantiate(dir, dentry); + error = smb_instantiate(dentry); } out: return error; @@ -459,7 +416,7 @@ int error; error = -ENAMETOOLONG; - if (dentry->d_name.len > NFS_MAXNAMLEN) + if (dentry->d_name.len > SMB_MAXNAMELEN) goto out; /* @@ -468,11 +425,12 @@ */ if (dentry->d_inode) smb_close(dentry->d_inode); - smb_invalid_dir_cache(dir); + smb_invalid_dir_cache(dir); error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name)); if (!error) { + smb_renew_times(dentry); d_delete(dentry); } out: @@ -494,11 +452,12 @@ */ if (dentry->d_inode) smb_close(dentry->d_inode); - smb_invalid_dir_cache(dir); + smb_invalid_dir_cache(dir); error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name)); if (!error) { + smb_renew_times(dentry); d_delete(dentry); } out: @@ -511,19 +470,6 @@ { int error; - error = -ENOTDIR; - if (!old_dir || !S_ISDIR(old_dir->i_mode)) - { - printk("smb_rename: old inode is NULL or not a directory\n"); - goto out; - } - - if (!new_dir || !S_ISDIR(new_dir->i_mode)) - { - printk("smb_rename: new inode is NULL or not a directory\n"); - goto out; - } - error = -ENAMETOOLONG; if (old_dentry->d_name.len > SMB_MAXNAMELEN || new_dentry->d_name.len > SMB_MAXNAMELEN) @@ -538,10 +484,8 @@ if (new_dentry->d_inode) smb_close(new_dentry->d_inode); - /* Assume success and invalidate now */ smb_invalid_dir_cache(old_dir); smb_invalid_dir_cache(new_dir); - error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name), new_dentry->d_parent, &(new_dentry->d_name)); /* @@ -549,22 +493,24 @@ */ if (error == -EEXIST) { -#ifdef SMBFS_PARANOIA +#ifdef SMBFS_DEBUG_VERBOSE printk("smb_rename: existing file %s/%s, d_count=%d\n", new_dentry->d_parent->d_name.name, new_dentry->d_name.name, new_dentry->d_count); #endif error = smb_proc_unlink(new_dentry->d_parent, &(new_dentry->d_name)); -#ifdef SMBFS_PARANOIA +#ifdef SMBFS_DEBUG_VERBOSE printk("smb_rename: after unlink error=%d\n", error); #endif - if (error) - goto out; - d_delete(new_dentry); - - error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name), - new_dentry->d_parent, &(new_dentry->d_name)); + if (!error) + { + d_delete(new_dentry); + error = smb_proc_mv(old_dentry->d_parent, + &(old_dentry->d_name), + new_dentry->d_parent, + &(new_dentry->d_name)); + } } /* @@ -572,6 +518,8 @@ */ if (!error) { + smb_renew_times(old_dentry); + smb_renew_times(new_dentry->d_parent); d_move(old_dentry, new_dentry); } out: diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/file.c linux/fs/smbfs/file.c --- v2.1.57/linux/fs/smbfs/file.c Tue Sep 23 16:48:49 1997 +++ linux/fs/smbfs/file.c Sun Oct 12 10:17:05 1997 @@ -23,17 +23,29 @@ /* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define pr_debug printk */ +extern int smb_get_rsize(struct smb_sb_info *); +extern int smb_get_wsize(struct smb_sb_info *); + static inline int min(int a, int b) { return a < b ? a : b; } +static inline void +smb_unlock_page(struct page *page) +{ + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); +} + static int smb_fsync(struct file *file, struct dentry * dentry) { - printk("smb_fsync: sync file %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_fsync: sync file %s/%s\n", +dentry->d_parent->d_name.name, dentry->d_name.name); +#endif return 0; } @@ -43,14 +55,13 @@ static int smb_readpage_sync(struct inode *inode, struct page *page) { - unsigned long offset = page->offset; char *buffer = (char *) page_address(page); + unsigned long offset = page->offset; struct dentry * dentry = inode->u.smbfs_i.dentry; - int rsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15); - int result, refresh = 0; + int rsize = smb_get_rsize(SMB_SERVER(inode)); int count = PAGE_SIZE; + int result; - pr_debug("SMB: smb_readpage_sync(%p)\n", page); clear_bit(PG_error, &page->flags); result = -EIO; @@ -60,24 +71,22 @@ goto io_error; } +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_readpage_sync: file %s/%s, count=%d@%ld, rsize=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, count, offset, rsize); +#endif result = smb_open(dentry, O_RDONLY); if (result < 0) goto io_error; - /* Should revalidate inode ... */ do { if (count < rsize) rsize = count; -#ifdef SMBFS_DEBUG_VERBOSE -printk("smb_readpage: reading %s/%s, offset=%ld, buffer=%p, size=%d\n", -dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize); -#endif result = smb_proc_read(inode, offset, rsize, buffer); if (result < 0) goto io_error; - refresh = 1; count -= result; offset += result; buffer += result; @@ -90,10 +99,7 @@ result = 0; io_error: - if (refresh) - smb_refresh_inode(inode); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + smb_unlock_page(page); return result; } @@ -110,7 +116,7 @@ set_bit(PG_locked, &page->flags); atomic_inc(&page->count); error = smb_readpage_sync(inode, page); - __free_page(page); + free_page(page_address(page)); return error; } @@ -122,24 +128,24 @@ smb_writepage_sync(struct inode *inode, struct page *page, unsigned long offset, unsigned int count) { - int wsize = SMB_SERVER(inode)->opt.max_xmit - (SMB_HEADER_LEN+15); + u8 *buffer = (u8 *) page_address(page) + offset; + int wsize = smb_get_wsize(SMB_SERVER(inode)); int result, refresh = 0, written = 0; - u8 *buffer; - pr_debug("SMB: smb_writepage_sync(%x/%ld %d@%ld)\n", - inode->i_dev, inode->i_ino, - count, page->offset + offset); - - buffer = (u8 *) page_address(page) + offset; offset += page->offset; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_writepage_sync: file %s/%s, count=%d@%ld, wsize=%d\n", +((struct dentry *) inode->u.smbfs_i.dentry)->d_parent->d_name.name, +((struct dentry *) inode->u.smbfs_i.dentry)->d_name.name, count, offset, wsize); +#endif do { if (count < wsize) wsize = count; result = smb_proc_write(inode, offset, wsize, buffer); - - if (result < 0) { + if (result < 0) + { /* Must mark the page invalid after I/O error */ clear_bit(PG_uptodate, &page->flags); goto io_error; @@ -157,11 +163,11 @@ } while (count); io_error: +#if 0 if (refresh) smb_refresh_inode(inode); - - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); +#endif + smb_unlock_page(page); return written ? written : result; } @@ -181,7 +187,7 @@ set_bit(PG_locked, &page->flags); atomic_inc(&page->count); result = smb_writepage_sync(inode, page, 0, PAGE_SIZE); - __free_page(page); + free_page(page_address(page)); return result; } @@ -189,8 +195,8 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer, unsigned long offset, unsigned int count, int sync) { - u8 *page_addr; - int result; + unsigned long page_addr = page_address(page); + int result; pr_debug("SMB: smb_updatepage(%x/%ld %d@%ld, sync=%d)\n", inode->i_dev, inode->i_ino, @@ -203,21 +209,21 @@ set_bit(PG_locked, &page->flags); atomic_inc(&page->count); - page_addr = (u8 *) page_address(page); - - if (copy_from_user(page_addr + offset, buffer, count)) + if (copy_from_user((char *) page_addr + offset, buffer, count)) goto bad_fault; result = smb_writepage_sync(inode, page, offset, count); out: - __free_page(page); + free_page(page_addr); return result; bad_fault: - printk("smb_updatepage: fault at page=%p buffer=%p\n", page, buffer); +#ifdef SMBFS_PARANOIA +printk("smb_updatepage: fault at addr=%lu, offset=%lu, buffer=%p\n", +page_addr, offset, buffer); +#endif result = -EFAULT; clear_bit(PG_uptodate, &page->flags); - clear_bit(PG_locked, &page->flags); - wake_up(&page->wait); + smb_unlock_page(page); goto out; } @@ -227,9 +233,11 @@ { int status; - pr_debug("SMB: read(%x/%ld (%d), %lu@%lu)\n", - inode->i_dev, inode->i_ino, inode->i_count, - count, (unsigned long) file->f_pos); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_file_read: file %s/%s, count=%lu@%lu\n", +file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, +count, (unsigned long) file->f_pos); +#endif status = smb_revalidate_inode(inode); if (status >= 0) @@ -246,6 +254,11 @@ struct inode * inode = dentry->d_inode; int status; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_file_mmap: file %s/%s, address %lu - %lu\n", +file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, +vma->vm_start, vma->vm_end); +#endif status = smb_revalidate_inode(inode); if (status >= 0) { @@ -263,40 +276,67 @@ { int result; - pr_debug("SMB: write(%x/%ld (%d), %lu@%lu)\n", - inode->i_dev, inode->i_ino, inode->i_count, - count, (unsigned long) file->f_pos); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_file_write: file %s/%s, count=%lu@%lu\n", +file->f_dentry->d_parent->d_name.name, file->f_dentry->d_name.name, +count, (unsigned long) file->f_pos); +#endif +#ifdef SMBFS_PARANOIA + /* Should be impossible now that inodes can't change mode */ result = -EINVAL; - if (!inode) { - printk("smb_file_write: inode = NULL\n"); + if (!S_ISREG(inode->i_mode)) + { + printk("smb_file_write: write to non-file, mode %07o\n", + inode->i_mode); goto out; } +#endif result = smb_revalidate_inode(inode); - if (result < 0) + if (result) goto out; result = smb_open(file->f_dentry, O_WRONLY); - if (result < 0) + if (result) goto out; - result = -EINVAL; - if (!S_ISREG(inode->i_mode)) { - printk("smb_file_write: write to non-file, mode %07o\n", - inode->i_mode); - goto out; - } - - result = 0; if (count > 0) { result = generic_file_write(inode, file, buf, count); + if (result > 0) + smb_refresh_inode(inode); } out: return result; } +static int +smb_file_open(struct inode *inode, struct file * file) +{ +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_file_open: inode=%p, file=%p\n", inode, file); +#endif + return 0; +} + +static int +smb_file_release(struct inode *inode, struct file * file) +{ + struct dentry * dentry = file->f_dentry; + +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_file_release: closing file %s/%s, d_count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); +#endif + + if (dentry->d_count == 1) + { + smb_close(inode); + } + return 0; +} + static struct file_operations smb_file_operations = { NULL, /* lseek - default */ @@ -305,10 +345,14 @@ NULL, /* readdir - bad */ NULL, /* poll - default */ smb_ioctl, /* ioctl */ - smb_file_mmap, /* mmap */ - NULL, /* open */ - NULL, /* release */ - smb_fsync, /* fsync */ + smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*) */ + smb_file_open, /* open(struct inode*, struct file*) */ + smb_file_release, /* release(struct inode*, struct file*) */ + smb_fsync, /* fsync(struct file*, struct dentry*) */ + NULL, /* fasync(struct file*, int) */ + NULL, /* check_media_change(kdev_t dev) */ + NULL, /* revalidate(kdev_t dev) */ + NULL /* lock(struct file*, int, struct file_lock*) */ }; struct inode_operations smb_file_inode_operations = diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c --- v2.1.57/linux/fs/smbfs/inode.c Tue Sep 23 16:48:49 1997 +++ linux/fs/smbfs/inode.c Sun Oct 12 10:17:05 1997 @@ -6,6 +6,8 @@ * */ +#define SMBFS_DCACHE_EXT 1 + #include #include @@ -18,17 +20,20 @@ #include #include #include -#include -#include #include #include +#include #include #include -#define SB_of(server) ((struct super_block *) ((char *)(server) - \ - (unsigned long)(&((struct super_block *)0)->u.smbfs_sb))) +#define SMBFS_PARANOIA 1 +/* #define SMBFS_DEBUG_VERBOSE 1 */ +#ifndef SMBFS_DCACHE_EXT +#define shrink_dcache_sb(sb) shrink_dcache() +#endif +extern void smb_renew_times(struct dentry *); extern int close_fp(struct file *filp); static void smb_put_inode(struct inode *); @@ -67,7 +72,7 @@ return ino; } -static struct smb_fattr *read_fattr; +static struct smb_fattr *read_fattr = NULL; static struct semaphore read_semaphore = MUTEX; struct inode * @@ -80,6 +85,7 @@ down(&read_semaphore); read_fattr = fattr; result = iget(sb, fattr->f_ino); + read_fattr = NULL; up(&read_semaphore); return result; } @@ -87,7 +93,6 @@ static void smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr) { - inode->i_dev = inode->i_sb->s_dev; inode->i_mode = fattr->f_mode; inode->i_nlink = fattr->f_nlink; inode->i_uid = fattr->f_uid; @@ -99,6 +104,10 @@ inode->i_atime = fattr->f_atime; inode->i_blksize= fattr->f_blksize; inode->i_blocks = fattr->f_blocks; + /* + * Update the "last time refreshed" field for revalidation. + */ + inode->u.smbfs_i.oldmtime = jiffies; } static void @@ -106,15 +115,15 @@ { pr_debug("smb_iget: %p\n", read_fattr); - if ((atomic_read(&read_semaphore.count) == 1) || - (inode->i_ino != read_fattr->f_ino)) + if (!read_fattr || inode->i_ino != read_fattr->f_ino) { printk("smb_read_inode called from invalid point\n"); return; } - smb_set_inode_attr(inode, read_fattr); + inode->i_dev = inode->i_sb->s_dev; memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i)); + smb_set_inode_attr(inode, read_fattr); if (S_ISREG(inode->i_mode)) inode->i_op = &smb_file_inode_operations; @@ -131,59 +140,129 @@ void smb_invalidate_inodes(struct smb_sb_info *server) { - printk("smb_invalidate_inodes\n"); - shrink_dcache(); /* should shrink only this sb */ +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_invalidate_inodes\n"); +#endif + shrink_dcache_sb(SB_of(server)); invalidate_inodes(SB_of(server)); } int -smb_revalidate_inode(struct inode *ino) +smb_revalidate_inode(struct inode *inode) { - struct dentry * dentry = ino->u.smbfs_i.dentry; + time_t last_time; int error = 0; pr_debug("smb_revalidate_inode\n"); - if (!ino) - goto bad_no_inode; - dentry = ino->u.smbfs_i.dentry; -#if 0 - if (dentry) + /* + * Check whether we've recently refreshed the inode. + */ + if (jiffies < inode->u.smbfs_i.oldmtime + HZ/10) { - printk("smb_revalidate: checking %s/%s\n", - dentry->d_parent->d_name.name, dentry->d_name.name); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_revalidate_inode: up-to-date, jiffies=%lu, oldtime=%lu\n", +jiffies, inode->u.smbfs_i.oldmtime); +#endif + goto out; } + + /* + * Save the last modified time, then refresh the inode + */ + last_time = inode->i_mtime; + error = smb_refresh_inode(inode); + if (!error) + { + if (inode->i_mtime != last_time) + { +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n", +((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name, +((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name, +(long) last_time, (long) inode->i_mtime); #endif + if (!S_ISDIR(inode->i_mode)) + invalidate_inode_pages(inode); + else + smb_invalid_dir_cache(inode); + } + } out: return error; - -bad_no_inode: - printk("smb_revalidate: no inode!\n"); - error = -EINVAL; - goto out; } +/* + * This is called to update the inode attributes after + * we've made changes to a file or directory. + */ int -smb_refresh_inode(struct inode *ino) +smb_refresh_inode(struct inode *inode) { - struct dentry * dentry = ino->u.smbfs_i.dentry; + struct dentry * dentry = inode->u.smbfs_i.dentry; struct smb_fattr fattr; int error; pr_debug("smb_refresh_inode\n"); - error = -EIO; - if (!dentry) { + if (!dentry) + { printk("smb_refresh_inode: no dentry, can't refresh\n"); + error = -EIO; goto out; } - /* N.B. Should check for changes of important fields! cf. NFS */ + /* + * Kludge alert ... for some reason we can't get attributes + * for the root directory, so just return success. + */ + error = 0; + if (IS_ROOT(dentry)) + goto out; + error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr); if (!error) { - smb_set_inode_attr(ino, &fattr); + smb_renew_times(dentry); + /* + * Check whether the type part of the mode changed, + * and don't update the attributes if it did. + */ + if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT)) + smb_set_inode_attr(inode, &fattr); + else + { + /* + * Big trouble! The inode has become a new object, + * so any operations attempted on it are invalid. + * + * Take a couple of steps to limit damage: + * (1) Mark the inode as bad so that subsequent + * lookup validations will fail. + * (2) Clear i_nlink so the inode will be released + * at iput() time. (Unhash it as well?) + * We also invalidate the caches for good measure. + */ +#ifdef SMBFS_PARANOIA +printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +inode->i_mode, fattr.f_mode); +#endif + fattr.f_mode = inode->i_mode; /* save mode */ + make_bad_inode(inode); + inode->i_mode = fattr.f_mode; /* restore mode */ + inode->i_nlink = 0; + /* + * No need to worry about unhashing the dentry: the + * lookup validation will see that the inode is bad. + * But we may need to invalidate the caches ... + */ + invalidate_inode_pages(inode); + smb_invalid_dir_cache(inode); + error = -EIO; + } } out: return error; + } /* @@ -192,19 +271,20 @@ static void smb_put_inode(struct inode *ino) { - struct dentry * dentry; pr_debug("smb_put_inode: count = %d\n", ino->i_count); if (ino->i_count > 1) { + struct dentry * dentry; /* * Check whether the dentry still holds this inode. - * This looks scary, but should work ... d_inode is - * cleared before iput() in the dcache. + * This looks scary, but should work ... if this is + * the last use, d_inode == NULL or d_count == 0. */ dentry = (struct dentry *) ino->u.smbfs_i.dentry; - if (dentry && dentry->d_inode != ino) { + if (dentry && (dentry->d_inode != ino || dentry->d_count == 0)) + { ino->u.smbfs_i.dentry = NULL; -#if 1 +#ifdef SMBFS_DEBUG_VERBOSE printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count); #endif @@ -263,7 +343,6 @@ struct smb_mount_data *data = (struct smb_mount_data *)raw_data; struct smb_fattr root; kdev_t dev = sb->s_dev; - unsigned char *packet; struct inode *root_inode; struct dentry *dentry; @@ -275,62 +354,65 @@ if (data->version != SMB_MOUNT_VERSION) goto out_wrong_data; - packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE); - if (!packet) - goto out_no_mem; - lock_super(sb); sb->s_blocksize = 1024; /* Eh... Is this correct? */ sb->s_blocksize_bits = 10; sb->s_magic = SMB_SUPER_MAGIC; - sb->s_dev = dev; + sb->s_dev = dev; /* shouldn't need this ... */ sb->s_op = &smb_sops; sb->u.smbfs_sb.sock_file = NULL; sb->u.smbfs_sb.sem = MUTEX; + sb->u.smbfs_sb.wait = NULL; sb->u.smbfs_sb.conn_pid = 0; - sb->u.smbfs_sb.packet = packet; - sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE; + sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */ sb->u.smbfs_sb.generation = 0; + sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE; + sb->u.smbfs_sb.packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE); + if (!sb->u.smbfs_sb.packet) + goto out_no_mem; sb->u.smbfs_sb.m = *data; sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG; sb->u.smbfs_sb.m.dir_mode = (sb->u.smbfs_sb.m.dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR; - + /* + * Keep the super block locked while we get the root inode. + */ smb_init_root_dirent(&(sb->u.smbfs_sb), &root); - - sb->s_root = NULL; - unlock_super(sb); - root_inode = smb_iget(sb, &root); if (!root_inode) goto out_no_root; + dentry = d_alloc_root(root_inode, NULL); if (!dentry) goto out_no_root; root_inode->u.smbfs_i.dentry = dentry; sb->s_root = dentry; + + unlock_super(sb); return sb; -out_no_data: - printk("smb_read_super: missing data argument\n"); - goto out; -out_wrong_data: - printk(KERN_ERR "smb_read_super: wrong data argument." - " Recompile smbmount.\n"); - goto out; -out_no_mem: - pr_debug("smb_read_super: could not alloc packet\n"); - goto out; out_no_root: - sb->s_dev = 0; /* iput() might block */ printk(KERN_ERR "smb_read_super: get root inode failed\n"); iput(root_inode); - smb_vfree(packet); -out: + smb_vfree(sb->u.smbfs_sb.packet); + goto out_unlock; +out_no_mem: + printk("smb_read_super: could not alloc packet\n"); + goto out_unlock; +out_wrong_data: + printk(KERN_ERR "smb_read_super: wrong data argument." + " Recompile smbmount.\n"); + goto out_fail; +out_no_data: + printk("smb_read_super: missing data argument\n"); + goto out_fail; +out_unlock: + unlock_super(sb); +out_fail: sb->s_dev = 0; MOD_DEC_USE_COUNT; return NULL; @@ -355,8 +437,9 @@ int smb_notify_change(struct inode *inode, struct iattr *attr) { + struct smb_sb_info *server = SMB_SERVER(inode); struct dentry *dentry = inode->u.smbfs_i.dentry; - int error; + int error, refresh = 0; error = -EIO; if (!dentry) @@ -365,40 +448,54 @@ goto out; } + /* + * Make sure our inode is up-to-date ... + */ + error = smb_revalidate_inode(inode); + if (error) + goto out; + if ((error = inode_change_ok(inode, attr)) < 0) goto out; error = -EPERM; - if (((attr->ia_valid & ATTR_UID) && - (attr->ia_uid != SMB_SERVER(inode)->m.uid))) + if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid))) goto out; - if (((attr->ia_valid & ATTR_GID) && - (attr->ia_uid != SMB_SERVER(inode)->m.gid))) + if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid))) goto out; if (((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO)))) goto out; - /* - * Assume success and invalidate the parent's dir cache - */ - smb_invalid_dir_cache(dentry->d_parent->d_inode); - if ((attr->ia_valid & ATTR_SIZE) != 0) { - if ((error = smb_open(dentry, O_WRONLY)) < 0) + error = smb_open(dentry, O_WRONLY); + if (error) goto out; - - if ((error = smb_proc_trunc(SMB_SERVER(inode), - inode->u.smbfs_i.fileid, - attr->ia_size)) < 0) +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_notify_change: changing %s/%s, old size=%ld, new size=%ld\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +(long) inode->i_size, (long) attr->ia_size); +#endif + error = smb_proc_trunc(server, inode->u.smbfs_i.fileid, + attr->ia_size); + if (error) goto out; + /* + * We don't implement an i_op->truncate operation, + * so we have to update the page cache here. + */ + if (attr->ia_size < inode->i_size) { + truncate_inode_pages(inode, attr->ia_size); + inode->i_size = attr->ia_size; + } + refresh = 1; } + if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0) { - struct smb_fattr fattr; fattr.attr = 0; @@ -417,15 +514,25 @@ if ((attr->ia_valid & ATTR_ATIME) != 0) fattr.f_atime = attr->ia_atime; - error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr); - if (error >= 0) - { - inode->i_ctime = fattr.f_ctime; - inode->i_mtime = fattr.f_mtime; - inode->i_atime = fattr.f_atime; - } + error = smb_proc_setattr(server, dentry, &fattr); + if (error) + goto out; + refresh = 1; } + error = 0; + out: + if (refresh) + { + /* + * N.B. Currently we're only using the dir cache for + * file names, so we don't need to invalidate here. + */ +#if 0 + smb_invalid_dir_cache(dentry->d_parent->d_inode); +#endif + smb_refresh_inode(inode); + } return error; } @@ -461,7 +568,6 @@ smb_current_vmalloced = 0; #endif - smb_init_dir_cache(); read_semaphore = MUTEX; return init_smb_fs(); @@ -471,7 +577,6 @@ cleanup_module(void) { pr_debug("smbfs: cleanup_module called\n"); - smb_free_dir_cache(); unregister_filesystem(&smb_fs_type); #ifdef DEBUG_SMB_MALLOC printk(KERN_DEBUG "smb_malloced: %d\n", smb_malloced); diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.1.57/linux/fs/smbfs/proc.c Tue Sep 23 16:48:49 1997 +++ linux/fs/smbfs/proc.c Sun Oct 12 10:17:05 1997 @@ -5,6 +5,8 @@ * Copyright (C) 1997 by Volker Lendecke * * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per + * 28/09/97 - Fixed smb_d_path [now smb_build_path()] to be non-recursive + * by Riccardo Facchetti */ #include @@ -16,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -33,8 +37,7 @@ /* #define SMBFS_DEBUG_VERBOSE 1 */ /* #define pr_debug printk */ -static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc); -void smb_close_socket(struct smb_sb_info *); +extern void smb_renew_times(struct dentry *); static inline int min(int a, int b) @@ -64,6 +67,17 @@ } } +static void reverse_string(char *buf, int len) { + char c; + char *end = buf+len-1; + + while(buf < end) { + c = *buf; + *(buf++) = *end; + *(end--) = c; + } +} + /*****************************************************************************/ /* */ /* Encoding/Decoding section */ @@ -85,30 +99,55 @@ } /* - * Return the server for the specified dentry - * N.B. Make this a #define in the smb header + * smb_build_path: build the path to entry and name storing it in buf. + * The path returned will have the trailing '\0'. */ -static struct smb_sb_info * server_from_dentry(struct dentry * dentry) +static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf) { - return &dentry->d_sb->u.smbfs_sb; -} + char *path = buf; -static int smb_d_path(struct dentry * entry, char * buf) -{ + if (entry == NULL) + goto test_name_and_out; + + /* + * If IS_ROOT, we have to do no walking at all. + */ if (IS_ROOT(entry)) { - *buf = '\\'; - return 1; - } else { - int len = smb_d_path(entry->d_parent, buf); - - buf += len; - if (len > 1) { - *buf++ = '\\'; - len++; - } - memcpy(buf, entry->d_name.name, entry->d_name.len); - return len + entry->d_name.len; + *(path++) = '\\'; + if (name != NULL) + goto name_and_out; + goto out; } + + /* + * Build the path string walking the tree backward from end to ROOT + * and store it in reversed order [see reverse_string()] + */ + for (;;) { + memcpy(path, entry->d_name.name, entry->d_name.len); + reverse_string(path, entry->d_name.len); + path += entry->d_name.len; + + *(path++) = '\\'; + + entry = entry->d_parent; + + if (IS_ROOT(entry)) + break; + } + + reverse_string(buf, path-buf); + +test_name_and_out: + if (name != NULL) { + *(path++) = '\\'; +name_and_out: + memcpy(path, name->name, name->len); + path += name->len; + } +out: + *(path++) = '\0'; + return (path-buf); } static char *smb_encode_path(struct smb_sb_info *server, char *buf, @@ -116,15 +155,7 @@ { char *start = buf; - if (dir != NULL) - buf += smb_d_path(dir, buf); - - if (name != NULL) { - *buf++ = '\\'; - memcpy(buf, name->name, name->len); - buf += name->len; - *buf++ = 0; - } + buf += smb_build_path(dir, name, buf); if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS) str_upper(start); @@ -249,6 +280,55 @@ (bcc == -1 || SMB_BCC(packet) >= bcc)) ? 0 : -EIO; } +/* + * Returns the maximum read or write size for the current packet size + * and max_xmit value. + * N.B. Since this value is usually computed before locking the server, + * the server's packet size must never be decreased! + */ +static int +smb_get_xmitsize(struct smb_sb_info *server, int overhead) +{ + int size = server->packet_size; + + /* + * Start with the smaller of packet size and max_xmit ... + */ + if (size > server->opt.max_xmit) + size = server->opt.max_xmit; + return size - overhead; +} + +/* + * Calculate the maximum read size + */ +int +smb_get_rsize(struct smb_sb_info *server) +{ + int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_get_rsize: packet=%d, xmit=%d, size=%d\n", +server->packet_size, server->opt.max_xmit, size); +#endif + return size; +} + +/* + * Calculate the maximum write size + */ +int +smb_get_wsize(struct smb_sb_info *server) +{ + int overhead = SMB_HEADER_LEN + 5 * sizeof(__u16) + 2 + 1 + 2; + int size = smb_get_xmitsize(server, overhead); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_get_wsize: packet=%d, xmit=%d, size=%d\n", +server->packet_size, server->opt.max_xmit, size); +#endif + return size; +} + static int smb_errno(int errcls, int error) { @@ -365,38 +445,6 @@ up(&(server->sem)); } -/* smb_request_ok: We expect the server to be locked. Then we do the - request and check the answer completely. When smb_request_ok - returns 0, you can be quite sure that everything went well. When - the answer is <=0, the returned number is a valid unix errno. */ - -static int -smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc) -{ - int result = 0; - - s->rcls = 0; - s->err = 0; - - if (smb_request(s) < 0) - { - pr_debug("smb_request failed\n"); - result = -EIO; - } else if (smb_valid_packet(s->packet) != 0) - { - pr_debug("not a valid packet!\n"); - result = -EIO; - } else if (s->rcls != 0) - { - result = -smb_errno(s->rcls, s->err); - } else if (smb_verify(s->packet, command, wct, bcc) != 0) - { - pr_debug("smb_verify failed\n"); - result = -EIO; - } - return result; -} - /* * smb_retry: This function should be called when smb_request_ok has indicated an error. If the error was indicated because the @@ -409,6 +457,8 @@ static int smb_retry(struct smb_sb_info *server) { + struct wait_queue wait = { current, NULL }; + unsigned long timeout; int result = 0; if (server->state != CONN_INVALID) @@ -423,31 +473,94 @@ goto out; } - printk("smb_retry: signalling process %d\n", server->conn_pid); kill_proc(server->conn_pid, SIGUSR1, 0); +#if 0 server->conn_pid = 0; +#endif +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_retry: signalled pid %d, waiting for new connection\n", +server->conn_pid); +#endif /* - * Block here until we get a new connection. - * N.B. This needs to be fixed ... we should wait in an - * interruptible sleep for CONN_VALID. + * Wait here for a new connection. */ - printk("smb_retry: blocking for new connection\n"); - smb_lock_server(server); + timeout = jiffies + 10*HZ; + add_wait_queue(&server->wait, &wait); + while (1) + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + HZ; + if (server->state != CONN_INVALID) + break; + if (jiffies > timeout) + { + printk("smb_retry: timed out, try again later\n"); + break; + } + if (signal_pending(current)) + { + printk("smb_retry: caught signal\n"); + break; + } + schedule(); + } + remove_wait_queue(&server->wait, &wait); + current->timeout = 0; + current->state = TASK_RUNNING; if (server->state == CONN_VALID) { - printk("smb_retry: new connection pid=%d\n", server->conn_pid); +#ifdef SMBFS_PARANOIA +printk("smb_retry: new connection pid=%d\n", server->conn_pid); +#endif result = 1; } + out: return result; } +/* smb_request_ok: We expect the server to be locked. Then we do the + request and check the answer completely. When smb_request_ok + returns 0, you can be quite sure that everything went well. When + the answer is <=0, the returned number is a valid unix errno. */ + +static int +smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc) +{ + int result = 0; + + s->rcls = 0; + s->err = 0; + + /* Make sure we have a connection */ + if (s->state != CONN_VALID && !smb_retry(s)) + { + result = -EIO; + } else if (smb_request(s) < 0) + { + pr_debug("smb_request failed\n"); + result = -EIO; + } else if (smb_valid_packet(s->packet) != 0) + { + pr_debug("not a valid packet!\n"); + result = -EIO; + } else if (s->rcls != 0) + { + result = -smb_errno(s->rcls, s->err); + } else if (smb_verify(s->packet, command, wct, bcc) != 0) + { + pr_debug("smb_verify failed\n"); + result = -EIO; + } + return result; +} + /* * This is called with the server locked after a successful smb_newconn(). * It installs the new connection pid, sets server->state to CONN_VALID, - * and unlocks the server. + * and wakes up the process waiting for the new connection. * N.B. The first call is made without locking the server -- need to fix! */ int @@ -458,21 +571,24 @@ error = -EACCES; if (!suser() && (current->uid != server->m.mounted_uid)) goto out; + if (atomic_read(&server->sem.count) == 1) + { + printk("smb_offerconn: server not locked, count=%d\n", + atomic_read(&server->sem.count)); +#if 0 + goto out; +#endif + } server->conn_pid = current->pid; server->state = CONN_VALID; - printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid); + wake_up_interruptible(&server->wait); +#ifdef SMBFS_PARANOIA +printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid); +#endif error = 0; - /* - * The initial call may be made without the server locked? - */ out: - if (atomic_read(&server->sem.count) != 1) - smb_unlock_server(server); - else - printk("smb_offerconn: server not locked, count=%d\n", - atomic_read(&server->sem.count)); return error; } @@ -488,15 +604,21 @@ error = -EBADF; if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd])) - goto out_unlock; - if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode)) - goto out_unlock; - if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode)) - goto out_unlock; + goto out; + if (!smb_valid_socket(filp->f_dentry->d_inode)) + goto out; error = -EACCES; if (!suser() && (current->uid != server->m.mounted_uid)) - goto out_unlock; + goto out; + if (atomic_read(&server->sem.count) == 1) + { + printk("smb_newconn: server not locked, count=%d\n", + atomic_read(&server->sem.count)); +#if 0 + goto out; +#endif + } /* * Make sure the old socket is closed @@ -507,23 +629,15 @@ server->sock_file = filp; smb_catch_keepalive(server); server->opt = *opt; - pr_debug("smb_newconn: protocol = %d\n", server->opt.protocol); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_newconn: protocol=%d, max_xmit=%d\n", +server->opt.protocol, server->opt.max_xmit); +#endif server->generation += 1; error = 0; out: return error; - - /* - * Unlock now if an error occurred. - */ -out_unlock: - if (atomic_read(&server->sem.count) != 1) - smb_unlock_server(server); - else - printk("smb_newconn: server not locked, count=%d\n", - atomic_read(&server->sem.count)); - goto out; } /* smb_setup_header: We completely set up the packet. You only have to @@ -536,6 +650,10 @@ __u8 *p = server->packet; __u8 *buf = server->packet; +if (xmit_len > server->packet_size) +printk("smb_setup_header: Aieee, xmit len > packet! len=%d, size=%d\n", +xmit_len, server->packet_size); + p = smb_encode_smb_length(p, xmit_len - 4); *p++ = 0xff; @@ -577,92 +695,106 @@ } /* - * We're called with the server locked, and we leave it that way. We - * try maximum permissions. + * We're called with the server locked, and we leave it that way. + * Set the permissions to be consistent with the desired access. */ static int -smb_proc_open(struct smb_sb_info *server, struct dentry *dir) +smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish) { struct inode *ino = dir->d_inode; + int mode, read_write = 0x42, read_only = 0x40; int error; char *p; + mode = read_write; +#if 0 + if (!(wish & (O_WRONLY | O_RDWR))) + mode = read_only; +#endif + retry: p = smb_setup_header(server, SMBopen, 2, 0); - WSET(server->packet, smb_vwv0, 0x42); /* read/write */ + WSET(server->packet, smb_vwv0, mode); WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); *p++ = 4; p = smb_encode_path(server, p, dir, NULL); smb_setup_bcc(server, p); - if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) + error = smb_request_ok(server, SMBopen, 7, 0); + if (error != 0) { if (smb_retry(server)) goto retry; - if ((error != -EACCES) && (error != -ETXTBSY) - && (error != -EROFS)) - goto out; - - p = smb_setup_header(server, SMBopen, 2, 0); - WSET(server->packet, smb_vwv0, 0x40); /* read only */ - WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR); - *p++ = 4; - p = smb_encode_path(server, p, dir, NULL); - smb_setup_bcc(server, p); - - if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) + if (mode == read_write && + (error == -EACCES || error == -ETXTBSY || error == -EROFS)) { - if (smb_retry(server)) - goto retry; - goto out; +#ifdef SMBFS_PARANOIA +printk("smb_proc_open: %s/%s open failed, error=%d, retrying R/O\n", +dir->d_parent->d_name.name, dir->d_name.name, error); +#endif + mode = read_only; + goto retry; } } /* We should now have data in vwv[0..6]. */ ino->u.smbfs_i.fileid = WVAL(server->packet, smb_vwv0); ino->u.smbfs_i.attr = WVAL(server->packet, smb_vwv1); + /* smb_vwv2 has mtime */ + /* smb_vwv4 has size */ ino->u.smbfs_i.access = WVAL(server->packet, smb_vwv6); ino->u.smbfs_i.access &= 3; + /* N.B. Suppose the open failed?? */ ino->u.smbfs_i.open = server->generation; - pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access); -out: +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_open: error=%d, access=%d\n", error, ino->u.smbfs_i.access); +#endif return error; } int -smb_open(struct dentry *dir, int wish) +smb_open(struct dentry *dentry, int wish) { - struct inode *i = dir->d_inode; + struct inode *i = dentry->d_inode; int result; - result = -EIO; + result = -ENOENT; if (!i) { printk("smb_open: no inode for dentry %s/%s\n", - dir->d_parent->d_name.name, dir->d_name.name); + dentry->d_parent->d_name.name, dentry->d_name.name); goto out; } /* - * If the inode is already open, we don't need to lock the server. + * Note: If the caller holds an active dentry and the file is + * currently open, we can be sure that the file isn't about + * to be closed. (See smb_close_dentry() below.) */ if (!smb_is_open(i)) { struct smb_sb_info *server = SMB_SERVER(i); smb_lock_server(server); - result = smb_proc_open(server, dir); + result = 0; + if (!smb_is_open(i)) + result = smb_proc_open(server, dentry, wish); smb_unlock_server(server); if (result) { - printk("smb_open: %s/%s open failed, result=%d\n", - dir->d_parent->d_name.name, dir->d_name.name, - result); +#ifdef SMBFS_PARANOIA +printk("smb_open: %s/%s open failed, result=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, result); +#endif goto out; } + /* + * A successful open means the path is still valid ... + */ + smb_renew_times(dentry); } result = -EACCES; @@ -679,15 +811,35 @@ /* We're called with the server locked */ -static int smb_proc_close(struct smb_sb_info *server, - __u16 fileid, __u32 mtime) +static int +smb_proc_close(struct smb_sb_info *server, __u16 fileid, __u32 mtime) { smb_setup_header(server, SMBclose, 3, 0); WSET(server->packet, smb_vwv0, fileid); - DSET(server->packet, smb_vwv1, mtime); + DSET(server->packet, smb_vwv1, utc2local(mtime)); return smb_request_ok(server, SMBclose, 0, 0); } +/* + * Called with the server locked + */ +static int +smb_proc_close_inode(struct smb_sb_info *server, struct inode * ino) +{ + int result = 0; + if (smb_is_open(ino)) + { + /* + * We clear the open flag in advance, in case another + * process observes the value while we block below. + */ + ino->u.smbfs_i.open = 0; + result = smb_proc_close(server, ino->u.smbfs_i.fileid, + ino->i_mtime); + } + return result; +} + int smb_close(struct inode *ino) { @@ -697,39 +849,66 @@ { struct smb_sb_info *server = SMB_SERVER(ino); smb_lock_server(server); - result = smb_proc_close(server, ino->u.smbfs_i.fileid, - ino->i_mtime); + result = smb_proc_close_inode(server, ino); smb_unlock_server(server); - ino->u.smbfs_i.open = 0; } return result; } +/* + * This routine is called from dput() when d_count is going to 0. + * We use this to close the file so that cached dentries don't + * keep too many files open. + * + * There are some tricky race conditions here: the dentry may go + * back into use while we're closing the file, and we don't want + * the new user to be confused as to the open status. + */ +void +smb_close_dentry(struct dentry * dentry) +{ + struct inode *ino = dentry->d_inode; + + if (ino) + { + if (smb_is_open(ino)) + { + struct smb_sb_info *server = SMB_SERVER(ino); + smb_lock_server(server); + /* + * Check whether the dentry is back in use. + */ + if (dentry->d_count <= 1) + { +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_close_dentry: closing %s/%s, count=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count); +#endif + smb_proc_close_inode(server, ino); + } + smb_unlock_server(server); + } + } + /* Consider dropping negative dentries? */ +#if 0 + else + d_drop(dentry); +#endif +} + /* In smb_proc_read and smb_proc_write we do not retry, because the file-id would not be valid after a reconnection. */ -/* smb_proc_read: fs indicates if it should be copied with - copy_to_user. */ - int -smb_proc_read(struct inode *ino, off_t offset, long count, char *data) +smb_proc_read(struct inode *ino, off_t offset, int count, char *data) { struct smb_sb_info *server = SMB_SERVER(ino); __u16 returned_count, data_len; char *buf; int result; - struct dentry * dentry; - - if (!ino || !(dentry = ino->u.smbfs_i.dentry)) - { - printk("smb_proc_read: no inode!\n"); - return -EIO; - } smb_lock_server(server); smb_setup_header(server, SMBread, 5, 0); - - /* Achtung! Do not refer to the cached packet after the request! */ buf = server->packet; WSET(buf, smb_vwv0, ino->u.smbfs_i.fileid); WSET(buf, smb_vwv1, count); @@ -739,10 +918,6 @@ result = smb_request_ok(server, SMBread, 5, -1); if (result < 0) goto out; -#if 0 -printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n", -dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet); -#endif returned_count = WVAL(server->packet, smb_vwv0); buf = SMB_BUF(server->packet); @@ -758,6 +933,11 @@ result = data_len; out: +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_read: file %s/%s, count=%d, result=%d\n", +((struct dentry *) ino->u.smbfs_i.dentry)->d_parent->d_name.name, +((struct dentry *) ino->u.smbfs_i.dentry)->d_name.name, count, result); +#endif smb_unlock_server(server); return result; } @@ -766,10 +946,17 @@ smb_proc_write(struct inode *ino, off_t offset, int count, const char *data) { struct smb_sb_info *server = SMB_SERVER(ino); - int res = 0; + int result; __u8 *p; smb_lock_server(server); +#if SMBFS_DEBUG_VERBOSE +{struct dentry * dentry = ino->u.smbfs_i.dentry; +printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n", +dentry->d_parent->d_name.name, dentry->d_name.name, +count, offset, server->packet_size); +} +#endif p = smb_setup_header(server, SMBwrite, 5, count + 3); WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid); WSET(server->packet, smb_vwv1, count); @@ -780,12 +967,12 @@ WSET(p, 0, count); memcpy(p+2, data, count); - if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0) - res = WVAL(server->packet, smb_vwv0); + if ((result = smb_request_ok(server, SMBwrite, 1, 0)) >= 0) + result = WVAL(server->packet, smb_vwv0); smb_unlock_server(server); - return res; + return result; } int @@ -1003,50 +1190,46 @@ smb_init_dirent(server, fattr); fattr->attr = aDIR; fattr->f_ino = 1; + fattr->f_mtime = CURRENT_TIME; smb_finish_dirent(server, fattr); } static __u8 * -smb_decode_dirent(struct smb_sb_info *server, __u8 *p, - struct smb_dirent *entry) +smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct dirent *entry) { - smb_init_dirent(server, &(entry->attr)); + int len; p += SMB_STATUS_SIZE; /* reserved (search_status) */ - entry->attr.attr = *p; - entry->attr.f_mtime = entry->attr.f_atime = entry->attr.f_ctime = - date_dos2unix(WVAL(p, 1), WVAL(p, 3)); - entry->attr.f_size = DVAL(p, 5); - entry->len = strlen(p + 9); - if (entry->len > 12) - { - entry->len = 12; - } - memcpy(entry->name, p + 9, entry->len); - entry->name[entry->len] = '\0'; - while (entry->len > 2) - { - /* Pathworks fills names with spaces */ - entry->len -= 1; - if (entry->name[entry->len] == ' ') - { - entry->name[entry->len] = '\0'; - } + len = strlen(p + 9); + if (len > 12) + { + len = 12; } + memcpy(entry->d_name, p + 9, len); +#ifdef SMBFS_TRIM_BLANKS + /* + * Trim trailing blanks for Pathworks servers + */ + while (len > 2 && entry->d_name[len-1] == ' ') + len--; +#endif + entry->d_name[len] = '\0'; + entry->d_reclen = len; + entry->d_ino = 0; /* no inode number available */ + switch (server->opt.case_handling) { case SMB_CASE_UPPER: - str_upper(entry->name); + str_upper(entry->d_name); break; case SMB_CASE_LOWER: - str_lower(entry->name); + str_lower(entry->d_name); break; default: break; } pr_debug("smb_decode_dirent: name = %s\n", entry->name); - smb_finish_dirent(server, &(entry->attr)); return p + 22; } @@ -1056,50 +1239,43 @@ static int smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos, - int cache_size, struct smb_dirent *entry) + void *cachep) { char *p; - char *buf; - int error; int result; - int i; - int first, total_count; - struct smb_dirent *current_entry; + int i, first, entries_seen, entries; + int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; __u16 bcc; __u16 count; char status[SMB_STATUS_SIZE]; - int entries_asked = (server->opt.max_xmit - 100) / SMB_DIRINFO_SIZE; - static struct qstr mask = { "*.*", 3, 0 }; - pr_debug("SMB call readdir %d @ %d\n", cache_size, fpos); + pr_debug("smb_proc_readdir_short: %d @ %d\n", cache_size, fpos); smb_lock_server(server); + /* N.B. We need to reinitialize the cache to restart */ retry: + smb_init_dircache(cachep); first = 1; - total_count = 0; - current_entry = entry; + entries = 0; + entries_seen = 2; /* implicit . and .. */ while (1) { - buf = server->packet; + p = smb_setup_header(server, SMBsearch, 2, 0); + WSET(server->packet, smb_vwv0, entries_asked); + WSET(server->packet, smb_vwv1, aDIR); + *p++ = 4; if (first == 1) { - p = smb_setup_header(server, SMBsearch, 2, 0); - WSET(buf, smb_vwv0, entries_asked); - WSET(buf, smb_vwv1, aDIR); - *p++ = 4; p = smb_encode_path(server, p, dir, &mask); *p++ = 5; WSET(p, 0, 0); p += 2; + first = 0; } else { - p = smb_setup_header(server, SMBsearch, 2, 0); - WSET(buf, smb_vwv0, entries_asked); - WSET(buf, smb_vwv1, aDIR); - *p++ = 4; *p++ = 0; *p++ = 5; WSET(p, 0, SMB_STATUS_SIZE); @@ -1110,39 +1286,25 @@ smb_setup_bcc(server, p); - if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0) + result = smb_request_ok(server, SMBsearch, 1, -1); + if (result < 0) { - if ((server->rcls == ERRDOS) - && (server->err == ERRnofiles)) - { - result = total_count - fpos; - goto unlock_return; - } else - { - if (smb_retry(server)) - { - goto retry; - } - result = error; - goto unlock_return; - } + if ((server->rcls == ERRDOS) && + (server->err == ERRnofiles)) + break; + if (smb_retry(server)) + goto retry; + goto unlock_return; } p = SMB_VWV(server->packet); count = WVAL(p, 0); - bcc = WVAL(p, 2); - - first = 0; - if (count <= 0) - { - result = total_count - fpos; - goto unlock_return; - } + break; + + result = -EIO; + bcc = WVAL(p, 2); if (bcc != count * SMB_DIRINFO_SIZE + 3) - { - result = -EIO; goto unlock_return; - } p += 7; /* Read the last entry into the status field. */ @@ -1155,102 +1317,125 @@ for (i = 0; i < count; i++) { - if (total_count < fpos) - { - p += SMB_DIRINFO_SIZE; - pr_debug("smb_proc_readdir: skipped entry.\n"); - pr_debug(" total_count = %d\n" - " i = %d, fpos = %d\n", - total_count, i, fpos); - } else if (total_count >= fpos + cache_size) + struct dirent this_ent, *entry = &this_ent; + + p = smb_decode_dirent(server, p, entry); + if (entries_seen == 2 && entry->d_name[0] == '.') { - result = total_count - fpos; - goto unlock_return; - } else + if (entry->d_reclen == 1) + continue; + if (entry->d_name[1] == '.' && + entry->d_reclen == 2) + continue; + } + if (entries_seen >= fpos) { - p = smb_decode_dirent(server, p, - current_entry); - current_entry->f_pos = total_count; - pr_debug("smb_proc_readdir: entry->f_pos = " - "%u\n", entry->f_pos); - current_entry += 1; + pr_debug("smb_proc_readdir: fpos=%u\n", + entries_seen); + smb_add_to_cache(cachep, entry, entries_seen); + entries++; } - total_count += 1; +#ifdef SMBFS_DEBUG_VERBOSE +else +printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n", +entries_seen, i, fpos); +#endif + entries_seen++; } } - unlock_return: + result = entries; + + unlock_return: smb_unlock_server(server); return result; } -/* interpret a long filename structure - this is mostly guesses at the - moment. The length of the structure is returned. The structure of - a long filename depends on the info level. 260 is used by NT and 2 - is used by OS/2. */ - +/* + * Interpret a long filename structure using the specified info level: + * level 1 -- Win NT, Win 95, OS/2 + * level 2 -- OS/2 + * level 259 -- File name and length only, Win NT, Win 95 + * level 260 -- Win NT, Win 95 + * There seem to be numerous inconsistencies and bugs in implementation. + */ static char * smb_decode_long_dirent(struct smb_sb_info *server, char *p, - struct smb_dirent *entry, int level) + struct dirent *entry, int level) { char *result; unsigned int len; - smb_init_dirent(server, &(entry->attr)); + /* + * SMB doesn't have a concept of inode numbers ... + */ + entry->d_ino = 0; switch (level) { - /* We might add more levels later... */ case 1: - entry->attr.f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4)); - entry->attr.f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8)); - entry->attr.f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12)); - entry->attr.f_size = DVAL(p, 16); - entry->attr.attr = *(p+24); - /* - * Achtung, lengths can go up to 255 - */ len = *((unsigned char *) p + 26); - entry->len = len; - strncpy(entry->name, p + 27, len); - entry->name[len] = '\0'; + entry->d_reclen = len; + strncpy(entry->d_name, p + 27, len); + entry->d_name[len] = '\0'; result = p + 28 + len; break; + case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */ + /* + * This info level returns just the file name and length, + * which is all we need right now. + */ + result = p + DVAL(p, 0); + /* DVAL(p, 4) should be resume key? Seems to be 0 .. */ + len = DVAL(p, 8); + if (len > 255) + len = 255; + strncpy(entry->d_name, p + 12, len); + /* + * Kludge alert: Win NT 4.0 adds a trailing null byte and + * counts it in the name length, but Win 95 doesn't. Hence + * we test for a trailing null and decrement the length ... + */ + if (len && entry->d_name[len-1] == '\0') + len--; + entry->d_name[len] = '\0'; + entry->d_reclen = len; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_decode_long_dirent: info 259, len=%d, name=%s\n", +len, entry->d_name); +#endif + break; + default: - printk("smb_decode: Unknown long filename format %d\n", level); + printk("smb_decode_long_dirent: Unknown level %d\n", level); result = p + WVAL(p, 0); } switch (server->opt.case_handling) { case SMB_CASE_UPPER: - str_upper(entry->name); + str_upper(entry->d_name); break; case SMB_CASE_LOWER: - str_lower(entry->name); + str_lower(entry->d_name); break; default: break; } - smb_finish_dirent(server, &(entry->attr)); - return result; } static int smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos, - int cache_size, struct smb_dirent *cache) + void *cachep) { - /* NT uses 260, OS/2 uses 2. Both accept 1. */ - const int info_level = 1; + /* Both NT and OS/2 accept info level 1 (but see note below). */ + int info_level = 1; const int max_matches = 512; - char *p; - char *lastname; - unsigned lastname_len; - int i; + char *p, *mask, *lastname; int first, entries, entries_seen; unsigned char *resp_data = NULL; @@ -1260,36 +1445,45 @@ __u16 command; - int result; - - int ff_resume_key = 0; + int ff_resume_key = 0; /* this isn't being used */ int ff_searchcount = 0; int ff_eos = 0; int ff_lastname = 0; int ff_dir_handle = 0; int loop_count = 0; + int mask_len, i, result; - char param[SMB_MAXPATHLEN + 2 + 12]; /* too long for the stack! */ - int mask_len; - char *mask = &(param[12]); - + char param[12 + SMB_MAXPATHLEN + 2]; /* too long for the stack! */ static struct qstr star = { "*", 1, 0 }; - mask_len = smb_encode_path(server, mask, dir, &star) - mask; - - mask[mask_len] = 0; - mask[mask_len + 1] = 0; - - pr_debug("smb_readdir_long cache=%d, fpos=%d, mask=%s\n", - cache_size, fpos, mask); + /* + * Check whether to change the info level. There appears to be + * a bug in Win NT 4.0's handling of info level 1, whereby it + * truncates the directory scan for certain patterns of files. + * Hence we use level 259 for NT. (Win 95 uses this too?) + */ + if (server->opt.protocol >= SMB_PROTOCOL_NT1) + info_level = 259; smb_lock_server(server); retry: - + /* + * Encode the initial path + */ + mask = &(param[12]); + mask_len = smb_encode_path(server, mask, dir, &star) - mask; first = 1; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir_long: starting fpos=%d, mask=%s\n", fpos, mask); +#endif + /* + * We must reinitialize the dircache when retrying. + */ + smb_init_dircache(cachep); entries = 0; entries_seen = 2; + ff_eos = 0; while (ff_eos == 0) { @@ -1301,6 +1495,7 @@ entries = -EIO; break; } + if (first != 0) { command = TRANSACT2_FINDFIRST; @@ -1314,10 +1509,11 @@ } else { command = TRANSACT2_FINDNEXT; - pr_debug("hand=0x%X resume=%d ff_lastnm=%d mask=%s\n", - ff_dir_handle, ff_resume_key, ff_lastname, - mask); - WSET(param, 0, ff_dir_handle); +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir_long: handle=0x%X, resume=%d, lastname=%d, mask=%s\n", +ff_dir_handle, ff_resume_key, ff_lastname, mask); +#endif + WSET(param, 0, ff_dir_handle); /* search handle */ WSET(param, 2, max_matches); /* max count */ WSET(param, 4, info_level); DSET(param, 6, ff_resume_key); /* ff_resume_key */ @@ -1335,7 +1531,7 @@ } result = smb_trans2_request(server, command, - 0, NULL, 12 + mask_len + 2, param, + 0, NULL, 12 + mask_len + 1, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); @@ -1343,10 +1539,13 @@ { if (smb_retry(server)) { +#ifdef SMBFS_PARANOIA +printk("smb_proc_readdir_long: error=%d, retrying\n", result); +#endif goto retry; } #ifdef SMBFS_PARANOIA -printk("smb_proc_readdir_long: trans2_request error=%d\n", result); +printk("smb_proc_readdir_long: error=%d, breaking\n", result); #endif entries = result; break; @@ -1354,13 +1553,17 @@ if (server->rcls != 0) { #ifdef SMBFS_PARANOIA -printk("smb_proc_readdir_long: error, rcls=%d, err=%d\n", +printk("smb_proc_readdir_long: rcls=%d, err=%d, breaking\n", server->rcls, server->err); #endif - /* Why isn't this considered an error? */ - /* entries = -EIO; */ + entries = -smb_errno(server->rcls, server->err); break; } +#ifdef SMBFS_PARANOIA +if (resp_data + resp_data_len > server->packet + server->packet_size) +printk("s_p_r_l: data past packet end! data=%p, len=%d, packet=%p\n", +resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size); +#endif /* parse out some important return info */ if (first != 0) @@ -1380,86 +1583,90 @@ { break; } - /* point to the data bytes */ - p = resp_data; /* we might need the lastname for continuations */ - lastname = ""; - lastname_len = 0; + mask_len = 0; if (ff_lastname > 0) { - ff_resume_key = 0; - lastname = p + ff_lastname; + lastname = resp_data + ff_lastname; switch (info_level) { case 260: - lastname_len = resp_data_len - ff_lastname; + if (ff_lastname < resp_data_len) + mask_len = resp_data_len - ff_lastname; break; case 1: - lastname_len = *((unsigned char *) lastname++); + /* Win NT 4.0 doesn't set the length byte */ + lastname++; + if (ff_lastname + 2 < resp_data_len) + mask_len = strlen(lastname); break; } + /* + * Update the mask string for the next message. + */ + if (mask_len > 255) + mask_len = 255; + if (mask_len) + strncpy(mask, lastname, mask_len); + ff_resume_key = 0; } - lastname_len = min(lastname_len, 256); - strncpy(mask, lastname, lastname_len); - mask[lastname_len] = '\0'; - + mask[mask_len] = 0; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir_long: new mask, len=%d@%d, mask=%s\n", +mask_len, ff_lastname, mask); +#endif /* Now we are ready to parse smb directory entries. */ + /* point to the data bytes */ + p = resp_data; for (i = 0; i < ff_searchcount; i++) { - struct smb_dirent *entry = &(cache[entries]); + struct dirent this_ent, *entry = &this_ent; - p = smb_decode_long_dirent(server, p, - entry, info_level); + p = smb_decode_long_dirent(server, p, entry, + info_level); pr_debug("smb_readdir_long: got %s\n", entry->name); - if ((entry->name[0] == '.') - && ((entry->name[1] == '\0') - || ((entry->name[1] == '.') - && (entry->name[2] == '\0')))) + /* ignore . and .. from the server */ + if (entries_seen == 2 && entry->d_name[0] == '.') { - /* ignore . and .. from the server */ - continue; + if (entry->d_reclen == 1) + continue; + if (entry->d_name[1] == '.' && + entry->d_reclen == 2) + continue; } if (entries_seen >= fpos) { - entry->f_pos = entries_seen; + smb_add_to_cache(cachep, entry, entries_seen); entries += 1; } - entries_seen += 1; - if (entries < cache_size) - continue; - - /* cache is full */ - goto finished; + entries_seen++; } - pr_debug("received %d entries (eos=%d resume=%d)\n", - ff_searchcount, ff_eos, ff_resume_key); - +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_readdir_long: received %d entries, eos=%d, resume=%d\n", +ff_searchcount, ff_eos, ff_resume_key); +#endif first = 0; } - finished: smb_unlock_server(server); return entries; } int -smb_proc_readdir(struct dentry *dir, int fpos, - int cache_size, struct smb_dirent *entry) +smb_proc_readdir(struct dentry *dir, int fpos, void *cachep) { struct smb_sb_info *server; server = server_from_dentry(dir); if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) - return smb_proc_readdir_long(server, dir, fpos, cache_size, - entry); + return smb_proc_readdir_long(server, dir, fpos, cachep); else - return smb_proc_readdir_short(server, dir, fpos, cache_size, - entry); + return smb_proc_readdir_short(server, dir, fpos, cachep); } static int @@ -1498,7 +1705,6 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir, struct qstr *name, struct smb_fattr *attr) { - char param[SMB_MAXPATHLEN + 20]; char *p; int result; @@ -1506,29 +1712,34 @@ unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; + char param[SMB_MAXPATHLEN + 20]; /* too big for the stack! */ + smb_lock_server(server); + + retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); p = smb_encode_path(server, param + 6, dir, name); - smb_lock_server(server); - retry: result = smb_trans2_request(server, TRANSACT2_QPATHINFO, 0, NULL, p - param, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); - - if (server->rcls != 0) - { - result = -smb_errno(server->rcls, server->err); - goto out; - } if (result < 0) { if (smb_retry(server)) goto retry; goto out; } + if (server->rcls != 0) + { +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n", +¶m[6], result, server->rcls, server->err); +#endif + result = -smb_errno(server->rcls, server->err); + goto out; + } result = -ENOENT; if (resp_data_len < 22) goto out; @@ -1553,15 +1764,19 @@ struct smb_fattr *fattr) { struct smb_sb_info *server; - int result = -1; + int result; server = server_from_dentry(dir); smb_init_dirent(server, fattr); + /* + * N.B. Why would we want to fall back to xxx_core on error? + * If the file doesn't exist (a very common case), the core + * protocol won't find it either. + */ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) result = smb_proc_getattr_trans2(server, dir, name, fattr); - - if (result < 0) + else result = smb_proc_getattr_core(server, dir, name, fattr); smb_finish_dirent(server, fattr); @@ -1605,8 +1820,6 @@ smb_proc_setattr_trans2(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr) { - char param[SMB_MAXPATHLEN + 20]; - char data[26]; char *p; int result; @@ -1614,7 +1827,12 @@ unsigned char *resp_param = NULL; int resp_data_len = 0; int resp_param_len = 0; + char param[SMB_MAXPATHLEN + 20]; /* too long for the stack! */ + char data[26]; + smb_lock_server(server); + + retry: WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ DSET(param, 2, 0); p = smb_encode_path(server, param + 6, dir, NULL); @@ -1627,22 +1845,20 @@ WSET(data, 20, fattr->attr); WSET(data, 22, 0); - smb_lock_server(server); - retry: result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, 26, data, p - param, param, &resp_data_len, &resp_data, &resp_param_len, &resp_param); - - if (server->rcls != 0) - { - smb_unlock_server(server); - return -smb_errno(server->rcls, server->err); - } if (result < 0) + { if (smb_retry(server)) goto retry; + goto out; + } + if (server->rcls != 0) + result = -smb_errno(server->rcls, server->err); +out: smb_unlock_server(server); return 0; } @@ -1651,12 +1867,14 @@ smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir, struct smb_fattr *fattr) { - int result = -1; + int result; + /* + * N.B. Why would we want to fall back to xxx_core on error? + */ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2) result = smb_proc_setattr_trans2(server, dir, fattr); - - if (result < 0) + else result = smb_proc_setattr_core(server, dir, fattr); return result; diff -u --recursive --new-file v2.1.57/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c --- v2.1.57/linux/fs/smbfs/sock.c Tue Sep 23 16:48:49 1997 +++ linux/fs/smbfs/sock.c Sun Oct 12 10:17:05 1997 @@ -126,18 +126,26 @@ } } +int +smb_valid_socket(struct inode * inode) +{ + return (inode && S_ISSOCK(inode->i_mode) && + inode->u.socket_i.type == SOCK_STREAM); +} + static struct socket * server_sock(struct smb_sb_info *server) { struct file *file; - struct inode *inode; - if (server && - (file = server->sock_file) && - (inode = file->f_dentry->d_inode) && - S_ISSOCK(inode->i_mode) && - inode->u.socket_i.type == SOCK_STREAM) - return &(inode->u.socket_i); + if (server && (file = server->sock_file)) + { +#ifdef SMBFS_PARANOIA + if (!smb_valid_socket(file->f_dentry->d_inode)) + printk("smb_server_sock: bad socket!\n"); +#endif + return &file->f_dentry->d_inode->u.socket_i; + } return NULL; } @@ -242,15 +250,13 @@ if (file) { - struct socket * socket = server_sock(server); - - printk("smb_close_socket: closing socket %p\n", socket); - /* - * We need a way to check for tasks running the callback! - */ - if (socket->sk->data_ready == smb_data_callback) - printk("smb_close_socket: still catching keepalives!\n"); - +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_close_socket: closing socket %p\n", server_sock(server)); +#endif +#ifdef SMBFS_PARANOIA +if (server_sock(server)->sk->data_ready == smb_data_callback) +printk("smb_close_socket: still catching keepalives!\n"); +#endif server->sock_file = NULL; close_fp(file); } @@ -325,7 +331,9 @@ if (result < 0) { - pr_debug("smb_get_length: recv error = %d\n", -result); +#ifdef SMBFS_PARANOIA +printk("smb_get_length: recv error = %d\n", -result); +#endif return result; } switch (peek_buf[0]) @@ -339,7 +347,9 @@ goto re_recv; default: - pr_debug("smb_get_length: Invalid NBT packet\n"); +#ifdef SMBFS_PARANOIA +printk("smb_get_length: Invalid NBT packet, code=%x\n", peek_buf[0]); +#endif return -EIO; } @@ -359,39 +369,39 @@ smb_receive(struct smb_sb_info *server) { struct socket *socket = server_sock(server); - int len; - int result; + int len, result; unsigned char peek_buf[4]; - len = smb_get_length(socket, peek_buf); - - if (len < 0) - { - return len; - } + result = smb_get_length(socket, peek_buf); + if (result < 0) + goto out; + len = result; + /* + * Some servers do not respect our max_xmit and send + * larger packets. Try to allocate a new packet, + * but don't free the old one unless we succeed. + */ if (len + 4 > server->packet_size) { - /* Some servers do not care about our max_xmit. They - send larger packets */ + char * packet; pr_debug("smb_receive: Increase packet size from %d to %d\n", server->packet_size, len + 4); + result = -ENOMEM; + packet = smb_vmalloc(len + 4); + if (packet == NULL) + goto out; smb_vfree(server->packet); - server->packet = 0; - server->packet_size = 0; - server->packet = smb_vmalloc(len + 4); - if (server->packet == NULL) - { - return -ENOMEM; - } + server->packet = packet; server->packet_size = len + 4; } memcpy(server->packet, peek_buf, 4); result = smb_receive_raw(socket, server->packet + 4, len); - if (result < 0) { - pr_debug("smb_receive: receive error: %d\n", result); - return result; +#ifdef SMBFS_DEBUG_VERBOSE +printk("smb_receive: receive error: %d\n", result); +#endif + goto out; } server->rcls = *(server->packet+9); server->err = WVAL(server->packet, 11); @@ -400,9 +410,16 @@ if (server->rcls != 0) printk("smb_receive: rcls=%d, err=%d\n", server->rcls, server->err); #endif +out: return result; } +/* + * This routine needs a lot of work. We should check whether the packet + * is all one part before allocating a new one, and should try first to + * copy to a temp buffer before allocating. + * The final server->packet should be the larger of the two. + */ static int smb_receive_trans2(struct smb_sb_info *server, int *ldata, unsigned char **data, @@ -515,6 +532,11 @@ *ldata = data_len; *lparam = param_len; +#ifdef SMBFS_PARANOIA +if (buf_len < server->packet_size) +printk("smb_receive_trans2: changing packet, old size=%d, new size=%d\n", +server->packet_size, buf_len); +#endif smb_vfree(server->packet); server->packet = rcv_buf; server->packet_size = buf_len; @@ -537,9 +559,6 @@ unsigned char *buffer; result = -EBADF; - if (!server) /* this can't happen */ - goto bad_no_server; - buffer = server->packet; if (!buffer) goto bad_no_packet; @@ -586,13 +605,12 @@ return result; bad_conn: - printk("smb_request: result %d, setting invalid\n", result); +#ifdef SMBFS_PARANOIA +printk("smb_request: result %d, setting invalid\n", result); +#endif server->state = CONN_INVALID; smb_invalidate_inodes(server); goto out; -bad_no_server: - printk("smb_request: no server!\n"); - goto out; bad_no_packet: printk("smb_request: no packet!\n"); goto out; @@ -631,6 +649,7 @@ struct iovec iov[4]; struct msghdr msg; + /* N.B. This test isn't valid! packet_size may be < max_xmit */ if ((bcc + oparam) > server->opt.max_xmit) { return -ENOMEM; @@ -639,6 +658,7 @@ WSET(server->packet, smb_tpscnt, lparam); WSET(server->packet, smb_tdscnt, ldata); + /* N.B. these values should reflect out current packet size */ WSET(server->packet, smb_mprcnt, TRANS2_MAX_TRANSFER); WSET(server->packet, smb_mdrcnt, TRANS2_MAX_TRANSFER); WSET(server->packet, smb_msrcnt, 0); @@ -745,7 +765,9 @@ return result; bad_conn: - printk("smb_trans2_request: connection bad, setting invalid\n"); +#ifdef SMBFS_PARANOIA +printk("smb_trans2_request: connection bad, setting invalid\n"); +#endif server->state = CONN_INVALID; smb_invalidate_inodes(server); goto out; diff -u --recursive --new-file v2.1.57/linux/fs/super.c linux/fs/super.c --- v2.1.57/linux/fs/super.c Wed Sep 24 20:05:48 1997 +++ linux/fs/super.c Mon Oct 13 15:09:20 1997 @@ -17,8 +17,6 @@ * Added change_root: Werner Almesberger & Hans Lermen, Feb '96 */ -#include - #include #include #include @@ -252,29 +250,24 @@ /* * Whee.. Weird sysv syscall. */ -asmlinkage int sys_sysfs(int option, ...) +asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2) { - va_list args; int retval = -EINVAL; - unsigned int index; lock_kernel(); - va_start(args, option); switch (option) { case 1: - retval = fs_index(va_arg(args, const char *)); + retval = fs_index((const char *) arg1); break; case 2: - index = va_arg(args, unsigned int); - retval = fs_name(index, va_arg(args, char *)); + retval = fs_name(arg1, (char *) arg2); break; case 3: retval = fs_maxindex(); break; } - va_end(args); unlock_kernel(); return retval; } @@ -933,12 +926,11 @@ struct file_system_type * fstype; struct dentry * dentry = NULL; struct inode * inode = NULL; - struct file_operations * fops; kdev_t dev; int retval = -EPERM; - const char * t; unsigned long flags = 0; unsigned long page = 0; + struct file dummy; /* allows read-write or read-only flag */ lock_kernel(); if (!suser()) @@ -954,6 +946,7 @@ free_page(page); goto out; } + retval = copy_mount_options (type, &page); if (retval < 0) goto out; @@ -962,8 +955,8 @@ retval = -ENODEV; if (!fstype) goto out; - t = fstype->name; - fops = NULL; + + memset(&dummy, 0, sizeof(dummy)); if (fstype->fs_flags & FS_REQUIRES_DEV) { dentry = namei(dev_name); retval = PTR_ERR(dentry); @@ -984,17 +977,15 @@ if (MAJOR(dev) >= MAX_BLKDEV) goto dput_and_out; - fops = get_blkfops(MAJOR(dev)); retval = -ENOTBLK; - if (!fops) + dummy.f_op = get_blkfops(MAJOR(dev)); + if (!dummy.f_op) goto dput_and_out; - if (fops->open) { - struct file dummy; /* allows read-write or read-only flag */ - memset(&dummy, 0, sizeof(dummy)); + if (dummy.f_op->open) { dummy.f_dentry = dentry; dummy.f_mode = (new_flags & MS_RDONLY) ? 1 : 3; - retval = fops->open(inode, &dummy); + retval = dummy.f_op->open(inode, &dummy); if (retval) goto dput_and_out; } @@ -1009,22 +1000,28 @@ if ((new_flags & MS_MGC_MSK) == MS_MGC_VAL) { flags = new_flags & ~MS_MGC_MSK; retval = copy_mount_options(data, &page); - if (retval < 0) { - put_unnamed_dev(dev); - goto dput_and_out; - } + if (retval < 0) + goto clean_up; } - retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page); + retval = do_mount(dev, dev_name, dir_name, fstype->name, flags, + (void *) page); free_page(page); - if (retval && fops && fops->release) { - fops->release(inode, NULL); - put_unnamed_dev(dev); - } + if (retval) + goto clean_up; + dput_and_out: dput(dentry); out: unlock_kernel(); return retval; + +clean_up: + if (dummy.f_op) { + if (dummy.f_op->release) + dummy.f_op->release(inode, NULL); + } else + put_unnamed_dev(dev); + goto dput_and_out; } __initfunc(static void do_mount_root(void)) diff -u --recursive --new-file v2.1.57/linux/include/linux/dcache.h linux/include/linux/dcache.h --- v2.1.57/linux/include/linux/dcache.h Tue Sep 23 16:48:49 1997 +++ linux/include/linux/dcache.h Sun Oct 12 09:51:42 1997 @@ -104,6 +104,7 @@ /* allocate/de-allocate */ extern struct dentry * d_alloc(struct dentry * parent, const struct qstr *name); extern void prune_dcache(int); +extern void shrink_dcache_sb(struct super_block *); extern int d_invalidate(struct dentry *); #define shrink_dcache() prune_dcache(0) diff -u --recursive --new-file v2.1.57/linux/include/linux/fs.h linux/include/linux/fs.h --- v2.1.57/linux/include/linux/fs.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/fs.h Tue Oct 14 18:29:28 1997 @@ -42,9 +42,10 @@ /* And dynamically-tunable limits and defaults: */ extern int max_inodes; -extern int max_files, nr_files; +extern int max_files, nr_files, nr_free_files; #define NR_INODE 4096 /* this should be bigger than NR_FILE */ #define NR_FILE 1024 /* this can well be larger on a larger system */ +#define NR_RESERVED_FILES 10 /* reserved for root */ #define MAY_EXEC 1 #define MAY_WRITE 2 @@ -627,6 +628,10 @@ extern void init_fifo(struct inode * inode); extern struct inode_operations fifo_inode_operations; + +/* Invalid inode operations -- fs/bad_inode.c */ +extern void make_bad_inode(struct inode * inode); +extern int is_bad_inode(struct inode * inode); extern struct file_operations connecting_fifo_fops; extern struct file_operations read_fifo_fops; diff -u --recursive --new-file v2.1.57/linux/include/linux/list.h linux/include/linux/list.h --- v2.1.57/linux/include/linux/list.h Thu Jul 17 10:06:08 1997 +++ linux/include/linux/list.h Sun Oct 12 10:46:27 1997 @@ -3,7 +3,14 @@ /* * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. */ + struct list_head { struct list_head *next, *prev; }; @@ -15,22 +22,46 @@ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) -static inline void list_add(struct list_head *new, struct list_head *head) +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head * new, + struct list_head * prev, + struct list_head * next) { - struct list_head *next = head->next; next->prev = new; new->next = next; - new->prev = head; - head->next = new; + new->prev = prev; + prev->next = new; } -static inline void list_del(struct list_head *entry) +/* + * Insert a new entry after the specified head.. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) { - struct list_head *next, *prev; - next = entry->next; - prev = entry->prev; next->prev = prev; prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); } static inline int list_empty(struct list_head *head) diff -u --recursive --new-file v2.1.57/linux/include/linux/mm.h linux/include/linux/mm.h --- v2.1.57/linux/include/linux/mm.h Mon Aug 4 16:25:40 1997 +++ linux/include/linux/mm.h Tue Oct 14 18:29:28 1997 @@ -296,6 +296,8 @@ extern unsigned long page_unuse(unsigned long); extern int shrink_mmap(int, int); extern void truncate_inode_pages(struct inode *, unsigned long); +extern unsigned long get_cached_page(struct inode *, unsigned long, int); +extern void put_cached_page(unsigned long); #define GFP_BUFFER 0x00 #define GFP_ATOMIC 0x01 diff -u --recursive --new-file v2.1.57/linux/include/linux/nfs.h linux/include/linux/nfs.h --- v2.1.57/linux/include/linux/nfs.h Mon Apr 14 16:28:26 1997 +++ linux/include/linux/nfs.h Sun Oct 12 10:15:56 1997 @@ -129,14 +129,6 @@ struct nfs_time mtime; }; -struct nfs_entry { - __u32 fileid; - char * name; - unsigned int length:31, - eof:1; - __u32 cookie; -}; - struct nfs_fsinfo { __u32 tsize; __u32 bsize; diff -u --recursive --new-file v2.1.57/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.1.57/linux/include/linux/nfs_fs.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/nfs_fs.h Tue Oct 14 18:29:28 1997 @@ -125,7 +125,7 @@ extern int nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name); extern int nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle, - u32 cookie, unsigned int size, struct nfs_entry *entry); + u32 cookie, unsigned int size, __u32 *entry); extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *res); @@ -138,7 +138,7 @@ extern int init_nfs_fs(void); extern struct inode *nfs_fhget(struct super_block *sb, struct nfs_fh *fhandle, struct nfs_fattr *fattr); -extern void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr); +extern int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr); extern int nfs_revalidate(struct inode *); extern int _nfs_revalidate_inode(struct nfs_server *, struct inode *); diff -u --recursive --new-file v2.1.57/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h --- v2.1.57/linux/include/linux/proc_fs.h Thu Sep 11 09:02:24 1997 +++ linux/include/linux/proc_fs.h Tue Oct 14 18:29:36 1997 @@ -237,13 +237,15 @@ unsigned long size; struct inode_operations * ops; int (*get_info)(char *, char **, off_t, int, int); - void (*fill_inode)(struct inode *); + void (*fill_inode)(struct inode *, int); struct proc_dir_entry *next, *parent, *subdir; void *data; int (*read_proc)(char *page, char **start, off_t off, int count, int *eof, void *data); int (*write_proc)(struct file *file, const char *buffer, unsigned long count, void *data); + unsigned int count; /* use count */ + int deleted; /* delete flag */ }; extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, diff -u --recursive --new-file v2.1.57/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h --- v2.1.57/linux/include/linux/smb_fs.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/smb_fs.h Tue Oct 14 18:31:26 1997 @@ -9,6 +9,7 @@ #ifndef _LINUX_SMB_FS_H #define _LINUX_SMB_FS_H +#include #include /* @@ -71,30 +72,24 @@ /* linux/fs/smbfs/dir.c */ extern struct inode_operations smb_dir_inode_operations; -struct smb_inode_info *smb_find_inode(struct smb_sb_info *server, ino_t ino); -void smb_free_inode_info(struct smb_inode_info *i); -void smb_free_all_inodes(struct smb_sb_info *server); -void smb_init_root(struct smb_sb_info *server); -int smb_stat_root(struct smb_sb_info *server); -void smb_init_dir_cache(void); -void smb_invalid_dir_cache(struct inode *); -void smb_free_dir_cache(void); +void smb_init_root(struct smb_sb_info *); +int smb_stat_root(struct smb_sb_info *); +void smb_renew_times(struct dentry *); /* linux/fs/smbfs/ioctl.c */ int smb_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg); /* linux/fs/smbfs/inode.c */ -struct super_block *smb_read_super(struct super_block *sb, - void *raw_data, int silent); +struct super_block *smb_read_super(struct super_block *, void *, int); extern int init_smb_fs(void); -void smb_invalidate_inodes(struct smb_sb_info *server); -int smb_revalidate_inode(struct inode *i); -int smb_refresh_inode(struct inode *i); -int smb_notify_change(struct inode *inode, struct iattr *attr); -void smb_invalidate_connection(struct smb_sb_info *server); -int smb_conn_is_valid(struct smb_sb_info *server); -unsigned long smb_invent_inos(unsigned long n); +void smb_invalidate_inodes(struct smb_sb_info *); +int smb_revalidate_inode(struct inode *); +int smb_refresh_inode(struct inode *); +int smb_notify_change(struct inode *, struct iattr *); +void smb_invalidate_connection(struct smb_sb_info *); +int smb_conn_is_valid(struct smb_sb_info *); +unsigned long smb_invent_inos(unsigned long); struct inode *smb_iget(struct super_block *, struct smb_fattr *); /* linux/fs/smbfs/proc.c */ @@ -105,6 +100,7 @@ int smb_offerconn(struct smb_sb_info *server); int smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt); int smb_close(struct inode *); +void smb_close_dentry(struct dentry *); int smb_open(struct dentry *, int); static inline int smb_is_open(struct inode *i) @@ -112,34 +108,31 @@ return (i->u.smbfs_i.open == SMB_SERVER(i)->generation); } -int smb_proc_read(struct inode *, off_t, long, char *); +int smb_proc_read(struct inode *, off_t, int, char *); int smb_proc_write(struct inode *, off_t, int, const char *); int smb_proc_create(struct dentry *, struct qstr *, __u16, time_t); int smb_proc_mv(struct dentry *, struct qstr *, struct dentry *, struct qstr *); int smb_proc_mkdir(struct dentry *, struct qstr *); int smb_proc_rmdir(struct dentry *, struct qstr *); int smb_proc_unlink(struct dentry *dir, struct qstr *); -int smb_proc_readdir(struct dentry *dir, int fpos, int cache_size, struct smb_dirent *entry); -int smb_proc_getattr(struct dentry *dir, struct qstr *name, - struct smb_fattr *entry); -int smb_proc_setattr(struct smb_sb_info *server, - struct dentry *dir, - struct smb_fattr *new_finfo); -int smb_proc_dskattr(struct super_block *sb, struct statfs *attr); -int smb_proc_reconnect(struct smb_sb_info *server); -int smb_proc_connect(struct smb_sb_info *server); -int smb_proc_disconnect(struct smb_sb_info *server); -int smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length); +int smb_proc_readdir(struct dentry *, int, void *); +int smb_proc_getattr(struct dentry *, struct qstr *, struct smb_fattr *); +int smb_proc_setattr(struct smb_sb_info *, struct dentry *, struct smb_fattr *); +int smb_proc_dskattr(struct super_block *, struct statfs *); +int smb_proc_reconnect(struct smb_sb_info *); +int smb_proc_connect(struct smb_sb_info *); +int smb_proc_disconnect(struct smb_sb_info *); +int smb_proc_trunc(struct smb_sb_info *, __u16, __u32); void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *); /* linux/fs/smbfs/sock.c */ +int smb_valid_socket(struct inode *); +void smb_close_socket(struct smb_sb_info *); int smb_release(struct smb_sb_info *server); int smb_connect(struct smb_sb_info *server); int smb_request(struct smb_sb_info *server); -int smb_request_read_raw(struct smb_sb_info *server, - unsigned char *target, int max_len); -int smb_request_write_raw(struct smb_sb_info *server, - unsigned const char *source, int length); +int smb_request_read_raw(struct smb_sb_info *, unsigned char *, int); +int smb_request_write_raw(struct smb_sb_info *, unsigned const char *, int); int smb_catch_keepalive(struct smb_sb_info *server); int smb_dont_catch_keepalive(struct smb_sb_info *server); int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command, @@ -150,6 +143,72 @@ /* linux/fs/smbfs/mmap.c */ int smb_mmap(struct file * file, struct vm_area_struct * vma); + +/* fs/smbfs/cache.c */ + +/* + * The cache index describes the pages mapped starting + * at offset PAGE_SIZE. We keep only a minimal amount + * of information here. + */ +struct cache_index { + unsigned short num_entries; + unsigned short space; + struct cache_block * block; +}; + +#define NINDEX (PAGE_SIZE-64)/sizeof(struct cache_index) +/* + * The cache head is mapped as the page at offset 0. + */ +struct cache_head { + int valid; + int status; /* error code or 0 */ + int entries; /* total entries */ + int pages; /* number of data pages */ + int idx; /* index of current data page */ + struct cache_index index[NINDEX]; +}; + +/* + * An array of cache_entry structures holds information + * for each object in the cache_block. + */ +struct cache_entry { + ino_t ino; + unsigned short namelen; + unsigned short offset; +}; + +/* + * The cache blocks hold the actual data. The entry table grows up + * while the names grow down, and we have space until they meet. + */ +struct cache_block { + union { + struct cache_entry table[1]; + char names[PAGE_SIZE]; + } cb_data; +}; + +/* + * To return an entry, we can pass a reference to the + * name instead of having to copy it. + */ +struct cache_dirent { + ino_t ino; + unsigned long pos; + int len; + char * name; +}; + +struct cache_head * smb_get_dircache(struct dentry *); +void smb_init_dircache(struct cache_head *); +void smb_free_dircache(struct cache_head *); +int smb_refill_dircache(struct cache_head *, struct dentry *); +void smb_add_to_cache(struct cache_head *, struct dirent *, off_t); +int smb_find_in_cache(struct cache_head *, off_t, struct cache_dirent *); +void smb_invalid_dir_cache(struct inode *); #endif /* __KERNEL__ */ diff -u --recursive --new-file v2.1.57/linux/include/linux/smb_fs_i.h linux/include/linux/smb_fs_i.h --- v2.1.57/linux/include/linux/smb_fs_i.h Tue Sep 23 16:48:50 1997 +++ linux/include/linux/smb_fs_i.h Sun Oct 12 10:17:05 1997 @@ -22,11 +22,13 @@ * (open == generation). */ unsigned int open; - void * dentry; /* The dentry we were opened with */ __u16 fileid; /* What id to handle a file with? */ __u16 attr; /* Attribute fields, DOS value */ __u16 access; /* Access bits. */ + __u16 cache_valid; /* dircache valid? */ + unsigned long oldmtime; /* last time refreshed */ + void * dentry; /* The dentry we were opened with */ }; #endif diff -u --recursive --new-file v2.1.57/linux/include/linux/smb_fs_sb.h linux/include/linux/smb_fs_sb.h --- v2.1.57/linux/include/linux/smb_fs_sb.h Wed Sep 3 20:52:44 1997 +++ linux/include/linux/smb_fs_sb.h Sun Oct 12 10:17:05 1997 @@ -15,6 +15,11 @@ #include #include +/* Get the server for the specified dentry */ +#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb +#define SB_of(server) ((struct super_block *) ((char *)(server) - \ + (unsigned long)(&((struct super_block *)0)->u.smbfs_sb))) + struct smb_sb_info { enum smb_conn_state state; struct file * sock_file; @@ -29,6 +34,7 @@ struct smb_conn_opt opt; struct semaphore sem; + struct wait_queue * wait; __u32 packet_size; unsigned char * packet; diff -u --recursive --new-file v2.1.57/linux/include/linux/sunrpc/sched.h linux/include/linux/sunrpc/sched.h --- v2.1.57/linux/include/linux/sunrpc/sched.h Thu Jun 12 15:30:27 1997 +++ linux/include/linux/sunrpc/sched.h Sun Oct 12 10:17:46 1997 @@ -143,7 +143,7 @@ void rpc_delay(struct rpc_task *, unsigned long); void * rpc_allocate(unsigned int flags, unsigned int); void rpc_free(void *); -void rpciod_up(void); +int rpciod_up(void); void rpciod_down(void); extern __inline__ void * diff -u --recursive --new-file v2.1.57/linux/kernel/fork.c linux/kernel/fork.c --- v2.1.57/linux/kernel/fork.c Wed Sep 24 20:05:48 1997 +++ linux/kernel/fork.c Sun Oct 12 10:12:38 1997 @@ -208,7 +208,6 @@ struct vm_area_struct * mpnt, *tmp, **pprev; int retval; - mm->mmap = mm->mmap_cache = NULL; flush_cache_mm(current->mm); pprev = &mm->mmap; for (mpnt = current->mm->mmap ; mpnt ; mpnt = mpnt->vm_next) { @@ -254,8 +253,7 @@ if (retval) goto fail_nomem; } - flush_tlb_mm(current->mm); - return 0; + retval = 0; fail_nomem: flush_tlb_mm(current->mm); @@ -276,7 +274,10 @@ mm->count = 1; mm->def_flags = 0; mm->mmap_sem = MUTEX; - mm->pgd = NULL; + /* + * Leave mm->pgd set to the parent's pgd + * so that pgd_offset() is always valid. + */ mm->mmap = mm->mmap_cache = NULL; /* It has not run yet, so cannot be present in anyone's @@ -324,10 +325,12 @@ goto free_mm; retval = dup_mmap(mm); if (retval) - goto free_mm; + goto free_pt; return 0; free_mm: + mm->pgd = NULL; +free_pt: tsk->mm = NULL; mmput(mm); fail_nomem: @@ -376,7 +379,13 @@ struct files_struct *oldf, *newf; struct file **old_fds, **new_fds; + /* + * A background process may not have any files ... + */ oldf = current->files; + if (!oldf) + return 0; + if (clone_flags & CLONE_FILES) { oldf->count++; return 0; @@ -516,7 +525,9 @@ } ++total_forks; error = p->pid; - goto fork_out; +bad_fork: + unlock_kernel(); + return error; bad_fork_cleanup_sighand: exit_sighand(p); @@ -536,10 +547,7 @@ nr_tasks--; bad_fork_free: free_task_struct(p); -bad_fork: -fork_out: - unlock_kernel(); - return error; + goto bad_fork; } static void files_ctor(void *fp, kmem_cache_t *cachep, unsigned long flags) diff -u --recursive --new-file v2.1.57/linux/kernel/ksyms.c linux/kernel/ksyms.c --- v2.1.57/linux/kernel/ksyms.c Thu Sep 11 09:02:24 1997 +++ linux/kernel/ksyms.c Sun Oct 12 10:11:28 1997 @@ -192,6 +192,8 @@ EXPORT_SYMBOL(posix_block_lock); EXPORT_SYMBOL(posix_unblock_lock); EXPORT_SYMBOL(dput); +EXPORT_SYMBOL(get_cached_page); +EXPORT_SYMBOL(put_cached_page); #if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE) EXPORT_SYMBOL(do_nfsservctl); @@ -369,6 +371,8 @@ EXPORT_SYMBOL(get_hash_table); EXPORT_SYMBOL(get_empty_inode); EXPORT_SYMBOL(insert_inode_hash); +EXPORT_SYMBOL(make_bad_inode); +EXPORT_SYMBOL(is_bad_inode); EXPORT_SYMBOL(event); EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__up); diff -u --recursive --new-file v2.1.57/linux/kernel/sysctl.c linux/kernel/sysctl.c --- v2.1.57/linux/kernel/sysctl.c Thu Jul 17 10:06:09 1997 +++ linux/kernel/sysctl.c Sun Oct 12 10:10:40 1997 @@ -147,7 +147,7 @@ 0444, NULL, &proc_dointvec}, {KERN_MAXINODE, "inode-max", &max_inodes, sizeof(int), 0644, NULL, &proc_dointvec}, - {KERN_NRFILE, "file-nr", &nr_files, sizeof(int), + {KERN_NRFILE, "file-nr", &nr_files, 3*sizeof(int), 0444, NULL, &proc_dointvec}, {KERN_MAXFILE, "file-max", &max_files, sizeof(int), 0644, NULL, &proc_dointvec}, diff -u --recursive --new-file v2.1.57/linux/mm/filemap.c linux/mm/filemap.c --- v2.1.57/linux/mm/filemap.c Tue Sep 23 16:48:50 1997 +++ linux/mm/filemap.c Sun Oct 12 10:11:28 1997 @@ -1398,3 +1398,60 @@ return written; return status; } + +/* + * Support routines for directory cacheing using the page cache. + */ + +/* + * Finds the page at the specified offset, installing a new page + * if requested. The count is incremented and the page is locked. + * + * Note: we don't have to worry about races here, as the caller + * is holding the inode semaphore. + */ +unsigned long get_cached_page(struct inode * inode, unsigned long offset, + int new) +{ + struct page * page; + struct page ** hash; + unsigned long page_cache; + + hash = page_hash(inode, offset); + page = __find_page(inode, offset, *hash); + if (!page) { + if (!new) + goto out; + page_cache = get_free_page(GFP_KERNEL); + if (!page_cache) + goto out; + page = mem_map + MAP_NR(page_cache); + add_to_page_cache(page, inode, offset, hash); + } + if (atomic_read(&page->count) != 2) + printk("get_cached_page: page count=%d\n", + atomic_read(&page->count)); + if (test_bit(PG_locked, &page->flags)) + printk("get_cached_page: page already locked!\n"); + set_bit(PG_locked, &page->flags); + +out: + return page_address(page); +} + +/* + * Unlock and free a page. + */ +void put_cached_page(unsigned long addr) +{ + struct page * page = mem_map + MAP_NR(addr); + + if (!test_bit(PG_locked, &page->flags)) + printk("put_cached_page: page not locked!\n"); + if (atomic_read(&page->count) != 2) + printk("put_cached_page: page count=%d\n", + atomic_read(&page->count)); + clear_bit(PG_locked, &page->flags); + wake_up(&page->wait); + __free_page(page); +} diff -u --recursive --new-file v2.1.57/linux/mm/vmscan.c linux/mm/vmscan.c --- v2.1.57/linux/mm/vmscan.c Wed Sep 24 20:05:48 1997 +++ linux/mm/vmscan.c Sun Oct 12 10:13:19 1997 @@ -24,9 +24,6 @@ #include #include -#include -#include /* for cli()/sti() */ -#include /* for copy_to/from_user */ #include #include @@ -418,6 +415,7 @@ printk ("Starting kswapd v%.*s\n", i, s); } +#define MAX_SWAP_FAIL 3 /* * The background pageout daemon. * Started as a kernel thread from the init process. @@ -445,6 +443,8 @@ init_swap_timer(); while (1) { + int fail; + kswapd_awake = 0; current->signal = 0; run_task_queue(&tq_disk); @@ -455,13 +455,27 @@ * We now only swap out as many pages as needed. * When we are truly low on memory, we swap out * synchronously (WAIT == 1). -- Rik. + * If we've had too many consecutive failures, + * go back to sleep to let other tasks run. + */ + for (fail = 0; fail++ < MAX_SWAP_FAIL;) { + int pages, wait; + + pages = nr_free_pages; + if (nr_free_pages >= min_free_pages) + pages += atomic_read(&nr_async_pages); + if (pages >= free_pages_high) + break; + wait = (pages < free_pages_low); + if (try_to_free_page(GFP_KERNEL, 0, wait)) + fail = 0; + } + /* + * Report failure if we couldn't reach the minimum goal. */ - while(nr_free_pages < min_free_pages) - try_to_free_page(GFP_KERNEL, 0, 1); - while((nr_free_pages + atomic_read(&nr_async_pages)) < free_pages_low) - try_to_free_page(GFP_KERNEL, 0, 1); - while((nr_free_pages + atomic_read(&nr_async_pages)) < free_pages_high) - try_to_free_page(GFP_KERNEL, 0, 0); + if (nr_free_pages < min_free_pages) + printk("kswapd: failed, got %d of %d\n", + nr_free_pages, min_free_pages); } } diff -u --recursive --new-file v2.1.57/linux/net/sunrpc/clnt.c linux/net/sunrpc/clnt.c --- v2.1.57/linux/net/sunrpc/clnt.c Wed Sep 24 20:05:49 1997 +++ linux/net/sunrpc/clnt.c Sun Oct 12 10:17:46 1997 @@ -116,17 +116,23 @@ /* * Properly shut down an RPC client, terminating all outstanding - * requests. + * requests. Note that we must be certain that cl_oneshot and + * cl_dead are cleared, or else the client would be destroyed + * when the last task releases it. */ int rpc_shutdown_client(struct rpc_clnt *clnt) { dprintk("RPC: shutting down %s client for %s\n", - clnt->cl_protname, clnt->cl_server); + clnt->cl_protname, clnt->cl_server); while (clnt->cl_users) { - dprintk("sigmask %08lx\n", current->signal); - dprintk("users %d\n", clnt->cl_users); - clnt->cl_dead = 1; +#ifdef RPC_DEBUG + printk("rpc_shutdown_client: client %s, tasks=%d\n", + clnt->cl_protname, clnt->cl_users); +#endif + /* Don't let rpc_release_client destroy us */ + clnt->cl_oneshot = 0; + clnt->cl_dead = 0; rpc_killall_tasks(clnt); sleep_on(&destroy_wait); } @@ -162,12 +168,16 @@ { dprintk("RPC: rpc_release_client(%p, %d)\n", clnt, clnt->cl_users); - if (--(clnt->cl_users) == 0) { - wake_up(&destroy_wait); - if (clnt->cl_oneshot || clnt->cl_dead) - rpc_destroy_client(clnt); - } - dprintk("RPC: rpc_release_client done\n"); + if (clnt->cl_users) { + if (--(clnt->cl_users) > 0) + return; + } else + printk("rpc_release_client: %s client already free??\n", + clnt->cl_protname); + + wake_up(&destroy_wait); + if (clnt->cl_oneshot || clnt->cl_dead) + rpc_destroy_client(clnt); } /* @@ -205,10 +215,9 @@ if ((async = (flags & RPC_TASK_ASYNC)) != 0) { if (!func) func = rpc_default_callback; - if (!(task = rpc_new_task(clnt, func, flags))) { - current->blocked = oldmask; - return -ENOMEM; - } + status = -ENOMEM; + if (!(task = rpc_new_task(clnt, func, flags))) + goto out; task->tk_calldata = data; } else { rpc_init_task(task, clnt, NULL, flags); @@ -222,12 +231,13 @@ } else async = 0; + status = 0; if (!async) { status = task->tk_status; rpc_release_task(task); - } else - status = 0; + } +out: current->blocked = oldmask; return status; } @@ -354,6 +364,7 @@ if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL) return; + printk("RPC: buffer allocation failed for task %p\n", task); if (1 || !signalled()) { xprt_release(task); diff -u --recursive --new-file v2.1.57/linux/net/sunrpc/pmap_clnt.c linux/net/sunrpc/pmap_clnt.c --- v2.1.57/linux/net/sunrpc/pmap_clnt.c Thu May 15 16:48:07 1997 +++ linux/net/sunrpc/pmap_clnt.c Sun Oct 12 10:17:46 1997 @@ -54,15 +54,16 @@ } clnt->cl_binding = 1; - task->tk_status = 0; - if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) { - task->tk_status = -EACCES; + task->tk_status = -EACCES; /* why set this? returns -EIO below */ + if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) goto bailout; - } - if (!(child = rpc_new_child(pmap_clnt, task))) { - rpc_destroy_client(pmap_clnt); + task->tk_status = 0; + + /* + * Note: rpc_new_child will release client after a failure. + */ + if (!(child = rpc_new_child(pmap_clnt, task))) goto bailout; - } /* Setup the call info struct */ rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0); diff -u --recursive --new-file v2.1.57/linux/net/sunrpc/sched.c linux/net/sunrpc/sched.c --- v2.1.57/linux/net/sunrpc/sched.c Tue Sep 23 16:48:50 1997 +++ linux/net/sunrpc/sched.c Sun Oct 12 10:17:46 1997 @@ -56,7 +56,8 @@ */ static struct wait_queue * rpciod_idle = NULL; static struct wait_queue * rpciod_killer = NULL; -static int rpciod_sema = 0; +static struct semaphore rpciod_sema = MUTEX; +static unsigned int rpciod_users = 0; static pid_t rpciod_pid = 0; static int rpc_inhibit = 0; @@ -575,19 +576,36 @@ current->pid); } +/* + * Create a new task for the specified client. We have to + * clean up after an allocation failure, as the client may + * have specified "oneshot". + */ struct rpc_task * rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags) { struct rpc_task *task; - if (!(task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task)))) - return NULL; + task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task)); + if (!task) + goto cleanup; rpc_init_task(task, clnt, callback, flags); dprintk("RPC: %4d allocated task\n", task->tk_pid); task->tk_flags |= RPC_TASK_DYNAMIC; +out: return task; + +cleanup: + /* Check whether to release the client */ + if (clnt) { + printk("rpc_new_task: failed, users=%d, oneshot=%d\n", + clnt->cl_users, clnt->cl_oneshot); + clnt->cl_users++; /* pretend we were used ... */ + rpc_release_client(clnt); + } + goto out; } void @@ -662,6 +680,9 @@ rpc_release_task(child); } +/* + * Note: rpc_new_task releases the client after a failure. + */ struct rpc_task * rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent) { @@ -715,11 +736,15 @@ unsigned long oldflags; int rounds = 0; + MOD_INC_USE_COUNT; lock_kernel(); + /* + * Let our maker know we're running ... + */ rpciod_pid = current->pid; + wake_up(&rpciod_idle); - MOD_INC_USE_COUNT; - /* exit_files(current); */ + exit_files(current); exit_mm(current); current->blocked |= ~_S(SIGKILL); current->session = 1; @@ -727,25 +752,28 @@ sprintf(current->comm, "rpciod"); dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid); - while (rpciod_sema) { + while (rpciod_users) { if (signalled()) { if (current->signal & _S(SIGKILL)) { rpciod_killall(); } else { printk("rpciod: ignoring signal (%d users)\n", - rpciod_sema); + rpciod_users); } current->signal &= current->blocked; } __rpc_schedule(); - if (++rounds >= 64) /* safeguard */ + if (++rounds >= 64) { /* safeguard */ schedule(); + rounds = 0; + } save_flags(oldflags); cli(); if (!schedq.task) { dprintk("RPC: rpciod back to sleep\n"); interruptible_sleep_on(&rpciod_idle); dprintk("RPC: switch to rpciod\n"); + rounds = 0; } restore_flags(oldflags); } @@ -780,26 +808,84 @@ } } -void +/* + * Start up the rpciod process if it's not already running. + */ +int rpciod_up(void) { - dprintk("rpciod_up pid %d sema %d\n", rpciod_pid, rpciod_sema); - if (!(rpciod_sema++) || !rpciod_pid) - kernel_thread(rpciod, &rpciod_killer, 0); + int error = 0; + + MOD_INC_USE_COUNT; + down(&rpciod_sema); + dprintk("rpciod_up: pid %d, users %d\n", rpciod_pid, rpciod_users); + rpciod_users++; + if (rpciod_pid) + goto out; + /* + * If there's no pid, we should be the first user. + */ + if (rpciod_users > 1) + printk("rpciod_up: no pid, %d users??\n", rpciod_users); + /* + * Create the rpciod thread and wait for it to start. + */ + error = kernel_thread(rpciod, &rpciod_killer, 0); + if (error < 0) { + printk("rpciod_up: create thread failed, error=%d\n", error); + goto out; + } + sleep_on(&rpciod_idle); + error = 0; +out: + up(&rpciod_sema); + MOD_DEC_USE_COUNT; + return error; } void rpciod_down(void) { - dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_sema); - if (--rpciod_sema > 0) - return; + unsigned long oldflags; + + MOD_INC_USE_COUNT; + down(&rpciod_sema); + dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_users); + if (rpciod_users) { + if (--rpciod_users) + goto out; + } else + printk("rpciod_down: pid=%d, no users??\n", rpciod_pid); + + if (!rpciod_pid) { + printk("rpciod_down: Nothing to do!\n"); + goto out; + } - rpciod_sema = 0; kill_proc(rpciod_pid, SIGKILL, 1); + /* + * Usually rpciod will exit very quickly, so we + * wait briefly before checking the process id. + */ + oldflags = current->signal; + current->signal = 0; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + 1; + schedule(); + current->timeout = 0; + /* + * Display a message if we're going to wait longer. + */ while (rpciod_pid) { - if (signalled()) - return; + printk("rpciod_down: waiting for pid %d to exit\n", rpciod_pid); + if (signalled()) { + printk("rpciod_down: caught signal\n"); + break; + } interruptible_sleep_on(&rpciod_killer); } + current->signal = oldflags; +out: + up(&rpciod_sema); + MOD_DEC_USE_COUNT; } diff -u --recursive --new-file v2.1.57/linux/net/sunrpc/svc.c linux/net/sunrpc/svc.c --- v2.1.57/linux/net/sunrpc/svc.c Mon Apr 7 11:35:33 1997 +++ linux/net/sunrpc/svc.c Sun Oct 12 10:17:46 1997 @@ -62,8 +62,12 @@ serv->sv_program->pg_name, serv->sv_nrthreads); - if (--(serv->sv_nrthreads) != 0) - return; + if (serv->sv_nrthreads) { + if (--(serv->sv_nrthreads) != 0) + return; + } else + printk("svc_destroy: no threads for serv=%p!\n", serv); + while ((svsk = serv->sv_allsocks) != NULL) svc_delete_socket(svsk); @@ -110,30 +114,31 @@ int svc_create_thread(svc_thread_fn func, struct svc_serv *serv) { - struct svc_rqst *rqstp = 0; - int error; + struct svc_rqst *rqstp; + int error = -ENOMEM; - if (!(rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL))) - return -ENOMEM; + rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL); + if (!rqstp) + goto out; memset(rqstp, 0, sizeof(*rqstp)); if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL)) - || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) { - error = -ENOMEM; - goto failure; - } + || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) + goto out_thread; serv->sv_nrthreads++; - if ((error = kernel_thread((int (*)(void *)) func, rqstp, 0)) < 0) - goto failure; - rqstp->rq_server = serv; - return 0; + error = kernel_thread((int (*)(void *)) func, rqstp, 0); + if (error < 0) + goto out_thread; + error = 0; +out: + return error; -failure: +out_thread: svc_exit_thread(rqstp); - return error; + goto out; } /* @@ -152,7 +157,8 @@ kfree(rqstp); /* Release the server */ - svc_destroy(serv); + if (serv) + svc_destroy(serv); } /* diff -u --recursive --new-file v2.1.57/linux/net/sunrpc/svcsock.c linux/net/sunrpc/svcsock.c --- v2.1.57/linux/net/sunrpc/svcsock.c Tue Sep 23 16:48:50 1997 +++ linux/net/sunrpc/svcsock.c Sun Oct 12 10:17:46 1997 @@ -743,14 +743,18 @@ if ((svsk = svc_sock_dequeue(serv)) != NULL) { enable_bh(NET_BH); rqstp->rq_sock = svsk; - svsk->sk_inuse++; + svsk->sk_inuse++; /* N.B. where is this decremented? */ } else { /* No data pending. Go to sleep */ rqstp->rq_sock = NULL; rqstp->rq_wait = NULL; svc_serv_enqueue(serv, rqstp); - current->state = TASK_UNINTERRUPTIBLE; + /* + * We have to be able to interrupt this wait + * to bring down the daemons ... + */ + current->state = TASK_INTERRUPTIBLE; add_wait_queue(&rqstp->rq_wait, &wait); enable_bh(NET_BH); schedule(); @@ -762,6 +766,7 @@ } } +printk("svc_recv: svsk=%p, use count=%d\n", svsk, svsk->sk_inuse); dprintk("svc: server %p servicing socket %p\n", rqstp, svsk); len = svsk->sk_recvfrom(rqstp);