Commit 73f103e4 authored by Linus Torvalds's avatar Linus Torvalds

Import 2.1.61

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