]> git.neil.brown.name Git - history.git/commitdiff
Import 2.1.61 2.1.61
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:59 +0000 (15:13 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:13:59 +0000 (15:13 -0500)
33 files changed:
CREDITS
MAINTAINERS
Makefile
arch/i386/defconfig
fs/Config.in
fs/autofs/root.c
fs/dcache.c
fs/isofs/dir.c
fs/isofs/inode.c
fs/namei.c
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
fs/nfs/write.c
fs/nls/Config.in
fs/nls/Makefile
fs/nls/nls_base.c
fs/smbfs/cache.c
fs/smbfs/dir.c
fs/smbfs/inode.c
fs/smbfs/ioctl.c
fs/smbfs/proc.c
fs/smbfs/sock.c
fs/vfat/namei.c
include/asm-alpha/socket.h
include/linux/dcache.h
include/linux/nfs_fs.h
include/linux/posix_types.h
include/linux/smb_fs.h
include/linux/smb_fs_sb.h
mm/mmap.c
mm/swap_state.c
mm/swapfile.c

diff --git a/CREDITS b/CREDITS
index 725edcf4c6e9487b0ab3642a6b5d7ce902a07ddb..03a596513a1ac7a126494bb6dac02aef7b0ad464 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -215,7 +215,7 @@ S: Fremont, California 94539
 S: USA
 
 N: Gordon Chaffee
-E: chaffee@bmrc.berkeley.edu
+E: chaffee@cs.berkeley.edu
 W: http://bmrc.berkeley.edu/people/chaffee/
 D: vfat, fat32, joliet, native language support
 S: 3674 Oakwood Terrace #201
index cad66c81389000872a5b11bbc42fafc565258427..5f22e1bbefcf0990cd05bbf43f4d0c7bb52bd9a7 100644 (file)
@@ -368,9 +368,9 @@ S:  Maintained
 
 VFAT FILESYSTEM:
 P:     Gordon Chaffee
-M:     chaffee@plateau.cs.berkeley.edu
+M:     chaffee@cs.berkeley.edu
 L:     linux-kernel@vger.rutgers.edu
-W:     http://www-plateau.cs.berkeley.edu/people/chaffee
+W:     http://bmrc.berkeley.edu/people/chaffee
 S:     Maintained
 
 DIGI INTL. EPCA DRIVER:
index f2eb76cbaee6f7e15568e5bad45a3f0714c008c1..73c503a7a9c4b4ae4ed801a8dfe0e2a33786c41e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 60
+SUBLEVEL = 61
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
index d4118f63ffccbe6e9f3b15d15588582d5d69a18f..cc151a3cc37cf5e2285c4c232b7ee3766b240538 100644 (file)
@@ -198,10 +198,12 @@ CONFIG_EEXPRESS_PRO100=y
 # CONFIG_QUOTA is not set
 # CONFIG_MINIX_FS is not set
 CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
 # CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_NFS_FS=y
 # CONFIG_ROOT_NFS is not set
@@ -209,7 +211,6 @@ CONFIG_NFS_FS=y
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
 # CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
 # CONFIG_HPFS_FS is not set
 # CONFIG_SYSV_FS is not set
 # CONFIG_AFFS_FS is not set
@@ -218,6 +219,11 @@ CONFIG_AUTOFS_FS=y
 # CONFIG_UFS_FS is not set
 # CONFIG_MAC_PARTITION is not set
 
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
 #
 # Character devices
 #
index 88e9c56265f270cbc3dc477ff4fe64fabc71539a..5915c2583913ed47e1f2397733a946c46f408693 100644 (file)
@@ -10,19 +10,16 @@ tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
 
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
-if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
-  if [ "$CONFIG_ISO9660_FS" = "y" -o "$CONFIG_ISO9660_FS" = "m" ]; then
-    bool 'Microsoft Joliet cdrom extensions' CONFIG_JOLIET
-  fi
-
-  # msdos filesystems
-  dep_tristate 'DOS FAT fs support' CONFIG_FAT_FS $CONFIG_NLS
-  dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
-  dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
-  dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+if [ "$CONFIG_ISO9660_FS" != "n" ]; then
+  bool 'Microsoft Joliet cdrom extensions' CONFIG_JOLIET
 fi
 
+# msdos filesystems
+tristate 'DOS FAT fs support' CONFIG_FAT_FS
+dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+
 bool '/proc filesystem support' CONFIG_PROC_FS
 if [ "$CONFIG_INET" = "y" ]; then
   tristate 'NFS filesystem support' CONFIG_NFS_FS
index 919e61b7662131b266c1add9abd7f876ee7fa803..541f7fba38a2d8634f44fb3b523d06ce5a044bbe 100644 (file)
@@ -100,7 +100,7 @@ static int autofs_root_readdir(struct file *filp, void *dirent, filldir_t filldi
        return 0;
 }
 
-static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi)
+static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi)
 {
        struct inode * inode;
        struct autofs_dir_ent *ent;
@@ -132,9 +132,10 @@ static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, s
                dentry->d_inode = inode;
        }
 
-       if (S_ISDIR(dentry->d_inode->i_mode)) {
-               while (dentry == dentry->d_mounts)
-                       schedule();
+       /* If this is a directory that isn't a mount point, bitch at the
+          daemon and fix it in user space */
+       if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) {
+               return !autofs_wait(sbi, &dentry->d_name);
        }
 
        autofs_update_usage(&sbi->dirhash,ent);
@@ -159,16 +160,24 @@ static int autofs_revalidate(struct dentry * dentry)
        sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
 
        /* Pending dentry */
-       if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
+       if ( dentry->d_flags & DCACHE_AUTOFS_PENDING ) {
                if (autofs_oz_mode(sbi))
                        return 1;
-
-               return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+               else
+                       return try_to_fill_dentry(dentry, dir->i_sb, sbi);
        }
 
        /* Negative dentry.. invalidate if "old" */
        if (!dentry->d_inode)
                return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
+               
+       /* Check for a non-mountpoint directory */
+       if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) {
+               if (autofs_oz_mode(sbi))
+                       return 1;
+               else
+                       return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+       }
 
        /* Update the usage list */
        ent = (struct autofs_dir_ent *) dentry->d_time;
@@ -177,7 +186,7 @@ static int autofs_revalidate(struct dentry * dentry)
 }
 
 static struct dentry_operations autofs_dentry_operations = {
-       autofs_revalidate,
+       autofs_revalidate,      /* d_revalidate */
        NULL,                   /* d_hash */
        NULL,                   /* d_compare */
 };
index 3887b6be6c3318963c0f265ed837ad01923be0bc..bf312c1b204107595c2c145457c87430b6d7a540 100644 (file)
@@ -465,12 +465,13 @@ static inline struct list_head * d_hash(struct dentry * parent, unsigned long ha
        return dentry_hashtable + (hash & D_HASHMASK);
 }
 
-static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name)
+struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
 {
-       struct list_head *tmp = head->next;
-       int len = name->len;
-       int hash = name->hash;
+       unsigned int len = name->len;
+       unsigned int hash = name->hash;
        const unsigned char *str = name->name;
+       struct list_head *head = d_hash(parent,hash);
+       struct list_head *tmp = head->next;
 
        while (tmp != head) {
                struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
@@ -489,16 +490,11 @@ static inline struct dentry * __dlookup(struct list_head *head, struct dentry *
                        if (memcmp(dentry->d_name.name, str, len))
                                continue;
                }
-               return dget(dentry->d_mounts);
+               return dget(dentry);
        }
        return NULL;
 }
 
-struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
-{
-       return __dlookup(d_hash(dir, name->hash), dir, name);
-}
-
 /*
  * An insecure source has sent us a dentry, here we verify it.
  *
index 1d9a4f341115b7098dbf821ec1474e9d98274fd9..67ff77bd991fc3dc09eed8e4cc56008ddb8c4b80 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/locks.h>
+#include <linux/config.h>
 
 #include <asm/uaccess.h>
 
@@ -206,10 +207,13 @@ static int do_isofs_readdir(struct inode *inode, struct file *filp,
                        }
                }
 
+#ifdef CONFIG_JOLIET
                if (inode->i_sb->u.isofs_sb.s_joliet_level) {
                        len = get_joliet_filename(de, inode, tmpname);
                        p = tmpname;
-               } else {
+               } else 
+#endif
+               /* if not joliet */ {
                        map = 1;
                        if (inode->i_sb->u.isofs_sb.s_rock) {
                                len = get_rock_ridge_filename(de, tmpname, inode);
index 12c5bf9ad722c261ffe347f93ded9bd3a7702a2e..b77e9facdde9501c33d4e64807b393f0ba0d5e4c 100644 (file)
@@ -89,8 +89,7 @@ struct iso9660_options{
 
 static int parse_options(char *options, struct iso9660_options * popt)
 {
-       char *this_char,*value,*p;
-       int len;
+       char *this_char,*value;
 
        popt->map = 'n';
        popt->rock = 'y';
@@ -135,6 +134,9 @@ static int parse_options(char *options, struct iso9660_options * popt)
 
 #ifdef CONFIG_JOLIET
                if (!strcmp(this_char,"iocharset")) {
+                       char *p;
+                       int len;
+
                        p = value;
                        while (*value && *value != ',') value++;
                        len = value - p;
@@ -275,7 +277,6 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
        int                             joliet_level = 0;
        struct iso9660_options          opt;
        int                             orig_zonesize;
-       char                          * p;
        struct iso_primary_descriptor * pri = NULL;
        struct iso_directory_record   * rootp;
        struct iso_supplementary_descriptor *sec = NULL;
@@ -413,8 +414,10 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
            MOD_DEC_USE_COUNT;
            return NULL;
        }
-#ifdef CONFIG_JOLIET
+
        s->u.isofs_sb.s_joliet_level = joliet_level;
+
+#ifdef CONFIG_JOLIET
        if (joliet_level) {
            /* Note: In theory, it is possible to have Rock Ridge
             * extensions mixed with Joliet. All character strings
@@ -549,6 +552,7 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
                        opt.iocharset = NULL;
                }
        } else if (opt.utf8 == 0) {
+               char * p;
                p = opt.iocharset ? opt.iocharset : "iso8859-1";
                s->u.isofs_sb.s_nls_iocharset = load_nls(p);
                if (! s->u.isofs_sb.s_nls_iocharset) {
index fc90b3e83c32a57e7839c08585985e635e44c8be..1c9c4cdf557ea75b8935abadd78c32440d65d9b9 100644 (file)
@@ -237,8 +237,7 @@ static struct dentry * real_lookup(struct dentry * parent, struct qstr * name)
                        int error = dir->i_op->lookup(dir, dentry);
                        result = ERR_PTR(error);
                        if (!error)
-                               result = dget(dentry->d_mounts);
-                       dput(dentry);
+                               result = dentry;
                }
        }
        up(&dir->i_sem);
@@ -293,25 +292,6 @@ static struct dentry * reserved_lookup(struct dentry * parent, struct qstr * nam
        return dget(result);
 }
 
-/* In difference to the former version, lookup() no longer eats the dir. */
-static inline struct dentry * lookup(struct dentry * dir, struct qstr * name)
-{
-       struct dentry * result;
-
-       result = reserved_lookup(dir, name);
-       if (result)
-               goto done;
-
-       result = cached_lookup(dir, name);
-       if (result)
-               goto done;
-
-       result = real_lookup(dir, name);
-
-done:
-       return result;
-}
-
 static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
 {
        struct inode * inode = dentry->d_inode;
@@ -334,6 +314,18 @@ static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry
        return dentry;
 }
 
+static inline struct dentry * follow_mount(struct dentry * dentry)
+{
+       struct dentry * mnt = dentry->d_mounts;
+
+       if (mnt != dentry) {
+               dget(mnt);
+               dput(dentry);
+               dentry = mnt;
+       }
+       return dentry;
+}
+
 /*
  * Name resolution.
  *
@@ -415,9 +407,19 @@ struct dentry * lookup_dentry(const char * name, struct dentry * base, int follo
                        }
                }
 
-               dentry = lookup(base, &this);
-               if (IS_ERR(dentry))
-                       break;
+               /* This does the actual lookups.. */
+               dentry = reserved_lookup(base, &this);
+               if (!dentry) {
+                       dentry = cached_lookup(base, &this);
+                       if (!dentry) {
+                               dentry = real_lookup(base, &this);
+                               if (IS_ERR(dentry))
+                                       break;
+                       }
+               }
+
+               /* Check mountpoints.. */
+               dentry = follow_mount(dentry);
 
                if (!follow)
                        break;
index 8b0be8cdc2da219f453164513e280627e615042b..f830c02c5d400d276c0d52c9aefe6a33e7eb2d93 100644 (file)
@@ -333,7 +333,6 @@ nfs_invalidate_dircache_sb(struct super_block *sb)
 {
        struct nfs_dirent *cache = dircache;
        int             i;
-       int             freed = 0;
 
        for (i = NFS_MAX_DIRCACHE; i--; cache++) {
                if (sb && sb->s_dev != cache->dev)
@@ -347,14 +346,8 @@ nfs_invalidate_dircache_sb(struct super_block *sb)
                if (cache->entry) {
                        free_page((unsigned long) cache->entry);
                        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
 }
 
 /*
@@ -472,9 +465,9 @@ static int nfs_instantiate(struct inode *dir, struct dentry *dentry,
        struct inode *inode;
        int error = -EACCES;
 
+       nfs_invalidate_dircache(dir);
        inode = nfs_fhget(dir->i_sb, fhandle, fattr);
        if (inode) {
-               nfs_invalidate_dircache(dir);
                d_instantiate(dentry, inode);
                nfs_renew_times(dentry);
                error = 0;
@@ -638,14 +631,15 @@ struct dentry *nfs_silly_lookup(struct dentry *parent, char *silly, int slen)
 {
        struct qstr    sqstr;
        struct dentry *sdentry;
+       unsigned long hash;
        int i, error;
 
        sqstr.name = silly;
        sqstr.len  = slen;
-       sqstr.hash = init_name_hash();
+       hash = init_name_hash();
        for (i= 0; i < slen; i++)
-               sqstr.hash = partial_name_hash(silly[i], sqstr.hash);
-       sqstr.hash = end_name_hash(sqstr.hash);
+               hash = partial_name_hash(silly[i], hash);
+       sqstr.hash = end_name_hash(hash);
        sdentry = d_lookup(parent, &sqstr);
        if (!sdentry) {
                sdentry = d_alloc(parent, &sqstr);
@@ -674,6 +668,11 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
                return -EIO;  /* No need to silly rename. */
        }
 
+#ifdef NFS_PARANOIA
+if (!dentry->d_inode)
+printk("NFS: silly-renaming %s/%s, negative dentry??\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
                return -EBUSY; /* don't allow to unlink silly inode -- nope,
                                * think a bit: silly DENTRY, NOT inode --
@@ -729,20 +728,28 @@ static void nfs_silly_delete(struct dentry *dentry)
                error = nfs_proc_remove(NFS_SERVER(dir),
                                        NFS_FH(dir), dentry->d_name.name);
                if (error < 0)
-                       printk("NFS " __FUNCTION__ " failed (err = %d)\n",
-                              -error);
+                       printk("NFS: can't silly-delete %s/%s, error=%d\n",
+                               dentry->d_parent->d_name.name,
+                               dentry->d_name.name, error);
                if (dentry->d_inode) {
                        if (dentry->d_inode->i_nlink)
                                dentry->d_inode->i_nlink --;
-               } else
+               } else {
+#ifdef NFS_PARANOIA
                        printk("nfs_silly_delete: negative dentry %s/%s\n",
                                dentry->d_parent->d_name.name,
                                dentry->d_name.name);
+#endif
+               }
                nfs_invalidate_dircache(dir);
-               /*
-                * The dentry is unhashed, but we want to make it negative.
-                */
-               d_delete(dentry);
+       }
+       /*
+        * Check whether to expire the dentry ...
+        */
+       else {
+               unsigned long age = jiffies - dentry->d_time;
+               if (age > 10*HZ)
+                       d_drop(dentry);
        }
 }
 
@@ -769,12 +776,22 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
                return -ENOENT;
        }
 
+       error = -ENAMETOOLONG;
        if (dentry->d_name.len > NFS_MAXNAMLEN)
-               return -ENAMETOOLONG;
+               goto out;
 
        error = nfs_sillyrename(dir, dentry);
 
        if (error && error != -EBUSY) {
+#ifdef NFS_PARANOIA
+if (dentry->d_count > 1)
+printk("nfs_unlink: dentry %s/%s, d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+if (dentry->d_inode && dentry->d_inode->i_count > 1)
+printk("nfs_unlink: dentry %s/%s, inode i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count);
+#endif
+               /* N.B. should check for d_count > 1 and fail */
                error = nfs_proc_remove(NFS_SERVER(dir),
                                        NFS_FH(dir), dentry->d_name.name);
                if (!error) {
@@ -785,11 +802,12 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
                        d_delete(dentry);
                }
        }
-
+out:
        return error;
 }
 
-static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+static int
+nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
        struct nfs_sattr sattr;
        int error;
@@ -802,11 +820,12 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
                return -ENOENT;
        }
 
+       error = -ENAMETOOLONG;
        if (dentry->d_name.len > NFS_MAXNAMLEN)
-               return -ENAMETOOLONG;
+               goto out;
 
        if (strlen(symname) > NFS_MAXPATHLEN)
-               return -ENAMETOOLONG;
+               goto out;
 
        sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
        sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
@@ -827,10 +846,12 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
                 */
                d_drop(dentry);
        }
+out:
        return error;
 }
 
-static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+static int 
+nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
 {
        int error;
 
@@ -843,18 +864,37 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
                return -ENOENT;
        }
 
+       error = -ENAMETOOLONG;
        if (dentry->d_name.len > NFS_MAXNAMLEN)
-               return -ENAMETOOLONG;
+               goto out;
+
+       /*
+        * The NFS server may want to use a new fileid for the link,
+        * so we can't reuse the existing inode for the new dentry.
+        * To force a new lookup after the link operation, we can just
+        * drop the new dentry, as long as it's not busy. (See above.)
+        */
+       error = -EBUSY;
+       if (dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+printk("nfs_link: dentry %s/%s busy, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+               goto out;
+       }
+       d_drop(dentry);
 
        error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
                                dentry->d_name.name);
        if (!error) {
                nfs_invalidate_dircache(dir);
+#if 0
                inode->i_count ++;
                inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
                d_instantiate(dentry, inode);
-               error = 0;
+#endif
        }
+out:
        return error;
 }
 
@@ -875,16 +915,31 @@ static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentr
  * implementation that only depends on the dcache stuff instead of
  * using the inode layer
  *
+ * Unfortunately, things are a little more complicated than indicated
+ * above. The NFS server may decide to use a new fileid for the renamed
+ * file, so we can't link the new name to the old inode. Otherwise, the
+ * server might reuse the fileid after the old file has been removed, 
+ * which would leave the new dentry holding an invalid fileid (possibly
+ * leading to file corruption). To handle this consider these cases:
+ *   (1) within-directory:
+ *       -- no problem, just use nfs_proc_rename
+ *   (2) cross-directory, only one user for old and new dentry:
+ *       -- drop both dentries to force new lookups, then use rename
+ *   (3) cross-directory, multiple users for old, one user for new:
+ *       -- drop new dentry, silly-rename old dentry and make a link
+ *   (4) cross-directory, multiple users for new dentry:
+ *       -- sorry, we're busy.
  */
 static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                      struct inode *new_dir, struct dentry *new_dentry)
 {
-       int error;
-
-       dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n",
-                               old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name,
-                               new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name);
+       int update = 1, error;
 
+#ifdef NFS_DEBUG_VERBOSE
+printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n",
+old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count,
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+#endif
        if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
                printk("nfs_rename: old inode is NULL or not a directory\n");
                return -ENOENT;
@@ -895,37 +950,66 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                return -ENOENT;
        }
 
-       if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN)
-               return -ENAMETOOLONG;
-
-       if (new_dir != old_dir) {
-               error = nfs_sillyrename(old_dir, old_dentry);
-
-               if (error == -EBUSY) {
-                       return -EBUSY;
-               } else if (error == 0) { /* did silly rename stuff */
-                       error = nfs_link(old_dentry->d_inode,
-                                        new_dir, new_dentry);
-                       
-                       return error;
-               }
-               /* no need for silly rename, proceed as usual */
-       }
+       error = -ENAMETOOLONG;
+       if (old_dentry->d_name.len > NFS_MAXNAMLEN ||
+           new_dentry->d_name.len > NFS_MAXNAMLEN)
+               goto out;
+       /*
+        * Examine the cases as noted above.
+        */
+       if (new_dir == old_dir)
+               goto simple_case;
+       error = -EBUSY;
+       if (new_dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+printk("nfs_rename: new dentry %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+#endif
+               goto out;
+       }
+       d_drop(new_dentry);
+       if (old_dentry->d_count > 1)
+               goto complex_case;
+       d_drop(old_dentry);
+       update = 0;
+       
+       /* no need for silly rename, proceed as usual */
+simple_case:
        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) {
-               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);
+       if (error)
+               goto out;
+       nfs_invalidate_dircache(new_dir);
+       nfs_invalidate_dircache(old_dir);
 
-               /* Update the dcache */
+       /* Update the dcache if needed */
+       if (update)
                d_move(old_dentry, new_dentry);
-       }
+       goto out;
+
+       /*
+        * We don't need to update the dcache in this case ... the
+        * new dentry has been dropped, and the old one silly-renamed.
+        */
+complex_case:
+       error = nfs_sillyrename(old_dir, old_dentry);
+       if (error)
+               goto out;
+       nfs_invalidate_dircache(old_dir);
+
+       error = nfs_link(old_dentry->d_inode, new_dir, new_dentry);
+       if (error)
+               goto out;
+       nfs_invalidate_dircache(new_dir);
+#ifdef NFS_PARANOIA
+printk("nfs_rename: dentry %s/%s linked to %s/%s, old count=%d\n",
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,
+old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count);
+#endif
+
+out:
        return error;
 }
 
index d3900b52a0b9921ee8cc6b2c9626814a2690bfa8..0f3bd5ed387190fdf922893fdc4e4857a01f810a 100644 (file)
@@ -83,53 +83,77 @@ struct inode_operations nfs_file_inode_operations = {
 # define IS_SWAPFILE(inode)    (0)
 #endif
 
-
+/*
+ * Flush all dirty pages, and check for write errors.
+ *
+ * Note that since the file close operation is called only by the
+ * _last_ process to close the file, we need to flush _all_ dirty 
+ * pages. This also means that there is little sense in checking
+ * for errors for this specific process -- we should probably just
+ * clear all errors.
+ */
 static int
 nfs_file_close(struct inode *inode, struct file *file)
 {
-       int     status;
+       int     status, error;
 
        dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
-       if ((status = nfs_flush_dirty_pages(inode, 0, 0)) < 0)
-               return status;
-       return nfs_write_error(inode);
+       status = nfs_flush_dirty_pages(inode, 0, 0, 0);
+       error = nfs_write_error(inode);
+       if (!status)
+               status = error;
+       return status;
 }
 
 static ssize_t
 nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
 {
        struct inode * inode = file->f_dentry->d_inode;
-       int     status;
+       ssize_t result;
 
        dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
                        inode->i_dev, inode->i_ino, count,
                        (unsigned long) *ppos);
 
-       if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
-               return status;
-       return generic_file_read(file, buf, count, ppos);
+       result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (!result)
+               result = generic_file_read(file, buf, count, ppos);
+       return result;
 }
 
 static int
 nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
-       int     status;
        struct inode *inode = file->f_dentry->d_inode;
+       int     status;
 
        dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
-       if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
-               return status;
-       return generic_file_mmap(file, vma);
+       status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (!status)
+               status = generic_file_mmap(file, vma);
+       return status;
 }
 
-static int nfs_fsync(struct file *file, struct dentry *dentry)
+/*
+ * Flush any dirty pages for this process, and check for write errors.
+ * The return status from this call provides a reliable indication of
+ * whether any write errors occurred for this process.
+ */
+static int
+nfs_fsync(struct file *file, struct dentry *dentry)
 {
        struct inode *inode = dentry->d_inode;
+       int status, error;
+
        dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
 
-       return nfs_flush_dirty_pages(inode, 0, 0);
+       status = nfs_flush_dirty_pages(inode, current->pid, 0, 0);
+       error = nfs_write_error(inode);
+       if (!status)
+               status = error;
+       return status;
 }
 
 /* 
@@ -139,7 +163,7 @@ static ssize_t
 nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
        struct inode * inode = file->f_dentry->d_inode;
-       int     result;
+       ssize_t result;
 
        dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
                        inode->i_dev, inode->i_ino, inode->i_count,
@@ -153,21 +177,26 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
                printk("NFS: attempt to write to active swap file!\n");
                return -EBUSY;
        }
-       if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
-               return result;
+       result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+       if (result)
+               goto out;
+
+       /* N.B. This should be impossible now -- inodes can't change mode */
        if (!S_ISREG(inode->i_mode)) {
                printk("nfs_file_write: write to non-file, mode %07o\n",
                        inode->i_mode);
                return -EINVAL;
        }
-       if (count <= 0)
-               return 0;
-
-       /* Return error from previous async call */
-       if ((result = nfs_write_error(inode)) < 0)
-               return result;
-
-       return generic_file_write(file, buf, count, ppos);
+       result = count;
+       if (!count)
+               goto out;
+
+       /* Check for an error from a previous async call */
+       result = nfs_write_error(inode);
+       if (!result)
+               result = generic_file_write(file, buf, count, ppos);
+out:
+       return result;
 }
 
 /*
@@ -176,15 +205,15 @@ nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 int
 nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
 {
+       struct inode * inode = filp->f_dentry->d_inode;
        int     status;
-       struct inode * inode;
 
        dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
-                       filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino,
+                       inode->i_dev, inode->i_ino,
                        fl->fl_type, fl->fl_flags,
                        fl->fl_start, fl->fl_end);
 
-       if (!(inode = filp->f_dentry->d_inode))
+       if (!inode)
                return -EINVAL;
 
        /* No mandatory locks over NFS */
@@ -209,7 +238,7 @@ nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
         * been killed by a signal, that is). */
        if (cmd == F_SETLK && fl->fl_type == F_UNLCK
            && !signal_pending(current)) {
-               status = nfs_flush_dirty_pages(inode,
+               status = nfs_flush_dirty_pages(inode, current->pid,
                        fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 :
                        fl->fl_end - fl->fl_start + 1);
                if (status < 0)
index a2634007650a0c17c37c596258c7a67840567b32..d30165ba4e5dbeeb6979323d16e189436de4b696 100644 (file)
@@ -91,16 +91,19 @@ nfs_put_inode(struct inode * inode)
 static void
 nfs_delete_inode(struct inode * inode)
 {
+       int failed;
+
        dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
        /*
         * Flush out any pending write requests ...
         */
        if (NFS_WRITEBACK(inode) != NULL) {
                unsigned long timeout = jiffies + 5*HZ;
-               printk("NFS: invalidating pending RPC requests\n");
+               printk("NFS: inode %ld, invalidating pending RPC requests\n",
+                       inode->i_ino);
                nfs_invalidate_pages(inode);
                while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) {
-                       current->state = TASK_UNINTERRUPTIBLE;
+                       current->state = TASK_INTERRUPTIBLE;
                        current->timeout = jiffies + HZ/10;
                        schedule();
                }
@@ -109,8 +112,10 @@ nfs_delete_inode(struct inode * inode)
                        printk("NFS: Arghhh, stuck RPC requests!\n");
        }
 
-       if (check_failed_request(inode))
-               printk("NFS: inode had failed requests\n");
+       failed = check_failed_request(inode);
+       if (failed)
+               printk("NFS: inode %ld had %d failed requests\n",
+                       inode->i_ino, failed);
        clear_inode(inode);
 }
 
index d6d21368679d7d5f079095c7045b5b84517ece29..97663cc11664ce6fe09ca06451fdf86e5b45bb7c 100644 (file)
@@ -286,16 +286,17 @@ find_write_request(struct inode *inode, struct page *page)
  * Find a failed write request by pid
  */
 static struct nfs_wreq *
-find_failed_request(struct inode *inode, pid_t pid, int all)
+find_failed_request(struct inode *inode, pid_t pid)
 {
        struct nfs_wreq *head, *req;
 
-       if (!(req = head = nfs_failed_requests))
-               return NULL;
-       do {
-               if (req->wb_inode == inode && (all || req->wb_pid == pid))
+       req = head = nfs_failed_requests;
+       while (req != NULL) {
+               if (req->wb_inode == inode && (pid == 0 || req->wb_pid == pid))
                        return req;
-       } while ((req = WB_NEXT(req)) != head);
+               if ((req = WB_NEXT(req)) == head)
+                       break;
+       }
        return NULL;
 }
 
@@ -335,7 +336,7 @@ check_failed_request(struct inode * inode)
        struct nfs_wreq * req;
        int found = 0;
 
-       while ((req = find_failed_request(inode, 0, 1)) != NULL) {
+       while ((req = find_failed_request(inode, 0)) != NULL) {
                remove_failed_request(req);
                found++;
        }
@@ -561,12 +562,13 @@ nfs_updatepage(struct inode *inode, struct page *page, const char *buffer,
        }
 
        /* Create the write request. */
-       if (!(req = create_write_request(inode, page, offset, count))) {
-               status = -ENOBUFS;
+       status = -ENOBUFS;
+       req = create_write_request(inode, page, offset, count);
+       if (!req)
                goto done;
-       }
 
        /* Copy data to page buffer. */
+       /* N.B. should check for fault here ... */
        copy_from_user(page_addr + offset, buffer, count);
 
        /* Schedule request */
@@ -593,6 +595,7 @@ done:
                        transfer_page_lock(req);
                        /* rpc_execute(&req->wb_task); */
                        if (sync) {
+                               /* N.B. if signalled, result not ready? */
                                wait_on_write_request(req);
                                if ((count = nfs_write_error(inode)) < 0)
                                        status = count;
@@ -652,10 +655,20 @@ nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
 
                        if (rqoffset < end && offset < rqend
                         && (pid == 0 || req->wb_pid == pid)) {
-                               if (!WB_HAVELOCK(req))
+                               if (!WB_HAVELOCK(req)) {
+#ifdef NFS_PARANOIA
+printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
+req->wb_inode->i_ino, req->wb_bytes, rqoffset);
+#endif
                                        nfs_flush_request(req);
+                               }
                                last = req;
                        }
+               } else {
+#ifdef NFS_PARANOIA
+printk("nfs_flush_pages: in progress inode=%ld, %d @ %lu\n",
+req->wb_inode->i_ino, req->wb_bytes, rqoffset);
+#endif
                }
                if (invalidate)
                        req->wb_flags |= NFS_WRITE_INVALIDATE;
@@ -668,6 +681,10 @@ nfs_flush_pages(struct inode *inode, pid_t pid, off_t offset, off_t len,
 
 /*
  * Cancel all writeback requests, both pending and in progress.
+ *
+ * N.B. This doesn't seem to wake up the tasks -- are we sure
+ * they will eventually complete? Also, this could overwrite a
+ * failed status code from an already-completed task.
  */
 static void
 nfs_cancel_dirty(struct inode *inode, pid_t pid)
@@ -676,7 +693,8 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid)
 
        req = head = NFS_WRITEBACK(inode);
        while (req != NULL) {
-               if (req->wb_pid == pid) {
+               /* N.B. check for task already finished? */
+               if (pid == 0 || req->wb_pid == pid) {
                        req->wb_flags |= NFS_WRITE_CANCELLED;
                        rpc_exit(&req->wb_task, 0);
                }
@@ -694,24 +712,30 @@ nfs_cancel_dirty(struct inode *inode, pid_t pid)
  * this isn't used by the nlm module yet.
  */
 int
-nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
+nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
 {
        struct nfs_wreq *last = NULL;
-       int result = 0;
+       int result = 0, cancel = 0;
 
        dprintk("NFS:      flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
                                inode->i_dev, inode->i_ino, current->pid,
                                offset, len);
 
+       if (IS_SOFT && signalled()) {
+               nfs_cancel_dirty(inode, pid);
+               cancel = 1;
+       }
+
        for (;;) {
                if (IS_SOFT && signalled()) {
-                       nfs_cancel_dirty(inode, current->pid);
+                       if (!cancel)
+                               nfs_cancel_dirty(inode, pid);
                        result = -ERESTARTSYS;
                        break;
                }
 
-               /* Flush all pending writes for this pid and file region */
-               last = nfs_flush_pages(inode, current->pid, offset, len, 0);
+               /* Flush all pending writes for the pid and file region */
+               last = nfs_flush_pages(inode, pid, offset, len, 0);
                if (last == NULL)
                        break;
                wait_on_write_request(last);
@@ -724,7 +748,7 @@ nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
  * Flush out any pending write requests and flag that they be discarded
  * after the write is complete.
  *
- * This function is called from nfs_revalidate_inode just before it calls
+ * This function is called from nfs_refresh_inode just before it calls
  * invalidate_inode_pages. After nfs_flush_pages returns, we can be sure
  * that all dirty pages are locked, so that invalidate_inode_pages does
  * not throw away any dirty pages.
@@ -780,7 +804,7 @@ nfs_check_error(struct inode *inode)
        dprintk("nfs:      checking for write error inode %04x/%ld\n",
                        inode->i_dev, inode->i_ino);
 
-       req = find_failed_request(inode, current->pid, 0);
+       req = find_failed_request(inode, current->pid);
        if (req) {
                dprintk("nfs: write error %d inode %04x/%ld\n",
                        req->wb_task.tk_status, inode->i_dev, inode->i_ino);
@@ -869,7 +893,7 @@ nfs_wback_result(struct rpc_task *task)
                 * application by adding the request to the failed
                 * requests list.
                 */
-               if (find_failed_request(inode, req->wb_pid, 0))
+               if (find_failed_request(inode, req->wb_pid))
                        status = 0;
                clear_bit(PG_uptodate, &page->flags);
        } else if (!WB_CANCELLED(req)) {
index 595c6ff557d5be298446fb5075cef7bbf9d8d1e0..90cc3a161b0567df903be0507cbf4aff0ccec135 100644 (file)
@@ -5,35 +5,40 @@
 mainmenu_option next_comment
 comment 'Native Language Support'
 
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
+# msdos and Joliet want NLS
+if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" ]; then
+  define_bool CONFIG_NLS y
+else
+  define_bool CONFIG_NLS n
+fi
 
-if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
-  dep_tristate 'Codepage 437'      CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS
-  dep_tristate 'Codepage 737'      CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS
-  dep_tristate 'Codepage 775'      CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS
-  dep_tristate 'Codepage 850'      CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS
-  dep_tristate 'Codepage 852'      CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS
-  dep_tristate 'Codepage 855'      CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS
-  dep_tristate 'Codepage 857'      CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS
-  dep_tristate 'Codepage 860'      CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS
-  dep_tristate 'Codepage 861'      CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS
-  dep_tristate 'Codepage 862'      CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS
-  dep_tristate 'Codepage 863'      CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS
-  dep_tristate 'Codepage 864'      CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS
-  dep_tristate 'Codepage 865'      CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS
-  dep_tristate 'Codepage 866'      CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS
-  dep_tristate 'Codepage 869'      CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS
-  dep_tristate 'Codepage 874'      CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-1'    CONFIG_NLS_ISO8859_1    $CONFIG_NLS 
-  dep_tristate 'NLS ISO 8859-2'    CONFIG_NLS_ISO8859_2    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-3'    CONFIG_NLS_ISO8859_3    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-4'    CONFIG_NLS_ISO8859_4    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-5'    CONFIG_NLS_ISO8859_5    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-6'    CONFIG_NLS_ISO8859_6    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-7'    CONFIG_NLS_ISO8859_7    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-8'    CONFIG_NLS_ISO8859_8    $CONFIG_NLS
-  dep_tristate 'NLS ISO 8859-9'    CONFIG_NLS_ISO8859_9    $CONFIG_NLS
-  dep_tristate 'NLS KOI8-R'        CONFIG_NLS_KOI8_R       $CONFIG_NLS
+if [ "$CONFIG_NLS" = "y" ]; then
+  tristate 'Codepage 437'      CONFIG_NLS_CODEPAGE_437
+  tristate 'Codepage 737'      CONFIG_NLS_CODEPAGE_737
+  tristate 'Codepage 775'      CONFIG_NLS_CODEPAGE_775
+  tristate 'Codepage 850'      CONFIG_NLS_CODEPAGE_850
+  tristate 'Codepage 852'      CONFIG_NLS_CODEPAGE_852
+  tristate 'Codepage 855'      CONFIG_NLS_CODEPAGE_855
+  tristate 'Codepage 857'      CONFIG_NLS_CODEPAGE_857
+  tristate 'Codepage 860'      CONFIG_NLS_CODEPAGE_860
+  tristate 'Codepage 861'      CONFIG_NLS_CODEPAGE_861
+  tristate 'Codepage 862'      CONFIG_NLS_CODEPAGE_862
+  tristate 'Codepage 863'      CONFIG_NLS_CODEPAGE_863
+  tristate 'Codepage 864'      CONFIG_NLS_CODEPAGE_864
+  tristate 'Codepage 865'      CONFIG_NLS_CODEPAGE_865
+  tristate 'Codepage 866'      CONFIG_NLS_CODEPAGE_866
+  tristate 'Codepage 869'      CONFIG_NLS_CODEPAGE_869
+  tristate 'Codepage 874'      CONFIG_NLS_CODEPAGE_874
+  tristate 'NLS ISO 8859-1'    CONFIG_NLS_ISO8859_1
+  tristate 'NLS ISO 8859-2'    CONFIG_NLS_ISO8859_2
+  tristate 'NLS ISO 8859-3'    CONFIG_NLS_ISO8859_3
+  tristate 'NLS ISO 8859-4'    CONFIG_NLS_ISO8859_4
+  tristate 'NLS ISO 8859-5'    CONFIG_NLS_ISO8859_5
+  tristate 'NLS ISO 8859-6'    CONFIG_NLS_ISO8859_6
+  tristate 'NLS ISO 8859-7'    CONFIG_NLS_ISO8859_7
+  tristate 'NLS ISO 8859-8'    CONFIG_NLS_ISO8859_8
+  tristate 'NLS ISO 8859-9'    CONFIG_NLS_ISO8859_9
+  tristate 'NLS KOI8-R'        CONFIG_NLS_KOI8_R
 fi
 
 endmenu
index 270211b1ccdf401cabad10ba651e63aac256c5b8..4f51a5f9d29245620f27b0e40116aae8dff5bd88 100644 (file)
@@ -4,15 +4,7 @@
 
 MOD_LIST_NAME := NLS_MODULES
 
-ifeq ($(CONFIG_NLS),y)
-NLS += nls_base.o
-O_TARGET = nls.o
-OX_OBJS  = $(NLS)
-else
-  ifeq ($(CONFIG_NLS),m)
-  MX_OBJS += nls_base.o
-  endif
-endif
+NLS = nls_base.o
 
 ifeq ($(CONFIG_NLS_CODEPAGE_437),y)
 NLS += nls_cp437.o
@@ -302,4 +294,7 @@ else
   endif
 endif
 
+O_TARGET = nls.o
+OX_OBJS  = $(NLS)
+
 include $(TOPDIR)/Rules.make
index 65c13811efe9c102619a590bd1812b48ac86dceb..ba0a134e4c5584e08451c71fe7a4a50becc3e557 100644 (file)
@@ -6,7 +6,6 @@
  *
  */
 
-#define ASC_LINUX_VERSION(V, P, S)     (((V) * 65536) + ((P) * 256) + (S))
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -479,11 +478,10 @@ int init_nls(void)
 #ifdef CONFIG_NLS_CODEPAGE_874
        init_nls_cp874();
 #endif
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
-       return 0;
-#else
-       return register_symtab(&nls_syms);
+#ifdef CONFIG_NLS_KOI8_R
+       init_nls_koi8_r();
 #endif
+       return 0;
 }
 
 #ifdef MODULE
index 7e2e7f0151fb7926a81c6ea474f66c126f7d506f..e33d38cda43c00539bf24e0853e83738c6c847eb 100644 (file)
@@ -130,13 +130,14 @@ printk("smb_init_dircache: initializing cache, %d blocks\n", cachep->pages);
  * 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)
+smb_add_to_cache(struct cache_head * cachep, struct cache_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 nent, offset, len = entry->len;
        unsigned int needed = len + sizeof(struct cache_entry);
 
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -163,10 +164,10 @@ inode, cachep->status, entry->d_name, fpos);
                offset = index->space + 
                         index->num_entries * sizeof(struct cache_entry);
                block = index->block;
-               memcpy(&block->cb_data.names[offset], entry->d_name, len);
+               memcpy(&block->cb_data.names[offset], entry->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;
+               block->cb_data.table[nent].ino = entry->ino;
                cachep->entries++;
 #ifdef SMBFS_DEBUG_VERBOSE
 printk("smb_add_to_cache: added entry %s, len=%d, pos=%ld, entries=%d\n",
index a572de027146391a7f8e80a33e0b81839e33c9d9..6b3ce2907c3d4f8f03e71a47fa824a6be94ecbcc 100644 (file)
@@ -8,20 +8,14 @@
 
 #include <linux/sched.h>
 #include <linux/errno.h>
-#include <linux/stat.h>
 #include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
-#include <linux/errno.h>
-
-#include <asm/uaccess.h>
-#include <asm/semaphore.h>
 
 #define SMBFS_PARANOIA 1
 /* #define SMBFS_DEBUG_VERBOSE 1 */
 /* #define pr_debug printk */
+#define SMBFS_MAX_AGE 5*HZ
 
 static ssize_t smb_dir_read(struct file *, char *, size_t, loff_t *);
 static int smb_readdir(struct file *, void *, filldir_t);
@@ -94,13 +88,13 @@ hash_it(const char * name, unsigned int len)
  * If a dentry already exists, we have to give the cache entry
  * the correct inode number.  This is needed for getcwd().
  */
-static unsigned long
+static void
 smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
 {
        struct dentry * new_dentry;
        struct qstr qname;
-       unsigned long ino = 0;
 
+       /* N.B. Make cache_dirent name a qstr! */
        qname.name = entry->name;
        qname.len  = entry->len;
        qname.hash = hash_it(qname.name, qname.len);
@@ -109,12 +103,11 @@ smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
        {
                struct inode * inode = new_dentry->d_inode;
                if (inode)
-                       ino = inode->i_ino;
+                       entry->ino = inode->i_ino;
                dput(new_dentry);
        }
-       if (!ino)
-               ino = smb_invent_inos(1);
-       return ino;
+       if (!entry->ino)
+               entry->ino = smb_invent_inos(1);
 }
 
 static int 
@@ -125,9 +118,10 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
        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);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_readdir: reading %s/%s, f_pos=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos);
+#endif
        /*
         * Make sure our inode is up-to-date.
         */
@@ -137,6 +131,7 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
        /*
         * Get the cache pointer ...
         */
+       result = -EIO;
        cachep = smb_get_dircache(dentry);
        if (!cachep)
                goto out;
@@ -147,19 +142,20 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
        {
                result = smb_refill_dircache(cachep, dentry);
                if (result)
-                       goto up_and_out;
+                       goto out_free;
        }
 
+       result = 0;
        switch ((unsigned int) filp->f_pos)
        {
        case 0:
                if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
-                       goto up_and_out;
+                       goto out_free;
                filp->f_pos = 1;
        case 1:
                if (filldir(dirent, "..", 2, 1,
                                dentry->d_parent->d_inode->i_ino) < 0)
-                       goto up_and_out;
+                       goto out_free;
                filp->f_pos = 2;
        }
 
@@ -173,21 +169,18 @@ smb_readdir(struct file *filp, void *dirent, filldir_t filldir)
                 * Check whether to look up the inode number.
                 */
                if (!entry->ino)
-               {
-                       entry->ino = smb_find_ino(dentry, entry);
-               }
+                       smb_find_ino(dentry, entry);
 
                if (filldir(dirent, entry->name, entry->len, 
                                    filp->f_pos, entry->ino) < 0)
                        break;
                filp->f_pos += 1;
        }
-       result = 0;
 
        /*
         * Release the dircache.
         */
-up_and_out:
+out_free:
        smb_free_dircache(cachep);
 out:
        return result;
@@ -220,7 +213,8 @@ static struct dentry_operations smbfs_dentry_operations =
 /*
  * This is the callback when the dcache has a lookup hit.
  */
-static int smb_lookup_validate(struct dentry * dentry)
+static int
+smb_lookup_validate(struct dentry * dentry)
 {
        struct inode * inode = dentry->d_inode;
        unsigned long age = jiffies - dentry->d_time;
@@ -231,11 +225,11 @@ static int smb_lookup_validate(struct dentry * dentry)
         * we believe in dentries for 5 seconds.  (But each
         * successful server lookup renews the timestamp.)
         */
-       valid = age < 5 * HZ || IS_ROOT(dentry);
+       valid = (age <= SMBFS_MAX_AGE) || 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)
+printk("smb_lookup_validate: %s/%s not valid, age=%lu\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name, age);
 #endif
 
        if (inode)
@@ -259,10 +253,20 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
 
 /*
  * 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.
+ * We use this to unhash dentries with bad inodes and close files.
  */
-static void smb_delete_dentry(struct dentry * dentry)
+static void
+smb_delete_dentry(struct dentry * dentry)
 {
+       if ((jiffies - dentry->d_time) > SMBFS_MAX_AGE)
+       {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_delete_dentry: %s/%s expired, d_time=%lu, now=%lu\n", 
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_time, jiffies);
+#endif
+               d_drop(dentry);
+       }
+
        if (dentry->d_inode)
        {
                if (is_bad_inode(dentry->d_inode))
@@ -285,9 +289,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name);
  * are all valid, so we want to update the dentry timestamps.
  * N.B. Move this to dcache?
  */
-void smb_renew_times(struct dentry * dentry)
+void
+smb_renew_times(struct dentry * dentry)
 {
-       for (;;) {
+       for (;;)
+       {
                dentry->d_time = jiffies;
                if (dentry == dentry->d_parent)
                        break;
@@ -361,6 +367,10 @@ smb_instantiate(struct dentry *dentry)
                        error = 0;
                }
        }
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_instantiate: file %s/%s, error=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, error);
+#endif
        return error;
 }
 
@@ -370,6 +380,10 @@ smb_create(struct inode *dir, struct dentry *dentry, int mode)
 {
        int error;
 
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_create: creating %s/%s, mode=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, mode);
+#endif
        error = -ENAMETOOLONG;
        if (dentry->d_name.len > SMB_MAXNAMELEN)
                goto out;
index 45ac97a31b68ffb7653f935ef9cd8a6cea36bfa4..9fa49bd5d4bea82dc4318f1a77504882e13a015c 100644 (file)
@@ -6,14 +6,10 @@
  *
  */
 
-#define SMBFS_DCACHE_EXT 1
-
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/sched.h>
-#include <linux/smb_fs.h>
-#include <linux/smbno.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/string.h>
@@ -23,6 +19,9 @@
 #include <linux/malloc.h>
 #include <linux/init.h>
 #include <linux/dcache.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #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_read_inode(struct inode *);
 static void smb_put_inode(struct inode *);
 static void smb_delete_inode(struct inode *);
-static void smb_read_inode(struct inode *);
 static void smb_put_super(struct super_block *);
-static int smb_statfs(struct super_block *, struct statfs *, int);
+static int  smb_statfs(struct super_block *, struct statfs *, int);
 
 static struct super_operations smb_sops =
 {
@@ -147,6 +140,11 @@ printk("smb_invalidate_inodes\n");
        invalidate_inodes(SB_of(server));
 }
 
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server.  If it has changed, we must
+ * invalidate our local caches.
+ */
 int
 smb_revalidate_inode(struct inode *inode)
 {
@@ -167,25 +165,23 @@ jiffies, inode->u.smbfs_i.oldmtime);
        }
 
        /*
-        * Save the last modified time, then refresh the inode
+        * Save the last modified time, then refresh the inode.
+        * (Note: a size change should have a different mtime.)
         */
        last_time = inode->i_mtime;
        error = smb_refresh_inode(inode);
-       if (!error)
+       if (error || inode->i_mtime != last_time)
        {
-               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);
-               }
+               if (!S_ISDIR(inode->i_mode))
+                       invalidate_inode_pages(inode);
+               else
+                       smb_invalid_dir_cache(inode);
        }
 out:
        return error;
@@ -253,10 +249,12 @@ inode->i_mode, fattr.f_mode);
                        /*
                         * 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 ...
+                        * But we do want to invalidate the caches ...
                         */
-                       invalidate_inode_pages(inode);
-                       smb_invalid_dir_cache(inode);
+                       if (!S_ISDIR(inode->i_mode))
+                               invalidate_inode_pages(inode);
+                       else
+                               smb_invalid_dir_cache(inode);
                        error = -EIO;
                }
        }
@@ -328,6 +326,7 @@ smb_put_super(struct super_block *sb)
        if (server->conn_pid)
               kill_proc(server->conn_pid, SIGTERM, 0);
 
+       kfree(server->mnt);
        if (server->packet)
                smb_vfree(server->packet);
        sb->s_dev = 0;
@@ -340,7 +339,7 @@ smb_put_super(struct super_block *sb)
 struct super_block *
 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_mount_data *mnt, *data = (struct smb_mount_data *) raw_data;
        struct smb_fattr root;
        kdev_t dev = sb->s_dev;
        struct inode *root_inode;
@@ -368,16 +367,25 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
        sb->u.smbfs_sb.conn_pid = 0;
        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);   
+       sb->u.smbfs_sb.packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE); 
+       sb->u.smbfs_sb.packet = smb_vmalloc(sb->u.smbfs_sb.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;
+       mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
+       if (!mnt)
+               goto out_no_mount;
+       *mnt = *data;
+       mnt->version = 0; /* dynamic flags */
+#ifdef CONFIG_SMB_WIN95
+       mnt->version |= 1;
+#endif
+       mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+       mnt->file_mode |= S_IFREG;
+       mnt->dir_mode  &= (S_IRWXU | S_IRWXG | S_IRWXO);
+       mnt->dir_mode  |= S_IFDIR;
+       sb->u.smbfs_sb.mnt = mnt;
+
        /*
         * Keep the super block locked while we get the root inode.
         */
@@ -398,20 +406,20 @@ smb_read_super(struct super_block *sb, void *raw_data, int silent)
 out_no_root:
        printk(KERN_ERR "smb_read_super: get root inode failed\n");
        iput(root_inode);
+       kfree(sb->u.smbfs_sb.mnt);
+out_no_mount:
        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_unlock:
+       unlock_super(sb);
+       goto out_fail;
 out_wrong_data:
-       printk(KERN_ERR "smb_read_super: wrong data argument."
-              " Recompile smbmount.\n");
+       printk("smb_read_super: need mount version %d\n", SMB_MOUNT_VERSION);
        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;
@@ -439,6 +447,7 @@ 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;
+       unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
        int error, refresh = 0;
 
        error = -EIO;
@@ -459,14 +468,13 @@ smb_notify_change(struct inode *inode, struct iattr *attr)
                goto out;
 
        error = -EPERM;
-       if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid)))
+       if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid))
                goto out;
 
-       if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid)))
+       if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid))
                goto out;
 
-       if (((attr->ia_valid & ATTR_MODE) &&
-       (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+       if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
                goto out;
 
        if ((attr->ia_valid & ATTR_SIZE) != 0)
index 45bebd2fedb0e9c3a0f7950ce87423bb171fc02c..5eb3dc88ff760f5ac14565ada338b090229e49e3 100644 (file)
@@ -8,10 +8,11 @@
 
 #include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/smb_fs.h>
 #include <linux/ioctl.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
 
 #include <asm/uaccess.h>
 
@@ -19,33 +20,33 @@ int
 smb_ioctl(struct inode *inode, struct file *filp,
          unsigned int cmd, unsigned long arg)
 {
+       int result = -EINVAL;
+
        switch (cmd)
        {
        case SMB_IOC_GETMOUNTUID:
-               return put_user(SMB_SERVER(inode)->m.mounted_uid,
+               result = put_user(SMB_SERVER(inode)->mnt->mounted_uid,
                                (uid_t *) arg);
+               break;
 
        case SMB_IOC_NEWCONN:
        {
                struct smb_conn_opt opt;
-               int result;
 
                if (arg == 0)
                {
                        /* The process offers a new connection upon SIGUSR1 */
-                       return smb_offerconn(SMB_SERVER(inode));
+                       result = smb_offerconn(SMB_SERVER(inode));
                }
-
-               if ((result = verify_area(VERIFY_READ, (uid_t *) arg,
-                                         sizeof(opt))) != 0)
+               else
                {
-                       return result;
+                       result = -EFAULT;
+                       if (!copy_from_user(&opt, (void *)arg, sizeof(opt)))
+                               result = smb_newconn(SMB_SERVER(inode), &opt);
                }
-               copy_from_user(&opt, (void *)arg, sizeof(opt));
-
-               return smb_newconn(SMB_SERVER(inode), &opt);
+               break;
        }
        default:
-               return -EINVAL;
        }
+       return result;
 }
index fb310adcfbfe786fd4ef1145327a1ece6a935192..af0bc12c07c87216578f28b1edc5dd5e2abdc750 100644 (file)
@@ -9,10 +9,7 @@
  *             by Riccardo Facchetti
  */
 
-#include <linux/config.h>
 #include <linux/fs.h>
-#include <linux/smbno.h>
-#include <linux/smb_fs.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/malloc.h>
 #include <linux/fcntl.h>
 #include <linux/dcache.h>
 #include <linux/dirent.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
 
-#include <asm/uaccess.h>
 #include <asm/string.h>
 
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+/* #define pr_debug printk */
+
 #define SMB_VWV(packet)  ((packet) + SMB_HEADER_LEN)
 #define SMB_CMD(packet)  (*(packet+8))
 #define SMB_WCT(packet)  (*(packet+SMB_HEADER_LEN - 1))
 #define SMB_DIRINFO_SIZE 43
 #define SMB_STATUS_SIZE  21
 
-#define SMBFS_PARANOIA 1
-/* #define SMBFS_DEBUG_VERBOSE 1 */
-/* #define pr_debug printk */
-
-extern void smb_renew_times(struct dentry *);
-
 static inline int
 min(int a, int b)
 {
@@ -46,9 +43,9 @@ min(int a, int b)
 }
 
 static void
-str_upper(char *name)
+str_upper(char *name, int len)
 {
-       while (*name)
+       while (len--)
        {
                if (*name >= 'a' && *name <= 'z')
                        *name -= ('a' - 'A');
@@ -57,9 +54,9 @@ str_upper(char *name)
 }
 
 static void
-str_lower(char *name)
+str_lower(char *name, int len)
 {
-       while (*name)
+       while (len--)
        {
                if (*name >= 'A' && *name <= 'Z')
                        *name += ('a' - 'A');
@@ -158,7 +155,7 @@ static char *smb_encode_path(struct smb_sb_info *server, char *buf,
        buf += smb_build_path(dir, name, buf);
 
        if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
-               str_upper(start);
+               str_upper(start, buf - start);
 
        return buf;
 }
@@ -569,7 +566,7 @@ smb_offerconn(struct smb_sb_info *server)
        int error;
 
        error = -EACCES;
-       if (!suser() && (current->uid != server->m.mounted_uid))
+       if ((current->uid != server->mnt->mounted_uid) && !suser()) 
                goto out;
        if (atomic_read(&server->sem.count) == 1)
        {
@@ -609,7 +606,7 @@ smb_newconn(struct smb_sb_info *server, struct smb_conn_opt *opt)
                goto out;
 
        error = -EACCES;
-       if (!suser() && (current->uid != server->m.mounted_uid))
+       if ((current->uid != server->mnt->mounted_uid) && !suser())
                goto out;
        if (atomic_read(&server->sem.count) == 1)
        {
@@ -888,12 +885,11 @@ dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
                        }
                        smb_unlock_server(server);
                }
-       }
-       /* Consider dropping negative dentries? */
-#if 0
-       else
-               d_drop(dentry);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_close_dentry: closed %s/%s, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
 #endif
+       }
 }
 
 /* In smb_proc_read and smb_proc_write we do not retry, because the
@@ -951,11 +947,10 @@ smb_proc_write(struct inode *ino, off_t offset, int count, const char *data)
 
        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, 
+((struct dentry *)ino->u.smbfs_i.dentry)->d_parent->d_name.name, 
+((struct dentry *)ino->u.smbfs_i.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);
@@ -967,11 +962,11 @@ count, offset, server->packet_size);
        WSET(p, 0, count);
        memcpy(p+2, data, count);
 
-       if ((result = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+       result = smb_request_ok(server, SMBwrite, 1, 0);
+       if (result >= 0)
                result = WVAL(server->packet, smb_vwv0);
 
        smb_unlock_server(server);
-
        return result;
 }
 
@@ -997,9 +992,7 @@ smb_proc_create(struct dentry *dir, struct qstr *name,
        if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
                goto out;
        }
        smb_proc_close(server, WVAL(server->packet, smb_vwv0), CURRENT_TIME);
@@ -1033,10 +1026,11 @@ smb_proc_mv(struct dentry *odir, struct qstr *oname,
        if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
+               goto out;
        }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1060,10 +1054,11 @@ smb_proc_mkdir(struct dentry *dir, struct qstr *name)
        if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
+               goto out;
        }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1087,10 +1082,11 @@ smb_proc_rmdir(struct dentry *dir, struct qstr *name)
        if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
+               goto out;
        }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1115,10 +1111,11 @@ smb_proc_unlink(struct dentry *dir, struct qstr *name)
        if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
+               goto out;
        }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1127,18 +1124,16 @@ int
 smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length)
 {
        char *p;
-       char *buf;
        int result;
 
        smb_lock_server(server);
 
       retry:
-       buf = server->packet;
        p = smb_setup_header(server, SMBwrite, 5, 0);
-       WSET(buf, smb_vwv0, fid);
-       WSET(buf, smb_vwv1, 0);
-       DSET(buf, smb_vwv2, length);
-       WSET(buf, smb_vwv4, 0);
+       WSET(server->packet, smb_vwv0, fid);
+       WSET(server->packet, smb_vwv1, 0);
+       DSET(server->packet, smb_vwv2, length);
+       WSET(server->packet, smb_vwv4, 0);
        *p++ = 4;
        *p++ = 0;
        smb_setup_bcc(server, p);
@@ -1146,10 +1141,11 @@ smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length)
        if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
        {
                if (smb_retry(server))
-               {
                        goto retry;
-               }
+               goto out;
        }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1160,18 +1156,18 @@ smb_init_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
        memset(fattr, 0, sizeof(*fattr));
 
        fattr->f_nlink = 1;
-       fattr->f_uid = server->m.uid;
-       fattr->f_gid = server->m.gid;
+       fattr->f_uid = server->mnt->uid;
+       fattr->f_gid = server->mnt->gid;
        fattr->f_blksize = 512;
 }
 
 static void
 smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
 {
-       fattr->f_mode = server->m.file_mode;
+       fattr->f_mode = server->mnt->file_mode;
        if (fattr->attr & aDIR)
        {
-               fattr->f_mode = server->m.dir_mode;
+               fattr->f_mode = server->mnt->dir_mode;
                fattr->f_size = 512;
        }
 
@@ -1194,42 +1190,47 @@ smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
        smb_finish_dirent(server, fattr);
 }
 
-
+/*
+ * Note that we are now returning the name as a reference to avoid
+ * an extra copy, and that the upper/lower casing is done in place.
+ */
 static __u8 *
-smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct dirent *entry)
+smb_decode_dirent(struct smb_sb_info *server, __u8 *p, 
+                       struct cache_dirent *entry)
 {
        int len;
 
+       /*
+        * SMB doesn't have a concept of inode numbers ...
+        */
+       entry->ino = 0;
+
        p += SMB_STATUS_SIZE;   /* reserved (search_status) */
-       len = strlen(p + 9);
+       entry->name = p + 9;
+       len = strlen(entry->name);
        if (len > 12)
        {
                len = 12;
        }
-       memcpy(entry->d_name, p + 9, len);
-#ifdef SMBFS_TRIM_BLANKS
        /*
         * Trim trailing blanks for Pathworks servers
         */
-       while (len > 2 && entry->d_name[len-1] == ' ')
+       while (len > 2 && entry->name[len-1] == ' ')
                len--;
-#endif
-       entry->d_name[len] = '\0';
-       entry->d_reclen = len;
-       entry->d_ino = 0; /* no inode number available */
+       entry->len = len;
 
        switch (server->opt.case_handling)
        {
        case SMB_CASE_UPPER:
-               str_upper(entry->d_name);
+               str_upper(entry->name, len);
                break;
        case SMB_CASE_LOWER:
-               str_lower(entry->d_name);
+               str_lower(entry->name, len);
                break;
        default:
                break;
        }
-       pr_debug("smb_decode_dirent: name = %s\n", entry->name);
+       pr_debug("smb_decode_dirent: len=%d, name=%s\n", len, entry->name);
        return p + 22;
 }
 
@@ -1250,7 +1251,10 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
        char status[SMB_STATUS_SIZE];
        static struct qstr mask = { "*.*", 3, 0 };
 
-       pr_debug("smb_proc_readdir_short: %d @ %d\n", cache_size, fpos);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_short: %s/%s, pos=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, fpos);
+#endif
 
        smb_lock_server(server);
 
@@ -1317,15 +1321,14 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
 
                for (i = 0; i < count; i++)
                {
-                       struct dirent this_ent, *entry = &this_ent;
+                       struct cache_dirent this_ent, *entry = &this_ent;
 
                        p = smb_decode_dirent(server, p, entry);
-                       if (entries_seen == 2 && entry->d_name[0] == '.')
+                       if (entries_seen == 2 && entry->name[0] == '.')
                        {
-                               if (entry->d_reclen == 1)
+                               if (entry->len == 1)
                                        continue;
-                               if (entry->d_name[1] == '.' &&
-                                   entry->d_reclen == 2)
+                               if (entry->name[1] == '.' && entry->len == 2)
                                        continue;
                        }
                        if (entries_seen >= fpos)
@@ -1334,12 +1337,13 @@ smb_proc_readdir_short(struct smb_sb_info *server, struct dentry *dir, int fpos,
                                        entries_seen);
                                smb_add_to_cache(cachep, entry, entries_seen);
                                entries++;
-                       }
+                       } else
+                       {
 #ifdef SMBFS_DEBUG_VERBOSE
-else
 printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n",
 entries_seen, i, fpos);
 #endif
+                       }
                        entries_seen++;
                }
        }
@@ -1352,58 +1356,53 @@ entries_seen, i, fpos);
 
 /*
  * Interpret a long filename structure using the specified info level:
- *   level 1 -- Win NT, Win 95, OS/2
- *   level 2 -- OS/2
+ *   level 1   -- Win NT, Win 95, 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.
+ *
+ * We return a reference to the name string to avoid copying, and perform
+ * any needed upper/lower casing in place.  Note!! Level 259 entries may
+ * not have any space beyond the name, so don't try to write a null byte!
  */
 static char *
 smb_decode_long_dirent(struct smb_sb_info *server, char *p,
-                       struct dirent *entry, int level)
+                       struct cache_dirent *entry, int level)
 {
        char *result;
-       unsigned int len;
+       unsigned int len = 0;
 
        /*
         * SMB doesn't have a concept of inode numbers ...
         */
-       entry->d_ino = 0;
+       entry->ino = 0;
 
        switch (level)
        {
        case 1:
                len = *((unsigned char *) p + 26);
-               entry->d_reclen = len;
-               strncpy(entry->d_name, p + 27, len);
-               entry->d_name[len] = '\0';
-
+               entry->len = len;
+               entry->name = p + 27;
                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);
+               entry->name = p + 12;
                /*
                 * 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')
+               if (len && entry->name[len-1] == '\0')
                        len--;
-               entry->d_name[len] = '\0';
-               entry->d_reclen = len;
+               entry->len = len;
 #ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_decode_long_dirent: info 259, len=%d, name=%s\n",
-len, entry->d_name);
+printk("smb_decode_long_dirent: info 259 at %p, len=%d, name=%s\n",
+p, len, entry->name);
 #endif
                break;
 
@@ -1415,10 +1414,10 @@ len, entry->d_name);
        switch (server->opt.case_handling)
        {
        case SMB_CASE_UPPER:
-               str_upper(entry->d_name);
+               str_upper(entry->name, len);
                break;
        case SMB_CASE_LOWER:
-               str_lower(entry->d_name);
+               str_lower(entry->name, len);
                break;
        default:
                break;
@@ -1460,7 +1459,7 @@ smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
         * 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?)
+        * Hence we use level 259 for NT. (And Win 95 as well ...)
         */
        if (server->opt.protocol >= SMB_PROTOCOL_NT1)
                info_level = 259;
@@ -1520,14 +1519,16 @@ ff_dir_handle, ff_resume_key, ff_lastname, mask);
                        WSET(param, 10, 8 + 4 + 2);     /* resume required +
                                                           close on end +
                                                           continue */
-#ifdef CONFIG_SMB_WIN95
-                       /* Windows 95 is not able to deliver answers
-                          to FIND_NEXT fast enough, so sleep 0.2 seconds */
-                       current->timeout = jiffies + HZ / 5;
-                       current->state = TASK_INTERRUPTIBLE;
-                       schedule();
-                       current->timeout = 0;
-#endif
+                       if (server->mnt->version & 1)
+                       {
+                               /* Windows 95 is not able to deliver answers
+                                * to FIND_NEXT fast enough, so sleep 0.2 sec
+                                */
+                               current->timeout = jiffies + HZ / 5;
+                               current->state = TASK_INTERRUPTIBLE;
+                               schedule();
+                               current->timeout = 0;
+                       }
                }
 
                result = smb_trans2_request(server, command,
@@ -1591,7 +1592,7 @@ resp_data + resp_data_len, resp_data_len, server->packet + server->packet_size);
                        lastname = resp_data + ff_lastname;
                        switch (info_level)
                        {
-                       case 260:
+                       case 259:
                                if (ff_lastname < resp_data_len)
                                        mask_len = resp_data_len - ff_lastname;
                                break;
@@ -1622,7 +1623,7 @@ mask_len, ff_lastname, mask);
                p = resp_data;
                for (i = 0; i < ff_searchcount; i++)
                {
-                       struct dirent this_ent, *entry = &this_ent;
+                       struct cache_dirent this_ent, *entry = &this_ent;
 
                        p = smb_decode_long_dirent(server, p, entry,
                                                        info_level);
@@ -1630,12 +1631,11 @@ mask_len, ff_lastname, mask);
                        pr_debug("smb_readdir_long: got %s\n", entry->name);
 
                        /* ignore . and .. from the server */
-                       if (entries_seen == 2 && entry->d_name[0] == '.')
+                       if (entries_seen == 2 && entry->name[0] == '.')
                        {
-                               if (entry->d_reclen == 1)
+                               if (entry->len == 1)
                                        continue;
-                               if (entry->d_name[1] == '.' && 
-                                   entry->d_reclen == 2)
+                               if (entry->name[1] == '.' && entry->len == 2)
                                        continue;
                        }
                        if (entries_seen >= fpos)
@@ -1742,7 +1742,13 @@ printk("smb_proc_getattr_trans2: for %s: result=%d, rcls=%d, err=%d\n",
        }
        result = -ENOENT;
        if (resp_data_len < 22)
+       {
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_getattr_trans2: not enough data for %s, len=%d\n",
+&param[6], resp_data_len);
+#endif
                goto out;
+       }
 
        attr->f_ctime = date_dos2unix(WVAL(resp_data, 2),
                                      WVAL(resp_data, 0));
@@ -1770,11 +1776,10 @@ smb_proc_getattr(struct dentry *dir, struct qstr *name,
        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)
+        * Win 95 is painfully slow at returning trans2 getattr info ...
+        */
+       if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
+           !(server->mnt->version & 1))
                result = smb_proc_getattr_trans2(server, dir, name, fattr);
        else
                result = smb_proc_getattr_core(server, dir, name, fattr);
@@ -1784,7 +1789,6 @@ smb_proc_getattr(struct dentry *dir, struct qstr *name,
        return result;
 }
 
-
 /* In core protocol, there is only 1 time to be set, we use
    entry->f_mtime, to make touch work. */
 static int
@@ -1808,10 +1812,15 @@ smb_proc_setattr_core(struct smb_sb_info *server,
        *p++ = 0;
 
        smb_setup_bcc(server, p);
-       if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
+       result = smb_request_ok(server, SMBsetatr, 0, 0);
+       if (result < 0)
+       {
                if (smb_retry(server))
                        goto retry;
-
+               goto out;
+       }
+       result = 0;
+out:
        smb_unlock_server(server);
        return result;
 }
@@ -1855,12 +1864,13 @@ smb_proc_setattr_trans2(struct smb_sb_info *server,
                        goto retry;
                goto out;
        }
+       result = 0;
        if (server->rcls != 0)
                result = -smb_errno(server->rcls, server->err);
 
 out:
        smb_unlock_server(server);
-       return 0;
+       return result;
 }
 
 int
@@ -1869,9 +1879,6 @@ smb_proc_setattr(struct smb_sb_info *server, struct dentry *dir,
 {
        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);
        else
index 88113f97b7dce1a7312d6ab2270ed03a3ea179cf..cf95cc3293ed095e2511654cac8db60f65217302 100644 (file)
@@ -7,11 +7,9 @@
  */
 
 #include <linux/sched.h>
-#include <linux/smb_fs.h>
 #include <linux/errno.h>
 #include <linux/socket.h>
 #include <linux/fcntl.h>
-#include <linux/stat.h>
 #include <linux/in.h>
 #include <linux/net.h>
 #include <linux/mm.h>
@@ -19,6 +17,7 @@
 #include <net/scm.h>
 #include <net/ip.h>
 
+#include <linux/smb_fs.h>
 #include <linux/smb.h>
 #include <linux/smbno.h>
 
@@ -97,11 +96,15 @@ smb_data_callback(struct sock *sk, int len)
 
        while (1)
        {
+               result = -EIO;
                if (sk->dead)
                {
+#ifdef SMBFS_PARANOIA
                        printk("smb_data_callback: sock dead!\n");
-                       return;
+#endif
+                       break;
                }
+
                result = _recvfrom(socket, (void *) peek_buf, 1,
                                   MSG_PEEK | MSG_DONTWAIT);
                if (result == -EAGAIN)
@@ -361,6 +364,16 @@ printk("smb_get_length: Invalid NBT packet, code=%x\n", peek_buf[0]);
        return smb_len(peek_buf);
 }
 
+/*
+ * Since we allocate memory in increments of PAGE_SIZE,
+ * round up the packet length to the next multiple.
+ */
+int
+smb_round_length(int len)
+{
+       return (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
 /*
  * smb_receive
  * fs points to the correct segment
@@ -369,6 +382,7 @@ static int
 smb_receive(struct smb_sb_info *server)
 {
        struct socket *socket = server_sock(server);
+       unsigned char * packet = server->packet;
        int len, result;
        unsigned char peek_buf[4];
 
@@ -383,19 +397,22 @@ smb_receive(struct smb_sb_info *server)
         */
        if (len + 4 > server->packet_size)
        {
-               char * packet;
-               pr_debug("smb_receive: Increase packet size from %d to %d\n",
-                       server->packet_size, len + 4);
+               int new_len = smb_round_length(len + 4);
+
+#ifdef SMBFS_PARANOIA
+printk("smb_receive: Increase packet size from %d to %d\n",
+server->packet_size, new_len);
+#endif
                result = -ENOMEM;
-               packet = smb_vmalloc(len + 4);
+               packet = smb_vmalloc(new_len);
                if (packet == NULL)
                        goto out;
                smb_vfree(server->packet);
                server->packet = packet;
-               server->packet_size = len + 4;
+               server->packet_size = new_len;
        }
-       memcpy(server->packet, peek_buf, 4);
-       result = smb_receive_raw(socket, server->packet + 4, len);
+       memcpy(packet, peek_buf, 4);
+       result = smb_receive_raw(socket, packet + 4, len);
        if (result < 0)
        {
 #ifdef SMBFS_DEBUG_VERBOSE
@@ -403,8 +420,8 @@ printk("smb_receive: receive error: %d\n", result);
 #endif
                goto out;
        }
-       server->rcls = *(server->packet+9);
-       server->err = WVAL(server->packet, 11);
+       server->rcls = *(packet+9);
+       server->err = WVAL(packet, 11);
 
 #ifdef SMBFS_DEBUG_VERBOSE
 if (server->rcls != 0)
@@ -415,136 +432,165 @@ out:
 }
 
 /*
- * 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.
+ * This routine checks first for "fast track" processing, as most
+ * packets won't need to be copied. Otherwise, it allocates a new
+ * packet to hold the incoming data.
+ *
+ * Note that the final server packet must be the larger of the two;
+ * server packets aren't allowed to shrink.
  */
 static int
 smb_receive_trans2(struct smb_sb_info *server,
                   int *ldata, unsigned char **data,
-                  int *lparam, unsigned char **param)
+                  int *lparm, unsigned char **parm)
 {
-       int total_data = 0;
-       int total_param = 0;
+       unsigned char *inbuf, *base, *rcv_buf = NULL;
+       unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
+       unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
+       unsigned int total_p = 0, total_d = 0, buf_len = 0;
        int result;
-       unsigned char *rcv_buf;
-       int buf_len;
-       int data_len = 0;
-       int param_len = 0;
-
-       if ((result = smb_receive(server)) < 0)
-       {
-               return result;
-       }
-       if (server->rcls != 0)
-       {
-               *param = *data = server->packet;
-               *ldata = *lparam = 0;
-               return 0;
-       }
-       total_data = WVAL(server->packet, smb_tdrcnt);
-       total_param = WVAL(server->packet, smb_tprcnt);
-
-       pr_debug("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
-
-       if ((total_data > TRANS2_MAX_TRANSFER)
-           || (total_param > TRANS2_MAX_TRANSFER))
-       {
-               pr_debug("smb_receive_trans2: data/param too long\n");
-               return -EIO;
-       }
-       buf_len = total_data + total_param;
-       if (server->packet_size > buf_len)
-       {
-               buf_len = server->packet_size;
-       }
-       if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
-       {
-               pr_debug("smb_receive_trans2: could not alloc data area\n");
-               return -ENOMEM;
-       }
-       *param = rcv_buf;
-       *data = rcv_buf + total_param;
 
        while (1)
        {
-               unsigned char *inbuf = server->packet;
-
-               if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
-                   > total_param)
+               result = smb_receive(server);
+               if (result < 0)
+                       goto out;
+               inbuf = server->packet;
+               if (server->rcls != 0)
                {
-                       pr_debug("smb_receive_trans2: invalid parameters\n");
-                       result = -EIO;
-                       goto fail;
+                       *parm = *data = inbuf;
+                       *ldata = *lparm = 0;
+                       goto out;
                }
-               memcpy(*param + WVAL(inbuf, smb_prdisp),
-                      smb_base(inbuf) + WVAL(inbuf, smb_proff),
-                      WVAL(inbuf, smb_prcnt));
-               param_len += WVAL(inbuf, smb_prcnt);
-
-               if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
-                   > total_data)
+               /*
+                * Extract the control data from the packet.
+                */
+               data_tot    = WVAL(inbuf, smb_tdrcnt);
+               parm_tot    = WVAL(inbuf, smb_tprcnt);
+               parm_disp   = WVAL(inbuf, smb_prdisp);
+               parm_offset = WVAL(inbuf, smb_proff);
+               parm_count  = WVAL(inbuf, smb_prcnt);
+               data_disp   = WVAL(inbuf, smb_drdisp);
+               data_offset = WVAL(inbuf, smb_droff);
+               data_count  = WVAL(inbuf, smb_drcnt);
+               base = smb_base(inbuf);
+
+               /*
+                * Assume success and increment lengths.
+                */
+               parm_len += parm_count;
+               data_len += data_count;
+
+               if (!rcv_buf)
                {
-                       pr_debug("smb_receive_trans2: invalid data block\n");
-                       result = -EIO;
-                       goto fail;
+                       /*
+                        * Check for fast track processing ... just this packet.
+                        */
+                       if (parm_count == parm_tot && data_count == data_tot)
+                       {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_receive_trans2: fast track, parm=%u %u %u, data=%u %u %u\n",
+parm_disp, parm_offset, parm_count, data_disp, data_offset, data_count);
+#endif
+                               *parm  = base + parm_offset;
+                               *data  = base + data_offset;
+                               goto success;
+                       }
+
+                       if (parm_tot > TRANS2_MAX_TRANSFER ||
+                           data_tot > TRANS2_MAX_TRANSFER)
+                               goto out_too_long;
+
+                       /*
+                        * Save the total parameter and data length.
+                        */
+                       total_d = data_tot;
+                       total_p = parm_tot;
+
+                       buf_len = total_d + total_p;
+                       if (server->packet_size > buf_len)
+                               buf_len = server->packet_size;
+                       buf_len = smb_round_length(buf_len);
+
+                       rcv_buf = smb_vmalloc(buf_len);
+                       if (!rcv_buf)
+                               goto out_no_mem;
+                       *parm = rcv_buf;
+                       *data = rcv_buf + total_p;
                }
-               pr_debug("disp: %d, off: %d, cnt: %d\n",
-                        WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
-                        WVAL(inbuf, smb_drcnt));
+               else if (data_tot > total_d || parm_tot > total_p)
+                       goto out_data_grew;
 
-               memcpy(*data + WVAL(inbuf, smb_drdisp),
-                      smb_base(inbuf) + WVAL(inbuf, smb_droff),
-                      WVAL(inbuf, smb_drcnt));
-               data_len += WVAL(inbuf, smb_drcnt);
-
-               if ((WVAL(inbuf, smb_tdrcnt) > total_data)
-                   || (WVAL(inbuf, smb_tprcnt) > total_param))
-               {
-                       pr_debug("smb_receive_trans2: data/params grew!\n");
-                       result = -EIO;
-                       goto fail;
-               }
-               /* the total lengths might shrink! */
-               total_data = WVAL(inbuf, smb_tdrcnt);
-               total_param = WVAL(inbuf, smb_tprcnt);
+               if (parm_disp + parm_count > total_p)
+                       goto out_bad_parm;
+               if (data_disp + data_count > total_d)
+                       goto out_bad_data;
+               memcpy(*parm + parm_disp, base + parm_offset, parm_count);
+               memcpy(*data + data_disp, base + data_offset, data_count);
 
 #ifdef SMBFS_PARANOIA
-if ((data_len >= total_data || param_len >= total_param) &&
-   !(data_len >= total_data && param_len >= total_param))
-printk("smb_receive_trans2: dlen=%d, tdata=%d, plen=%d, tlen=%d\n",
-data_len, total_data, param_len, total_param);
+printk("smb_receive_trans2: copied, parm=%u of %u, data=%u of %u\n",
+parm_len, parm_tot, data_len, data_tot);
 #endif
-               /* shouldn't this be an OR test? don't want to overrun */
-               if ((data_len >= total_data) && (param_len >= total_param))
-               {
+               /*
+                * Check whether we've received all of the data. Note that
+                * we use the packet totals -- total lengths might shrink!
+                */
+               if (data_len >= data_tot && parm_len >= parm_tot)
                        break;
-               }
-               if ((result = smb_receive(server)) < 0)
-               {
-                       goto fail;
-               }
-               result = -EIO;
-               if (server->rcls != 0)
-                       goto fail;
        }
-       *ldata = data_len;
-       *lparam = param_len;
 
+       /*
+        * Install the new packet.  Note that it's possible, though
+        * unlikely, that the new packet could be smaller than the
+        * old one, in which case we just copy the data.
+        */
+       inbuf = server->packet;
+       if (buf_len >= server->packet_size)
+       {
+               server->packet_size = buf_len;
+               server->packet = rcv_buf;
+               rcv_buf = inbuf;
+       } else
+       {
 #ifdef SMBFS_PARANOIA
-if (buf_len < server->packet_size)
-printk("smb_receive_trans2: changing packet, old size=%d, new size=%d\n",
+printk("smb_receive_trans2: copying data, old size=%d, new size=%u\n",
 server->packet_size, buf_len);
 #endif
-       smb_vfree(server->packet);
-       server->packet = rcv_buf;
-       server->packet_size = buf_len;
-       return 0;
+               memcpy(inbuf, rcv_buf, parm_len + data_len);
+       }
 
-      fail:
-       smb_vfree(rcv_buf);
+success:
+       *ldata = data_len;
+       *lparm = parm_len;
+out:
+       if (rcv_buf)
+               smb_vfree(rcv_buf);
        return result;
+
+out_no_mem:
+#ifdef SMBFS_PARANOIA
+       printk("smb_receive_trans2: couldn't allocate data area\n");
+#endif
+       result = -ENOMEM;
+       goto out;
+out_too_long:
+       printk("smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
+               data_tot, parm_tot);
+       goto out_error;
+out_data_grew:
+       printk("smb_receive_trans2: data/params grew!\n");
+       goto out_error;
+out_bad_parm:
+       printk("smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
+               parm_disp, parm_count, parm_tot);
+       goto out_error;
+out_bad_data:
+       printk("smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
+               data_disp, data_count, data_tot);
+out_error:
+       result = -EIO;
+       goto out;
 }
 
 /*
@@ -759,14 +805,13 @@ smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
        }
        if (result < 0)
                goto bad_conn;
-       pr_debug("smb_trans2_request: result = %d\n", result);
 
 out:
        return result;
 
 bad_conn:
 #ifdef SMBFS_PARANOIA
-printk("smb_trans2_request: connection bad, setting invalid\n");
+printk("smb_trans2_request: result=%d, setting invalid\n", result);
 #endif
        server->state = CONN_INVALID;
        smb_invalidate_inodes(server);
index cc8613c1593d67ce738cb2292a801f4c988206b1..c1ff4176c874fa7d4900c43d8dea3813564d34f1 100644 (file)
@@ -1415,8 +1415,9 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
        struct buffer_head *old_bh,*new_bh,*dotdot_bh;
        struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
        loff_t old_offset,new_offset,old_longname_offset;
-       int old_slots,old_ino,new_ino,dotdot_ino,ino;
-       struct inode *old_inode, *new_inode, *dotdot_inode, *walk;
+       int old_slots,old_ino,new_ino,dotdot_ino;
+       struct inode *old_inode, *new_inode, *dotdot_inode;
+       struct dentry *walk;
        int res, is_dir, i;
        int locked = 0;
        struct slot_info sinfo;
@@ -1451,25 +1452,13 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
                        res = -EINVAL;
                        goto rename_done;
                }
-               if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+               walk = new_dentry;
                /* prevent moving directory below itself */
-               while (walk->i_ino != MSDOS_ROOT_INO) {
-                       ino = fat_parent_ino(walk,1);
-                       iput(walk);
-                       if (ino < 0) {
-                               res = ino;
-                               goto rename_done;
-                       }
-                       if (ino == old_ino) {
-                               res = -EINVAL;
-                               goto rename_done;
-                       }
-                       if (!(walk = iget(new_dir->i_sb,ino))) {
-                               res = -EIO;
-                               goto rename_done;
-                       }
+               for (;;) {
+                       if (walk == old_dentry) return -EINVAL;
+                       if (walk == walk->d_parent) break;
+                       walk = walk->d_parent;
                }
-               iput(walk);
        }
 
        res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo);
@@ -1589,8 +1578,9 @@ int vfat_rename(struct inode *old_dir,struct dentry *old_dentry,
        }
 
        if (res > 0) res = 0;
-       d_instantiate(new_dentry,new_inode);
-       d_delete(old_dentry);
+       if (res == 0) {
+               d_move(old_dentry, new_dentry);
+       }
 
 rename_done:
        if (locked)
index 43a9d6d4705b81a4d5c7e0320c7b5bb6f9dda3de..ff34ee0f23bd2ed3d34a08cd8ed109a017bcf338 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _ASM_SOCKET_H
 #define _ASM_SOCKET_H
 
-#include <linux/types.h>
-#include <asm/ioctl.h>
 #include <asm/sockios.h>
 
 /* For setsockoptions(2) */
index 34a54bc1a5af2d2899bf1430b77f62a2721a7758..f5d7b88cfbb0a934253c1ba6cb3357549c2e946d 100644 (file)
@@ -17,7 +17,8 @@
  */
 struct qstr {
        const unsigned char * name;
-       unsigned int len, hash;
+       unsigned int len;
+       unsigned int hash;
 };
 
 /* Name hashing routines. Initial hash value */
@@ -38,6 +39,15 @@ static inline unsigned long end_name_hash(unsigned long hash)
        return (unsigned int) hash;
 }
 
+/* Compute the hash for a name string. */
+static inline unsigned int full_name_hash(const char * name, unsigned int len)
+{
+       unsigned long hash = init_name_hash();
+       while (len--)
+               hash = partial_name_hash(*name++, hash);
+       return end_name_hash(hash);
+}
+
 struct dentry {
        int d_count;
        unsigned int d_flags;
index c74ad05fb46a3cef711af9310f643275f401f183..1b32dcebedec7b2a061bb56bdd3106efa7af399b 100644 (file)
@@ -176,7 +176,7 @@ extern int nfs_lock(struct file *file, int cmd, struct file_lock *fl);
  */
 extern int  nfs_writepage(struct inode *, struct page *);
 extern int  nfs_check_error(struct inode *);
-extern int  nfs_flush_dirty_pages(struct inode *, off_t, off_t);
+extern int  nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
 extern int  nfs_truncate_dirty_pages(struct inode *, unsigned long);
 extern void nfs_invalidate_pages(struct inode *);
 extern int  nfs_updatepage(struct inode *, struct page *, const char *,
index f0ccb3bb9ada48e4b8bca73dee0267282f95bafe..338160e48b485bbd858f5ada03e8e697c92e8a62 100644 (file)
@@ -41,7 +41,7 @@
 #undef __FDMASK
 #define        __FDMASK(d)     (1UL << ((d) % __NFDBITS))
 
-typedef struct fd_set {
+typedef struct {
        unsigned long fds_bits [__FDSET_LONGS];
 } __kernel_fd_set;
 
index 9f25819bef7f687c43893e6298694e8abd24a5ba..9298bcbbca2d20a8d7b569b1146f5e7a7f4b1c24 100644 (file)
@@ -65,20 +65,18 @@ smb_vfree(void *obj)
 
 #endif /* DEBUG_SMB_MALLOC */
 
-struct smb_sb_info;
+/* linux/fs/smbfs/mmap.c */
+int smb_mmap(struct file *, struct vm_area_struct *);
 
 /* linux/fs/smbfs/file.c */
 extern struct inode_operations smb_file_inode_operations;
 
 /* linux/fs/smbfs/dir.c */
 extern struct inode_operations smb_dir_inode_operations;
-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);
+int smb_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
 
 /* linux/fs/smbfs/inode.c */
 struct super_block *smb_read_super(struct super_block *, void *, int);
@@ -126,6 +124,7 @@ 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_round_length(int);
 int smb_valid_socket(struct inode *);
 void smb_close_socket(struct smb_sb_info *);
 int smb_release(struct smb_sb_info *server);
@@ -141,9 +140,6 @@ int smb_trans2_request(struct smb_sb_info *server, __u16 trans2_command,
                       int *lrdata, unsigned char **rdata,
                       int *lrparam, unsigned char **rparam);
 
-/* linux/fs/smbfs/mmap.c */
-int smb_mmap(struct file * file, struct vm_area_struct * vma);
-
 /* fs/smbfs/cache.c */
 
 /*
@@ -206,7 +202,7 @@ 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);
+void smb_add_to_cache(struct cache_head *, struct cache_dirent *, off_t);
 int  smb_find_in_cache(struct cache_head *, off_t, struct cache_dirent *);
 void smb_invalid_dir_cache(struct inode *);
 
index 95d3cc03dd6f6fae2c1627c179f9ec7dda237705..4e277ad3cca277a7e96764ad2af484c0fa851ed6 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <linux/types.h>
 #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
@@ -24,7 +23,7 @@ struct smb_sb_info {
         enum smb_conn_state state;
        struct file * sock_file;
 
-        struct smb_mount_data m;
+        struct smb_mount_data *mnt;
 
        /* Connections are counted. Each time a new socket arrives,
         * generation is incremented.
index 4b358b1a83ae72dc57a362ad0681a6812dd5c17d..6066efeca5f3ad22724412a5a345fcd9e2c40bb9 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -92,25 +92,21 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
        struct mm_struct *mm = current->mm;
 
        lock_kernel();
-       retval = mm->brk;
        if (brk < mm->end_code)
                goto out;
        newbrk = PAGE_ALIGN(brk);
        oldbrk = PAGE_ALIGN(mm->brk);
-       if (oldbrk == newbrk) {
-               retval = mm->brk = brk;
-               goto out;
-       }
+       if (oldbrk == newbrk)
+               goto set_brk;
 
        /* Always allow shrinking brk. */
        if (brk <= mm->brk) {
-               retval = mm->brk = brk;
-               do_munmap(newbrk, oldbrk-newbrk);
+               if (!do_munmap(newbrk, oldbrk-newbrk))
+                       goto set_brk;
                goto out;
        }
 
        /* Check against rlimit and stack.. */
-       retval = mm->brk;
        rlim = current->rlim[RLIMIT_DATA].rlim_cur;
        if (rlim >= RLIM_INFINITY)
                rlim = ~0;
@@ -126,12 +122,14 @@ asmlinkage unsigned long sys_brk(unsigned long brk)
                goto out;
 
        /* Ok, looks good - let it rip. */
-       if(do_mmap(NULL, oldbrk, newbrk-oldbrk,
+       if (do_mmap(NULL, oldbrk, newbrk-oldbrk,
                   PROT_READ|PROT_WRITE|PROT_EXEC,
-                  MAP_FIXED|MAP_PRIVATE, 0) == oldbrk)
-               mm->brk = brk;
-       retval = mm->brk;
+                  MAP_FIXED|MAP_PRIVATE, 0) != oldbrk)
+               goto out;
+set_brk:
+       mm->brk = brk;
 out:
+       retval = mm->brk;
        unlock_kernel();
        return retval;
 }
@@ -163,7 +161,7 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
 {
        struct mm_struct * mm = current->mm;
        struct vm_area_struct * vma;
-       int correct_wcount = 0;
+       int correct_wcount = 0, error;
 
        if ((len = PAGE_ALIGN(len)) == 0)
                return addr;
@@ -262,26 +260,24 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
        vma->vm_dentry = NULL;
        vma->vm_pte = 0;
 
-       do_munmap(addr, len);   /* Clear old maps */
+       /* Clear old maps */
+       error = -ENOMEM;
+       if (do_munmap(addr, len))
+               goto free_vma;
 
        /* Check against address space limit. */
        if ((mm->total_vm << PAGE_SHIFT) + len
-           > current->rlim[RLIMIT_AS].rlim_cur) {
-               kmem_cache_free(vm_area_cachep, vma);
-               return -ENOMEM;
-       }
+           > current->rlim[RLIMIT_AS].rlim_cur)
+               goto free_vma;
 
        /* Private writable mapping? Check memory availability.. */
-       if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE) {
-               if (!(flags & MAP_NORESERVE) &&
-                   !vm_enough_memory(len >> PAGE_SHIFT)) {
-                       kmem_cache_free(vm_area_cachep, vma);
-                       return -ENOMEM;
-               }
-       }
+       if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
+           !(flags & MAP_NORESERVE)                             &&
+           !vm_enough_memory(len >> PAGE_SHIFT))
+               goto free_vma;
 
+       error = 0;
        if (file) {
-               int error = 0;
                if (vma->vm_flags & VM_DENYWRITE) {
                        if (file->f_dentry->d_inode->i_writecount > 0)
                                error = -ETXTBSY;
@@ -298,23 +294,22 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
                if (!error)
                        error = file->f_op->mmap(file, vma);
        
-               if (error) {
-                       if (correct_wcount)
-                               file->f_dentry->d_inode->i_writecount++;
-                       kmem_cache_free(vm_area_cachep, vma);
-                       return error;
-               }
        }
+       /* Fix up the count if necessary, then check for an error */
+       if (correct_wcount)
+               file->f_dentry->d_inode->i_writecount++;
+       if (error)
+               goto free_vma;
 
+       /*
+        * merge_segments may merge our vma, so we can't refer to it
+        * after the call.  Save the values we need now ...
+        */
        flags = vma->vm_flags;
+       addr = vma->vm_start; /* can addr have changed?? */
        insert_vm_struct(mm, vma);
-       if (correct_wcount)
-               file->f_dentry->d_inode->i_writecount++;
        merge_segments(mm, vma->vm_start, vma->vm_end);
        
-       addr = vma->vm_start;
-
-       /* merge_segments might have merged our vma, so we can't use it any more */
        mm->total_vm += len >> PAGE_SHIFT;
        if ((flags & VM_LOCKED) && !(flags & VM_IO)) {
                unsigned long start = addr;
@@ -328,6 +323,10 @@ unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
                } while (len > 0);
        }
        return addr;
+
+free_vma:
+       kmem_cache_free(vm_area_cachep, vma);
+       return error;
 }
 
 /* Get an address range which is currently unmapped.
index e0cfe1fefe762f9a862035c494edf8c308e95053..75f2841241e7ec9234cf7dd400f13a381aef0480 100644 (file)
@@ -18,9 +18,6 @@
 #include <linux/swapctl.h>
 #include <linux/init.h>
 
-#include <asm/dma.h>
-#include <asm/system.h> /* for cli()/sti() */
-#include <asm/uaccess.h> /* for cop_to/from_user */
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
@@ -60,31 +57,47 @@ int add_to_swap_cache(struct page *page, unsigned long entry)
        return 0;
 }
 
+/*
+ * If swap_map[] reaches 127, the entries are treated as "permanent".
+ */
 void swap_duplicate(unsigned long entry)
 {
        struct swap_info_struct * p;
        unsigned long offset, type;
 
        if (!entry)
-               return;
-       offset = SWP_OFFSET(entry);
+               goto out;
        type = SWP_TYPE(entry);
        if (type & SHM_SWP_TYPE)
-               return;
-       if (type >= nr_swapfiles) {
-               printk("Trying to duplicate nonexistent swap-page\n");
-               return;
-       }
+               goto out;
+       if (type >= nr_swapfiles)
+               goto bad_file;
        p = type + swap_info;
-       if (offset >= p->max) {
-               printk("swap_duplicate: weirdness\n");
-               return;
-       }
-       if (!p->swap_map[offset]) {
-               printk("swap_duplicate: trying to duplicate unused page\n");
-               return;
+       offset = SWP_OFFSET(entry);
+       if (offset >= p->max)
+               goto bad_offset;
+       if (!p->swap_map[offset])
+               goto bad_unused;
+       if (p->swap_map[offset] < 126)
+               p->swap_map[offset]++;
+       else {
+               static int overflow = 0;
+               if (overflow++ < 5)
+                       printk("swap_duplicate: entry %08lx map count=%d\n",
+                               entry, p->swap_map[offset]);
+               p->swap_map[offset] = 127;
        }
-       p->swap_map[offset]++;
+out:
        return;
+
+bad_file:
+       printk("swap_duplicate: Trying to duplicate nonexistent swap-page\n");
+       goto out;
+bad_offset:
+       printk("swap_duplicate: offset exceeds max\n");
+       goto out;
+bad_unused:
+       printk("swap_duplicate: unused page\n");
+       goto out;
 }
 
index 094ba576d048ef537e7f714da6dcff9a2ec3f76e..36ecdd5ba3a3248a9dd5d0ad75c81ded58cf1da0 100644 (file)
 #include <linux/malloc.h>
 #include <linux/blkdev.h> /* for blk_size */
 #include <linux/vmalloc.h>
-#include <linux/dcache.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>
 
@@ -122,52 +118,60 @@ unsigned long get_swap_page(void)
        }
 }
 
+/*
+ * If the swap count overflows (swap_map[] == 127), the entry is considered
+ * "permanent" and can't be reclaimed until the swap device is closed.
+ */
 void swap_free(unsigned long entry)
 {
        struct swap_info_struct * p;
        unsigned long offset, type;
 
        if (!entry)
-               return;
+               goto out;
        type = SWP_TYPE(entry);
        if (type & SHM_SWP_TYPE)
-               return;
-       if (type >= nr_swapfiles) {
-               printk("Trying to free nonexistent swap-page\n");
-               return;
-       }
+               goto out;
+       if (type >= nr_swapfiles)
+               goto bad_nofile;
        p = & swap_info[type];
+       if (!(p->flags & SWP_USED))
+               goto bad_device;
+       if (p->prio > swap_info[swap_list.next].prio)
+               swap_list.next = swap_list.head;
        offset = SWP_OFFSET(entry);
-       if (offset >= p->max) {
-               printk("swap_free: weirdness\n");
-               return;
-       }
-       if (!(p->flags & SWP_USED)) {
-               printk("Trying to free swap from unused swap-device\n");
-               return;
-       }
+       if (offset >= p->max)
+               goto bad_offset;
        if (offset < p->lowest_bit)
                p->lowest_bit = offset;
        if (offset > p->highest_bit)
                p->highest_bit = offset;
        if (!p->swap_map[offset])
-               printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
-       else
+               goto bad_free;
+       if (p->swap_map[offset] < 127) {
                if (!--p->swap_map[offset])
                        nr_swap_pages++;
-       if (p->prio > swap_info[swap_list.next].prio) {
-           swap_list.next = swap_list.head;
        }
+out:
+       return;
+
+bad_nofile:
+       printk("swap_free: Trying to free nonexistent swap-page\n");
+       goto out;
+bad_device:
+       printk("swap_free: Trying to free swap from unused swap-device\n");
+       goto out;
+bad_offset:
+       printk("swap_free: offset exceeds max\n");
+       goto out;
+bad_free:
+       printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
+       goto out;
 }
 
 /*
- * Trying to stop swapping from a file is fraught with races, so
- * we repeat quite a bit here when we have to pause. swapoff()
- * isn't exactly timing-critical, so who cares (but this is /really/
- * inefficient, ugh).
- *
- * We return 1 after having slept, which makes the process start over
- * from the beginning for this process..
+ * The swap entry has been read in advance, and we return 1 to indicate
+ * that the page has been used or is no longer needed.
  */
 static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
        pte_t *dir, unsigned long entry, unsigned long page)
@@ -198,9 +202,8 @@ static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
        if (pte_val(pte) != entry)
                return 0;
        set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
-       flush_tlb_page(vma, address);
        ++vma->vm_mm->rss;
-       swap_free(pte_val(pte));
+       swap_free(entry);
        return 1;
 }
 
@@ -296,18 +299,6 @@ static int unuse_process(struct mm_struct * mm, unsigned long entry,
        return 0;
 }
 
-static unsigned long find_swap_entry(int type)
-{
-       struct swap_info_struct * p = &swap_info[type];
-       int i;
-
-       for (i = 1 ; i < p->max ; i++) {
-               if (p->swap_map[i] > 0 && p->swap_map[i] != 0x80)
-                       return SWP_ENTRY(type, i);
-       }
-       return 0;
-}
-
 /*
  * We completely avoid races by reading each swap page in advance,
  * and then search for the process using it.  All the necessary
@@ -315,14 +306,13 @@ static unsigned long find_swap_entry(int type)
  */
 static int try_to_unuse(unsigned int type)
 {
-       unsigned long page = 0;
+       struct swap_info_struct * si = &swap_info[type];
        struct task_struct *p;
+       unsigned long page = 0;
        unsigned long entry;
+       int i;
 
-       /*
-        * Find all swap entries in use ...
-        */
-       while ((entry = find_swap_entry(type)) != 0) {
+       while (1) {
                if (!page) {
                        page = __get_free_page(GFP_KERNEL);
                        if (!page)
@@ -330,8 +320,16 @@ static int try_to_unuse(unsigned int type)
                }
 
                /*
-                * Read in the page, and then free the swap page.
-                */
+               * Find a swap page in use and read it in.
+               */
+               for (i = 1 , entry = 0; i < si->max ; i++) {
+                       if (si->swap_map[i] > 0 && si->swap_map[i] != 0x80) {
+                               entry = SWP_ENTRY(type, i);
+                               break;
+                       }
+               }
+               if (!entry)
+                       break;
                read_swap_page(entry, (char *) page);
 
                read_lock(&tasklist_lock);
@@ -344,9 +342,19 @@ static int try_to_unuse(unsigned int type)
        unlock:
                read_unlock(&tasklist_lock);
                if (page) {
-                       printk("try_to_unuse: didn't find entry %8lx\n",
-                               entry);
-                       swap_free(entry);
+                       /*
+                        * If we couldn't find an entry, there are several
+                        * possible reasons: someone else freed it first,
+                        * we freed the last reference to an overflowed entry,
+                        * or the system has lost track of the use counts.
+                        */
+                       if (si->swap_map[i] != 0) {
+                               if (si->swap_map[i] != 127)
+                                       printk("try_to_unuse: entry %08lx "
+                                              "not in use\n", entry);
+                               si->swap_map[i] = 0;
+                               nr_swap_pages++;
+                       }
                }
        }