diff -u --recursive --new-file v2.1.60/linux/CREDITS linux/CREDITS --- v2.1.60/linux/CREDITS Sat Oct 25 02:44:14 1997 +++ linux/CREDITS Tue Oct 28 13:42:37 1997 @@ -215,7 +215,7 @@ 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 diff -u --recursive --new-file v2.1.60/linux/MAINTAINERS linux/MAINTAINERS --- v2.1.60/linux/MAINTAINERS Thu Sep 11 09:02:23 1997 +++ linux/MAINTAINERS Tue Oct 28 13:42:37 1997 @@ -368,9 +368,9 @@ 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: diff -u --recursive --new-file v2.1.60/linux/Makefile linux/Makefile --- v2.1.60/linux/Makefile Sat Oct 25 02:44:14 1997 +++ linux/Makefile Wed Oct 29 14:09:50 1997 @@ -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/) diff -u --recursive --new-file v2.1.60/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v2.1.60/linux/arch/i386/defconfig Tue Sep 23 16:48:46 1997 +++ linux/arch/i386/defconfig Thu Oct 30 10:47:43 1997 @@ -198,10 +198,12 @@ # 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_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 @@ -217,6 +218,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 diff -u --recursive --new-file v2.1.60/linux/fs/Config.in linux/fs/Config.in --- v2.1.60/linux/fs/Config.in Sat Oct 25 02:44:17 1997 +++ linux/fs/Config.in Thu Oct 30 10:47:09 1997 @@ -10,18 +10,15 @@ 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 diff -u --recursive --new-file v2.1.60/linux/fs/autofs/root.c linux/fs/autofs/root.c --- v2.1.60/linux/fs/autofs/root.c Wed Sep 24 20:05:47 1997 +++ linux/fs/autofs/root.c Thu Oct 30 15:49:33 1997 @@ -100,7 +100,7 @@ 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 @@ 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 @@ 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 struct dentry_operations autofs_dentry_operations = { - autofs_revalidate, + autofs_revalidate, /* d_revalidate */ NULL, /* d_hash */ NULL, /* d_compare */ }; diff -u --recursive --new-file v2.1.60/linux/fs/dcache.c linux/fs/dcache.c --- v2.1.60/linux/fs/dcache.c Sat Oct 25 02:44:17 1997 +++ linux/fs/dcache.c Thu Oct 30 12:54:32 1997 @@ -465,12 +465,13 @@ 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,14 +490,9 @@ 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); } /* diff -u --recursive --new-file v2.1.60/linux/fs/isofs/dir.c linux/fs/isofs/dir.c --- v2.1.60/linux/fs/isofs/dir.c Sat Oct 25 02:44:17 1997 +++ linux/fs/isofs/dir.c Wed Oct 29 15:42:28 1997 @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -206,10 +207,13 @@ } } +#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); diff -u --recursive --new-file v2.1.60/linux/fs/isofs/inode.c linux/fs/isofs/inode.c --- v2.1.60/linux/fs/isofs/inode.c Sat Oct 25 02:44:17 1997 +++ linux/fs/isofs/inode.c Wed Oct 29 15:51:04 1997 @@ -89,8 +89,7 @@ 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 @@ #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 @@ 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 @@ 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 @@ 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) { diff -u --recursive --new-file v2.1.60/linux/fs/namei.c linux/fs/namei.c --- v2.1.60/linux/fs/namei.c Sat Oct 25 02:44:17 1997 +++ linux/fs/namei.c Thu Oct 30 13:06:49 1997 @@ -237,8 +237,7 @@ 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 @@ 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 @@ 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 @@ } } - 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; diff -u --recursive --new-file v2.1.60/linux/fs/nfs/dir.c linux/fs/nfs/dir.c --- v2.1.60/linux/fs/nfs/dir.c Sat Oct 25 02:44:17 1997 +++ linux/fs/nfs/dir.c Thu Oct 30 10:29:05 1997 @@ -333,7 +333,6 @@ { 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 @@ 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 @@ 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 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ 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 @@ * 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 @@ 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; } diff -u --recursive --new-file v2.1.60/linux/fs/nfs/file.c linux/fs/nfs/file.c --- v2.1.60/linux/fs/nfs/file.c Sat Oct 25 02:44:17 1997 +++ linux/fs/nfs/file.c Wed Oct 29 14:07:39 1997 @@ -83,53 +83,77 @@ # 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 @@ 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 @@ 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 @@ 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 @@ * 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) diff -u --recursive --new-file v2.1.60/linux/fs/nfs/inode.c linux/fs/nfs/inode.c --- v2.1.60/linux/fs/nfs/inode.c Sat Oct 25 02:44:17 1997 +++ linux/fs/nfs/inode.c Wed Oct 29 14:07:39 1997 @@ -91,16 +91,19 @@ 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 @@ 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); } diff -u --recursive --new-file v2.1.60/linux/fs/nfs/write.c linux/fs/nfs/write.c --- v2.1.60/linux/fs/nfs/write.c Sat Oct 25 02:44:17 1997 +++ linux/fs/nfs/write.c Wed Oct 29 14:07:39 1997 @@ -286,16 +286,17 @@ * 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 @@ 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 @@ } /* 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 @@ 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 @@ 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 @@ /* * 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 @@ 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 @@ * 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 @@ * 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 @@ 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 @@ * 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)) { diff -u --recursive --new-file v2.1.60/linux/fs/nls/Config.in linux/fs/nls/Config.in --- v2.1.60/linux/fs/nls/Config.in Sat Oct 25 02:44:18 1997 +++ linux/fs/nls/Config.in Thu Oct 30 10:47:09 1997 @@ -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 diff -u --recursive --new-file v2.1.60/linux/fs/nls/Makefile linux/fs/nls/Makefile --- v2.1.60/linux/fs/nls/Makefile Sat Oct 25 02:44:18 1997 +++ linux/fs/nls/Makefile Wed Oct 29 15:30:09 1997 @@ -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 @@ -301,5 +293,8 @@ M_OBJS += nls_koi8-r.o endif endif + +O_TARGET = nls.o +OX_OBJS = $(NLS) include $(TOPDIR)/Rules.make diff -u --recursive --new-file v2.1.60/linux/fs/nls/nls_base.c linux/fs/nls/nls_base.c --- v2.1.60/linux/fs/nls/nls_base.c Sat Oct 25 02:44:18 1997 +++ linux/fs/nls/nls_base.c Tue Oct 28 13:42:37 1997 @@ -6,7 +6,6 @@ * */ -#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S)) #include #include #include @@ -479,11 +478,10 @@ #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 diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/cache.c linux/fs/smbfs/cache.c --- v2.1.60/linux/fs/smbfs/cache.c Wed Oct 15 16:04:23 1997 +++ linux/fs/smbfs/cache.c Thu Oct 30 11:03:53 1997 @@ -130,13 +130,14 @@ * 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 @@ 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", diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/dir.c linux/fs/smbfs/dir.c --- v2.1.60/linux/fs/smbfs/dir.c Sat Oct 25 02:44:18 1997 +++ linux/fs/smbfs/dir.c Thu Oct 30 11:03:53 1997 @@ -8,20 +8,14 @@ #include #include -#include #include -#include -#include #include #include -#include - -#include -#include #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 @@ * 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 @@ { 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 @@ 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 @@ /* * Get the cache pointer ... */ + result = -EIO; cachep = smb_get_dircache(dentry); if (!cachep) goto out; @@ -147,19 +142,20 @@ { 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 @@ * 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 @@ /* * 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 @@ * 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 @@ /* * 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 @@ * 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 @@ 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 @@ { 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; diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/inode.c linux/fs/smbfs/inode.c --- v2.1.60/linux/fs/smbfs/inode.c Wed Oct 15 16:04:23 1997 +++ linux/fs/smbfs/inode.c Thu Oct 30 11:03:53 1997 @@ -6,14 +6,10 @@ * */ -#define SMBFS_DCACHE_EXT 1 - #include #include #include -#include -#include #include #include #include @@ -23,6 +19,9 @@ #include #include #include +#include +#include +#include #include #include @@ -30,17 +29,11 @@ #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 @@ 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 @@ } /* - * 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 @@ /* * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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) diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/ioctl.c linux/fs/smbfs/ioctl.c --- v2.1.60/linux/fs/smbfs/ioctl.c Wed Sep 3 20:52:43 1997 +++ linux/fs/smbfs/ioctl.c Thu Oct 30 11:03:53 1997 @@ -8,10 +8,11 @@ #include #include -#include #include #include #include +#include +#include #include @@ -19,33 +20,33 @@ 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; } diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c --- v2.1.60/linux/fs/smbfs/proc.c Wed Oct 15 16:04:23 1997 +++ linux/fs/smbfs/proc.c Thu Oct 30 11:12:51 1997 @@ -9,10 +9,7 @@ * by Riccardo Facchetti */ -#include #include -#include -#include #include #include #include @@ -20,10 +17,16 @@ #include #include #include +#include +#include +#include -#include #include +#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)) @@ -33,12 +36,6 @@ #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 @@ } 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ } 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_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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ /* * 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } result = -ENOENT; if (resp_data_len < 22) + { +#ifdef SMBFS_PARANOIA +printk("smb_proc_getattr_trans2: not enough data for %s, len=%d\n", +¶m[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_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 @@ 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 @@ *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 @@ 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 @@ { 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 diff -u --recursive --new-file v2.1.60/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c --- v2.1.60/linux/fs/smbfs/sock.c Wed Oct 15 16:04:23 1997 +++ linux/fs/smbfs/sock.c Thu Oct 30 11:03:53 1997 @@ -7,11 +7,9 @@ */ #include -#include #include #include #include -#include #include #include #include @@ -19,6 +17,7 @@ #include #include +#include #include #include @@ -97,11 +96,15 @@ 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) @@ -362,6 +365,16 @@ } /* + * 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 @@ 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 @@ */ 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 @@ #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 @@ } /* - * 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) - { - pr_debug("smb_receive_trans2: invalid parameters\n"); - result = -EIO; - goto fail; - } - 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) - { - pr_debug("smb_receive_trans2: invalid data block\n"); - result = -EIO; - goto fail; - } - pr_debug("disp: %d, off: %d, cnt: %d\n", - WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff), - WVAL(inbuf, smb_drcnt)); - - 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)) + result = smb_receive(server); + if (result < 0) + goto out; + inbuf = server->packet; + if (server->rcls != 0) { - pr_debug("smb_receive_trans2: data/params grew!\n"); - result = -EIO; - goto fail; + *parm = *data = inbuf; + *ldata = *lparm = 0; + goto out; } - /* the total lengths might shrink! */ - total_data = WVAL(inbuf, smb_tdrcnt); - total_param = WVAL(inbuf, smb_tprcnt); + /* + * 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) + { + /* + * 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; + } + else if (data_tot > total_d || parm_tot > total_p) + goto out_data_grew; + + 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 @@ } 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); diff -u --recursive --new-file v2.1.60/linux/fs/vfat/namei.c linux/fs/vfat/namei.c --- v2.1.60/linux/fs/vfat/namei.c Sat Oct 25 02:44:18 1997 +++ linux/fs/vfat/namei.c Tue Oct 28 13:42:37 1997 @@ -1415,8 +1415,9 @@ 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 @@ 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 @@ } 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) diff -u --recursive --new-file v2.1.60/linux/include/asm-alpha/socket.h linux/include/asm-alpha/socket.h --- v2.1.60/linux/include/asm-alpha/socket.h Thu Mar 27 14:40:06 1997 +++ linux/include/asm-alpha/socket.h Sat Oct 25 06:35:38 1997 @@ -1,8 +1,6 @@ #ifndef _ASM_SOCKET_H #define _ASM_SOCKET_H -#include -#include #include /* For setsockoptions(2) */ diff -u --recursive --new-file v2.1.60/linux/include/linux/dcache.h linux/include/linux/dcache.h --- v2.1.60/linux/include/linux/dcache.h Sat Oct 25 02:44:18 1997 +++ linux/include/linux/dcache.h Thu Oct 30 10:50:39 1997 @@ -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 */ @@ -36,6 +37,15 @@ if (sizeof(hash) > sizeof(unsigned int)) hash += hash >> 4*sizeof(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 { diff -u --recursive --new-file v2.1.60/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h --- v2.1.60/linux/include/linux/nfs_fs.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/nfs_fs.h Thu Oct 30 11:11:50 1997 @@ -176,7 +176,7 @@ */ 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 *, diff -u --recursive --new-file v2.1.60/linux/include/linux/posix_types.h linux/include/linux/posix_types.h --- v2.1.60/linux/include/linux/posix_types.h Thu Feb 6 02:37:57 1997 +++ linux/include/linux/posix_types.h Sat Oct 25 06:35:24 1997 @@ -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; diff -u --recursive --new-file v2.1.60/linux/include/linux/smb_fs.h linux/include/linux/smb_fs.h --- v2.1.60/linux/include/linux/smb_fs.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/smb_fs.h Thu Oct 30 11:13:52 1997 @@ -65,20 +65,18 @@ #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 @@ 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 *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 @@ 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 *); diff -u --recursive --new-file v2.1.60/linux/include/linux/smb_fs_sb.h linux/include/linux/smb_fs_sb.h --- v2.1.60/linux/include/linux/smb_fs_sb.h Wed Oct 15 16:04:24 1997 +++ linux/include/linux/smb_fs_sb.h Thu Oct 30 11:03:53 1997 @@ -13,7 +13,6 @@ #include #include -#include /* Get the server for the specified dentry */ #define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb @@ -24,7 +23,7 @@ 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. diff -u --recursive --new-file v2.1.60/linux/mm/mmap.c linux/mm/mmap.c --- v2.1.60/linux/mm/mmap.c Tue Sep 23 16:48:50 1997 +++ linux/mm/mmap.c Thu Oct 30 10:49:33 1997 @@ -92,25 +92,21 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ } while (len > 0); } return addr; + +free_vma: + kmem_cache_free(vm_area_cachep, vma); + return error; } /* Get an address range which is currently unmapped. diff -u --recursive --new-file v2.1.60/linux/mm/swap_state.c linux/mm/swap_state.c --- v2.1.60/linux/mm/swap_state.c Mon Jun 16 16:36:01 1997 +++ linux/mm/swap_state.c Thu Oct 30 10:52:30 1997 @@ -18,9 +18,6 @@ #include #include -#include -#include /* for cli()/sti() */ -#include /* for cop_to/from_user */ #include #include @@ -60,31 +57,47 @@ 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; } diff -u --recursive --new-file v2.1.60/linux/mm/swapfile.c linux/mm/swapfile.c --- v2.1.60/linux/mm/swapfile.c Wed Sep 3 20:52:44 1997 +++ linux/mm/swapfile.c Thu Oct 30 10:52:30 1997 @@ -21,11 +21,7 @@ #include #include /* for blk_size */ #include -#include -#include -#include /* for cli()/sti() */ -#include /* for copy_to/from_user */ #include #include @@ -122,52 +118,60 @@ } } +/* + * 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 @@ 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 @@ 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 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 @@ } /* - * 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 @@ 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++; + } } }