]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.58 2.1.58
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:56 +0000 (15:13 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:56 +0000 (15:13 -0500)
50 files changed:
CREDITS
Makefile
arch/i386/mm/fault.c
fs/bad_inode.c
fs/binfmt_misc.c
fs/dcache.c
fs/file_table.c
fs/lockd/clntproc.c
fs/lockd/svc.c
fs/locks.c
fs/namei.c
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/proc.c
fs/proc/array.c
fs/proc/base.c
fs/proc/generic.c
fs/proc/inode.c
fs/proc/root.c
fs/smbfs/Makefile
fs/smbfs/cache.c [new file with mode: 0644]
fs/smbfs/dir.c
fs/smbfs/file.c
fs/smbfs/inode.c
fs/smbfs/proc.c
fs/smbfs/sock.c
fs/super.c
include/linux/dcache.h
include/linux/fs.h
include/linux/list.h
include/linux/mm.h
include/linux/nfs.h
include/linux/nfs_fs.h
include/linux/proc_fs.h
include/linux/smb_fs.h
include/linux/smb_fs_i.h
include/linux/smb_fs_sb.h
include/linux/sunrpc/sched.h
kernel/fork.c
kernel/ksyms.c
kernel/sysctl.c
mm/filemap.c
mm/vmscan.c
net/sunrpc/clnt.c
net/sunrpc/pmap_clnt.c
net/sunrpc/sched.c
net/sunrpc/svc.c
net/sunrpc/svcsock.c

diff --git a/CREDITS b/CREDITS
index fc418ef083b7a6ab4b8050e866ffb2e5d3285970..1caa14182c5ac69fa1e54876b63d5aea8658db29 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -632,8 +632,9 @@ S: USA
 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
index d4eb3e0423acfff794a1301e0039c5480ba36309..23f5d961c007539cb265eb8de732306c167c1b1b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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/)
 
index 8181fd6c786a1810db5fe06575dab6a0db6890f7..7021b2d4c3e8f4e9a37b91d67500587a3c41afae 100644 (file)
@@ -172,9 +172,10 @@ bad_area:
 
        /* 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;
index 562a5d8b7b7241d167453890c9297eb33c1678dd..4d17002ab53cc91f45f6a43378d077c98fab0ea5 100644 (file)
@@ -19,7 +19,7 @@ static struct dentry * bad_follow_link(struct inode * ino, struct dentry *base)
        return ERR_PTR(-EIO);
 }
 
-static int return_EIO()
+static int return_EIO(void)
 {
        return -EIO;
 }
@@ -81,3 +81,12 @@ void make_bad_inode(struct inode * inode)
        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); 
+}
index 469f52898ee9bc3344a1113e030c519260cbe3cb..8f8579d7c90c15e0d431ea4ba711b9e6ef124b29 100644 (file)
@@ -281,21 +281,19 @@ static int proc_write_register(struct file *file, const char *buffer,
        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 @@ static int proc_write_register(struct file *file, const char *buffer,
        /* 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 @@ static int proc_write_register(struct file *file, const char *buffer,
 
        err = count;
 _err:
-       MOD_DEC_USE_COUNT;
        return err;
+free_err:
+       kfree(e);
+       err = -EINVAL;
+       goto _err;
 }
 
 /*
@@ -357,7 +354,6 @@ static int proc_read_status(char *page, char **start, off_t off,
        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 @@ static int proc_read_status(char *page, char **start, off_t off,
        err = elen;
 
 _err:
-       MOD_DEC_USE_COUNT;
        return err;
 }
 
@@ -429,7 +424,6 @@ static int proc_write_status(struct file *file, const char *buffer,
        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 @@ static int proc_write_status(struct file *file, const char *buffer,
        } else {
                res = -EINVAL;
        }
-       MOD_DEC_USE_COUNT;
        return res;
 }
 
@@ -477,29 +470,57 @@ static int entry_proc_setup(struct binfmt_entry *e)
        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
index 600e5b0e3f8b739d60be62c78b915b2daa83602d..e661a4f5dcb57a51b19333b54825c05d3b8cd944 100644 (file)
@@ -61,37 +61,54 @@ static inline void d_free(struct dentry *dentry)
  */
 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 @@ repeat:
  * 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;
 
@@ -113,6 +131,27 @@ int d_invalidate(struct dentry * dentry)
        return 0;
 }
 
+/*
+ * 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
@@ -131,24 +170,65 @@ void prune_dcache(int count)
                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;
+       }
+}
+
 #define NAME_ALLOC_LEN(len)    ((len+16) & ~15)
 
 struct dentry * d_alloc(struct dentry * parent, const struct qstr *name)
index 42f9255d213a83838e1bfce5b035444261cf2941..1dc6bf3fd2de2f59f63627eabb18fdec6476f52f 100644 (file)
@@ -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 @@ void insert_file_free(struct file *file)
                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 @@ static inline void put_inuse(struct file *file)
        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 @@ void file_table_init(void)
                                       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 @@ struct file * get_empty_filp(void)
        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");
index d219de5ea0bdb8e8bdcb9db30048aeb1a8729fb0..c25a19c975cb7bc76d3d05aa30dfe1bfd467c71a 100644 (file)
@@ -160,8 +160,9 @@ static inline int
 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 @@ nlmclnt_alloc_call(void)
                                        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 @@ nlmclnt_call(struct nlm_rqst *req, u32 proc)
                /* 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;
index fc133b51ea1c22f1bf5d02e6d41da932004818a5..c5f77ef890fe437bb34774308b4349e8a6aa7908 100644 (file)
 
 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 @@ lockd(struct svc_rqst *rqstp)
        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 @@ lockd(struct svc_rqst *rqstp)
        /* 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 @@ lockd(struct svc_rqst *rqstp)
 
        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 @@ lockd(struct svc_rqst *rqstp)
                        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 @@ lockd(struct svc_rqst *rqstp)
 
        /* Release module */
        MOD_DEC_USE_COUNT;
-       nlmsvc_pid = 0;
 }
 
 /*
@@ -185,42 +209,100 @@ lockd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
        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;
 
-       dprintk("lockd: creating service\n");
-       if ((serv = svc_create(&nlmsvc_program, 0, NLMSVC_XDRSIZE)) == NULL)
-               return -ENOMEM;
+       /*
+        * 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);
+
+       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 @@ lockd_down(void)
 int
 init_module(void)
 {
+       /* Init the static variables */
+       nlmsvc_sema = MUTEX;
+       nlmsvc_users = 0;
+       nlmsvc_pid = 0;
+       lockd_exit = NULL;
        nlmxdr_init();
        return 0;
 }
index 27e23f77dfc35eb8bbdd3e3afa2f7ce04bef6644..f6804b6477b1037715b5a747af904514454e41c8 100644 (file)
@@ -881,9 +881,6 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
 
        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;
@@ -895,6 +892,9 @@ int posix_lock_file(struct file *filp, struct file_lock *caller,
                        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);
                        locks_delete_block(fl, caller);
index 54714754d0bf5b4368fa7540757f0d9f3085029c..68064e883bbf704e36786d04fed489cac6e18bf8 100644 (file)
@@ -232,11 +232,14 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
        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 @@ static inline int do_rename(const char * oldname, const char * newname)
 
        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);
index c05f792476442dcb54c158dcceb28884c9a43bf2..daa8d1ba7151fc6b05263fdb6025af4f872e76fd 100644 (file)
 
 #include <asm/segment.h>       /* 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.
 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 struct nfs_dirent  dircache[NFS_MAX_DIRCACHE];
 
 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 @@ again:
                        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 @@ again:
                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 @@ again:
        /*
         * 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 @@ done:
 }
 
 /*
- * 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 @@ static int nfs_lookup_revalidate(struct dentry * dentry)
        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 @@ static int nfs_lookup(struct inode *dir, struct dentry * dentry)
        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 @@ static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
        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 @@ static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
        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 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
        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 @@ static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rde
 
        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 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
                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 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
        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 @@ static void nfs_silly_delete(struct dentry *dentry)
                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 @@ static void nfs_silly_delete(struct dentry *dentry)
  *
  *  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 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
 
        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 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
        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 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
        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 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        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 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
  * 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 @@ void nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
                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;
 }
 
 /*
index e91b34a342626f60af70addda070fec3cc953860..2b950eeb8ffddae5a6c85628a3a4d907dec71ae1 100644 (file)
@@ -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 @@ nfs_read_inode(struct inode * inode)
 {
        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 @@ static void
 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 @@ nfs_put_super(struct super_block *sb)
        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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
        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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
                        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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
        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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
        /* 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 @@ nfs_read_super(struct super_block *sb, void *raw_data, int silent)
        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;
 }
 
index 5eec5eb65e0d9f76e8fd2027b0bd33748942cf1c..0311b7d0b71bf3cd85a74d96e24d4b4f233ff52f 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/nfs_fs.h>
 
 #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 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
  * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
  * After decoding, the layout in memory looks like this:
  *     entry1 entry2 ... entryN <space> 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 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
        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 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
                        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;
 }
 
index f48a6217c623bdf8c4d2994c0fde0cb76428139c..94096d928a76857333285b38efdc932d8542452e 100644 (file)
@@ -384,17 +384,18 @@ nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
  * to avoid a malloc of NFS_MAXNAMLEN+1 for each file name.
  * After decoding, the layout in memory looks like this:
  *     entry1 entry2 ... entryN <space> 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 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
                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 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
                        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;
 }
 
index 58dcd95d07e0f956cdeb84db54d6efe0cf663499..416ed294eb1b4dab94946ad4618c60fece5d4b6c 100644 (file)
@@ -250,25 +250,43 @@ nfs_proc_rmdir(struct nfs_server *server, struct nfs_fh *dir, const char *name)
  */
 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 @@ nfs_proc_readdir(struct nfs_server *server, struct nfs_fh *fhandle,
        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;
 }
 
index 5211ad4da048fb4430b7d9bb7977b893e2823766..a6921cbfe74401d0b610f945ed5eec17bfdffd7d 100644 (file)
@@ -348,6 +348,12 @@ static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr)
 
        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 @@ static int get_statm(int pid, char * buffer)
 #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 @@ static long read_maps (int pid, struct file * file,
        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 @@ static long read_maps (int pid, struct file * file,
                        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 @@ static long read_maps (int pid, struct file * file,
                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 @@ static long read_maps (int pid, struct file * file,
                /* 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
index 7e9a65e088e7fcaccc090ff51d6a95feec6ffc07..ac6704e4bc7d3405961bebd923678b9bc9bf9fa2 100644 (file)
@@ -50,13 +50,17 @@ static struct inode_operations proc_base_inode_operations = {
        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;
index 83139f492700a35fa73939fb331211a373376fe2..4774cf8efe09e9a90419046cad7f293f6712bfb2 100644 (file)
 #include <linux/stat.h>
 #include <asm/bitops.h>
 
+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 @@ static struct file_operations proc_file_operations = {
     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 @@ static int xlate_proc_name(const char *name,
 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;
-
-       if (parent)
-               fn = name;
-       else {
-               if (xlate_proc_name(name, &parent, &fn))
-                       return NULL;
-       }
+       struct proc_dir_entry *ent = NULL;
+       const char *fn = name;
+       int len;
+
+       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;
 }
index 4bc4a4f1765b63542563e9a52654092ae911f757..c336166049d0fabf0edbdac06b6439e5315eca76 100644 (file)
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+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 @@ static struct super_operations proc_sops = {
        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 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid)
        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 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e
            && 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 @@ struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_e
                }
                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)
index 3f7d0cfeef110b5115438a65b312b477ae5e667f..c72c064835f5ed9dfda308a1574b4f5bdd14e537 100644 (file)
@@ -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};
 
@@ -72,6 +73,29 @@ struct inode_operations proc_dir_inode_operations = {
        NULL                    /* permission */
 };
 
+/*
+ * /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
  * <pid> directories. Thus we don't use the generic
@@ -173,7 +197,8 @@ proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, fil
 
 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 @@ proc_openprom_defreaddir(struct inode * inode, struct file * filp,
                                (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 @@ proc_openprom_deflookup(struct inode * dir, struct dentry *dentry)
                                (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 @@ static struct file_operations proc_openprom_operations = {
 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 */
@@ -638,6 +660,26 @@ void proc_root_init(void)
 #endif
 }
 
+/*
+ * 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 @@ int proc_lookup(struct inode * dir, struct dentry *dentry)
 {
        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 @@ int proc_lookup(struct inode * dir, struct dentry *dentry)
                                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 @@ static int proc_root_lookup(struct inode * dir, struct dentry * dentry)
                if (!inode)
                        return -EINVAL;
        }
+
+       dentry->d_op = &proc_dentry_operations;
        d_add(dentry, inode);
        return 0;
 }
@@ -827,3 +876,15 @@ static int proc_root_readdir(struct file * filp,
        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;
+}
index 3d9588e6dfc1a736175250eab9144c87c1d80417..202cc5932b2d2e4c6bf0ee9b634453f095d3f2b5 100644 (file)
@@ -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 --git a/fs/smbfs/cache.c b/fs/smbfs/cache.c
new file mode 100644 (file)
index 0000000..7e2e7f0
--- /dev/null
@@ -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 <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/dirent.h>
+#include <linux/smb_fs.h>
+
+#include <asm/page.h>
+
+#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;
+}
+
index 3196e90093a2285ef6e2d19b1c122be402cf2989..680a91c3e26f13016a9a5d7a5a311b881fa67993 100644 (file)
 /* #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 @@ static struct file_operations smb_dir_operations =
        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 @@ struct inode_operations smb_dir_inode_operations =
        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 @@ smb_dir_read(struct inode *inode, struct file *filp, char *buf,
 }
 
 /*
- * This is the callback from dput().  We close the file so that
- * cached dentries don't keep the file open.
- */
-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)
-{
-       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?
+ * Compute the hash for a qstr.
+ * N.B. Move to include/linux/dcache.h?
  */
-static unsigned int hash_it(const char * name, unsigned int len)
+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)
-       {
-               /*
-                * 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;
+       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)
        {
-               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 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
 {
        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)
+       while (1)
        {
-               /* 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)
+               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)
+       {
+               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
        {
-               c_sb = NULL;
-               c_ino = 0;
-               c_seen_eof = 0;
+       /*
+        * 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 @@ d_entry->d_parent->d_name.name, d_entry->d_name.name, error);
                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 @@ out:
  * 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 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
          * 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 @@ smb_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        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 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
        int error;
 
        error = -ENAMETOOLONG;
-       if (dentry->d_name.len > NFS_MAXNAMLEN)
+       if (dentry->d_name.len > SMB_MAXNAMELEN)
                goto out;
 
        /*
@@ -468,11 +425,12 @@ smb_rmdir(struct inode *dir, struct dentry *dentry)
         */
        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 @@ smb_unlink(struct inode *dir, struct dentry *dentry)
         */
        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 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry,
 {
        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 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry,
        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 @@ smb_rename(struct inode *old_dir, struct dentry *old_dentry,
         */
        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 @@ printk("smb_rename: after unlink error=%d\n", error);
         */
        if (!error)
        {
+               smb_renew_times(old_dentry);
+               smb_renew_times(new_dentry->d_parent);
                d_move(old_dentry, new_dentry);
        }
 out:
index 9653a7be6e3f2738c54aa801f11afacbad7a0117..7388ee31f2739104e3e1612a2bdc53fb96b7203e 100644 (file)
 /* #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 @@ smb_fsync(struct file *file, struct dentry * dentry)
 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 @@ smb_readpage_sync(struct inode *inode, struct page *page)
                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 @@ dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize);
        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 @@ smb_readpage(struct inode *inode, struct page *page)
        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 @@ static int
 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 @@ printk("smb_writepage_sync: short write, wsize=%d, result=%d\n", wsize, result);
        } 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 @@ smb_writepage(struct inode *inode, struct page *page)
        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 @@ static int
 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 @@ smb_updatepage(struct inode *inode, struct page *page, const char *buffer,
        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 @@ smb_file_read(struct inode * inode, struct file * file,
 {
        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 @@ smb_file_mmap(struct file * file, struct vm_area_struct * vma)
        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 @@ smb_file_write(struct inode *inode, struct file *file,
 {
        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 @@ static struct file_operations smb_file_operations =
        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 =
index 9203650e25deffc2ddbbaedb5f81b634feb02391..45ac97a31b68ffb7653f935ef9cd8a6cea36bfa4 100644 (file)
@@ -6,6 +6,8 @@
  *
  */
 
+#define SMBFS_DCACHE_EXT 1
+
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
+#include <linux/dcache.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#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 @@ smb_invent_inos(unsigned long n)
        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 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
        down(&read_semaphore);
        read_fattr = fattr;
        result = iget(sb, fattr->f_ino);
+       read_fattr = NULL;
        up(&read_semaphore);
        return result;
 }
@@ -87,7 +93,6 @@ smb_iget(struct super_block *sb, struct smb_fattr *fattr)
 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 @@ smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
        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 @@ smb_read_inode(struct inode *inode)
 {
        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 @@ smb_read_inode(struct inode *inode)
 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 @@ out:
 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 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
        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 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
        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 @@ smb_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 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 @@ smb_notify_change(struct inode *inode, struct iattr *attr)
                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 @@ smb_notify_change(struct inode *inode, struct iattr *attr)
                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 @@ init_module(void)
        smb_current_vmalloced = 0;
 #endif
 
-       smb_init_dir_cache();
        read_semaphore = MUTEX;
 
        return init_smb_fs();
@@ -471,7 +577,6 @@ void
 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);
index 57b4be6fbec16ce87c6f1ee254706c0084f8c8f1..fb310adcfbfe786fd4ef1145327a1ece6a935192 100644 (file)
@@ -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 <linux/config.h>
@@ -16,6 +18,8 @@
 #include <linux/malloc.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/dcache.h>
+#include <linux/dirent.h>
 
 #include <asm/uaccess.h>
 #include <asm/string.h>
@@ -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 @@ str_lower(char *name)
        }
 }
 
+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 @@ smb_encode_smb_length(__u8 * p, __u32 len)
 }
 
 /*
- * 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 @@ static char *smb_encode_path(struct smb_sb_info *server, char *buf,
 {
        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 @@ smb_verify(__u8 * packet, int command, int wct, int bcc)
                (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 @@ smb_unlock_server(struct smb_sb_info *server)
        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 @@ smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
 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 @@ smb_retry(struct smb_sb_info *server)
                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 @@ smb_offerconn(struct smb_sb_info *server)
        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 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
 
        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 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
        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 @@ smb_setup_header(struct smb_sb_info * server, __u8 command, __u16 wct, __u16 bcc
        __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 @@ smb_setup_bcc(struct smb_sb_info *server, __u8 * p)
 }
 
 /*
- * 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 @@ out:
 
 /* 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 @@ smb_close(struct inode *ino)
        {
                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 @@ smb_proc_read(struct inode *ino, off_t offset, long count, char *data)
        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 @@ dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet);
        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 @@ int
 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 @@ smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
        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_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
        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)
+       len = strlen(p + 9);
+       if (len > 12)
        {
-               /* Pathworks fills names with spaces */
-               entry->len -= 1;
-               if (entry->name[entry->len] == ' ')
-               {
-                       entry->name[entry->len] = '\0';
-               }
+               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 @@ smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
 
 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_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
 
                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 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
 
                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 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
 
        __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 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
                        entries = -EIO;
                        break;
                }
+
                if (first != 0)
                {
                        command = TRANSACT2_FINDFIRST;
@@ -1314,10 +1509,11 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
                } 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 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
                }
 
                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 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
                {
                        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 @@ printk("smb_proc_readdir_long: trans2_request error=%d\n", result);
                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 @@ server->rcls, server->err);
                {
                        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 @@ static int
 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 @@ smb_proc_getattr_trans2(struct smb_sb_info *server, struct dentry *dir,
        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",
+&param[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 @@ smb_proc_getattr(struct dentry *dir, struct qstr *name,
                     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 @@ static int
 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 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
        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 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
        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 @@ int
 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;
index aceca122b9e698a1d1f83552019935e4f39297c2..88113f97b7dce1a7312d6ab2270ed03a3ea179cf 100644 (file)
@@ -126,18 +126,26 @@ smb_data_callback(struct sock *sk, int len)
        }
 }
 
+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 @@ smb_close_socket(struct smb_sb_info *server)
 
        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 @@ smb_get_length(struct socket *socket, unsigned char *header)
 
        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 @@ smb_get_length(struct socket *socket, unsigned char *header)
                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 @@ static int
 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 @@ smb_receive(struct smb_sb_info *server)
 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 @@ data_len, total_data, param_len, total_param);
        *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 @@ smb_request(struct smb_sb_info *server)
        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 @@ out:
        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 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
        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 @@ smb_send_trans2(struct smb_sb_info *server, __u16 trans2_command,
 
        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 @@ out:
        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;
index 8fc3409851aff6d1478a455e776961b05a617747..5af01183063d920cb7624f0b9afe1513cc47f3b0 100644 (file)
@@ -17,8 +17,6 @@
  *  Added change_root: Werner Almesberger & Hans Lermen, Feb '96
  */
 
-#include <stdarg.h>
-
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -252,29 +250,24 @@ static int fs_maxindex(void)
 /*
  * 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 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
        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 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
                free_page(page);
                goto out;
        }
+
        retval = copy_mount_options (type, &page);
        if (retval < 0)
                goto out;
@@ -962,8 +955,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
        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 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
                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 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
        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))
index ebdf22bfcdd0308db8554b311b5efaa4e7f78d02..7654b2f16f274aa02cd6c92546318d6958964550 100644 (file)
@@ -104,6 +104,7 @@ extern void d_delete(struct dentry *);
 /* 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)
index 4fd14cf7aadfae44518ce05318240f8fbe2cd37d..c9c985ff575f56f18f7f16bc0ae74cd9ba7559df 100644 (file)
 
 /* 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
@@ -628,6 +629,10 @@ extern struct inode_operations chrdev_inode_operations;
 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;
 extern struct file_operations write_fifo_fops;
index 84a0ecef628bb035eb63765affe562a0755767f8..00b8793103b05340bc7e1f97bd12db3b36d3fb35 100644 (file)
@@ -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,24 +22,48 @@ struct list_head {
        (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)
 {
        return head->next == head;
index d02a86fefc84f0b54dd72ed44f18804b8477eafc..6d67c9505c6bb6b950e2ee4ee621b86681877dff 100644 (file)
@@ -296,6 +296,8 @@ extern unsigned long get_unmapped_area(unsigned long, unsigned long);
 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
index ea58d7b81d5e76b3dd2d6693cdec6b1f70401027..c8ea8ffdf632e0d3008376b3814bf317583f9171 100644 (file)
@@ -129,14 +129,6 @@ struct nfs_sattr {
        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;
index 9591046ca0c135a9cd34ae5033ed0fcafcb5ae81..c74ad05fb46a3cef711af9310f643275f401f183 100644 (file)
@@ -125,7 +125,7 @@ extern int nfs_proc_mkdir(struct nfs_server *server, struct nfs_fh *dir,
 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 struct super_block *nfs_read_super(struct super_block *sb,
 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 *);
 
index de812336680708784ee78e5a56ab0f3ccb77ae86..2f84b986416ba40c686abd7e10edff72f0fbca9d 100644 (file)
@@ -237,13 +237,15 @@ struct proc_dir_entry {
        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,
index e3e9ea98ab9df7bb55b3a5caa224df852345c372..9f25819bef7f687c43893e6298694e8abd24a5ba 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef _LINUX_SMB_FS_H
 #define _LINUX_SMB_FS_H
 
+#include <linux/dirent.h>
 #include <linux/smb.h>
 
 /*
@@ -71,30 +72,24 @@ extern struct inode_operations smb_file_inode_operations;
 
 /* 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 @@ __u8 *smb_setup_header(struct smb_sb_info *server, __u8 command,
 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 @@ smb_is_open(struct inode *i)
        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,
@@ -151,6 +144,72 @@ int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
 /* 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__ */
 
 #endif /* _LINUX_SMB_FS_H */
index 73ea69a89986a5f009774f2268380cc90451f600..954434b3df8ed0e21836614557f4ab2059be1f42 100644 (file)
@@ -22,11 +22,13 @@ struct smb_inode_info {
         * (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
index 72b805e60dda8a3add1581ae51f4be6620bdb63f..95d3cc03dd6f6fae2c1627c179f9ec7dda237705 100644 (file)
 #include <linux/smb.h>
 #include <linux/smb_mount.h>
 
+/* 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_sb_info {
        struct smb_conn_opt opt;
 
        struct semaphore sem;
+       struct wait_queue * wait;
 
        __u32              packet_size;
        unsigned char *    packet;
index 759da3c704740e19646d084433bd634a41cc1ce6..715abce2d3b9a99d41972cb1e9e5f64370f683ed 100644 (file)
@@ -143,7 +143,7 @@ void                rpc_del_timer(struct rpc_task *);
 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 *
index 6ed9eb8c0a3fcc87c3e42f10250780547d750778..116e0278649e64752cc3ff5593dde5542915df91 100644 (file)
@@ -208,7 +208,6 @@ static inline int dup_mmap(struct mm_struct * mm)
        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 @@ static inline int dup_mmap(struct mm_struct * mm)
                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 @@ struct mm_struct * mm_alloc(void)
                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 @@ static inline int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
                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 @@ static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk
        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 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
        }
        ++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 @@ bad_fork_cleanup:
        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)
index cb1f3be8e84377df30fac5648dfbaf74a25c8dfc..84f8bbf90adf3ef2f9a8f39b9cb9b68a1a5dd5c8 100644 (file)
@@ -192,6 +192,8 @@ EXPORT_SYMBOL(posix_test_lock);
 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(read_ahead);
 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);
index 205190e9a343bad47600e78bf2d18bac4dba46bf..5c5231abed8ad6eb2b701a3e3126186a3d8b4ebe 100644 (file)
@@ -147,7 +147,7 @@ static ctl_table kern_table[] = {
         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},
index 24a8943ad78f6910e02a24b8c3a2d27fe5f8663a..dab0bcbdb8e396f73f2702892f7338152f541f65 100644 (file)
@@ -1398,3 +1398,60 @@ done_with_page:
                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);
+}
index 570a86f7c391bc855d646d01cbe6bea444f9dd9f..6016620f8ef69e644c3b691ec9d832ec2376fc02 100644 (file)
@@ -24,9 +24,6 @@
 #include <linux/smp_lock.h>
 #include <linux/slab.h>
 
-#include <asm/dma.h>
-#include <asm/system.h> /* for cli()/sti() */
-#include <asm/uaccess.h> /* for copy_to/from_user */
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
@@ -418,6 +415,7 @@ void kswapd_setup(void)
        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 @@ int kswapd(void *unused)
        init_swap_timer();
        
        while (1) {
+               int fail;
+
                kswapd_awake = 0;
                current->signal = 0;
                run_task_queue(&tq_disk);
@@ -455,13 +455,27 @@ int kswapd(void *unused)
                 * 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);
        }
 }
 
index cb9984ac7fa5c2ca163f4a6728111e9868cea778..ce3cd7bd00e2a6cea3e14c0abb3086d0b4c2df8a 100644 (file)
@@ -116,17 +116,23 @@ rpc_create_client(struct rpc_xprt *xprt, char *servname,
 
 /*
  * 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 @@ rpc_release_client(struct rpc_clnt *clnt)
 {
        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 @@ rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
        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 @@ rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
        } 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 @@ call_allocate(struct rpc_task *task)
 
        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);
index 4a05efd9ca7fd9adf9c513f0b37702136c88a058..2bbe9d50a68d8a5f9fd2e92d17e68d4eace2521b 100644 (file)
@@ -54,15 +54,16 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
        }
        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);
index fb02640f9990e400c28576bb824eb1b974b3d192..8e2d5868c14621a1a72e6c06750ef2fabf8757d4 100644 (file)
@@ -56,7 +56,8 @@ static struct rpc_task *      all_tasks = NULL;
  */
 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 @@ rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
                                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_child_exit(struct rpc_task *child)
        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 @@ rpciod(void *ptr)
        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 @@ rpciod(void *ptr)
        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 @@ rpciod_killall(void)
        }
 }
 
-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;
 }
index 55282366fa34248cc77fd841b3d971788a928ad9..17376ef767ca366ebfe5f13fed7ed8bc2143b502 100644 (file)
@@ -62,8 +62,12 @@ svc_destroy(struct svc_serv *serv)
                                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 @@ svc_release_buffer(struct svc_buf *bufp)
 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 @@ svc_exit_thread(struct svc_rqst *rqstp)
        kfree(rqstp);
 
        /* Release the server */
-       svc_destroy(serv);
+       if (serv)
+               svc_destroy(serv);
 }
 
 /*
index 87a5ed82efcb6449d56c093fb7817d26e066996d..c04481d3e0d35db3dc74dd72998fac612fa5fade 100644 (file)
@@ -743,14 +743,18 @@ again:
        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 @@ again:
                }
        }
 
+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);