S: USA
N: Gordon Chaffee
-E: chaffee@bmrc.berkeley.edu
+E: chaffee@cs.berkeley.edu
W: http://bmrc.berkeley.edu/people/chaffee/
D: vfat, fat32, joliet, native language support
S: 3674 Oakwood Terrace #201
VFAT FILESYSTEM:
P: Gordon Chaffee
-M: chaffee@plateau.cs.berkeley.edu
+M: chaffee@cs.berkeley.edu
L: linux-kernel@vger.rutgers.edu
-W: http://www-plateau.cs.berkeley.edu/people/chaffee
+W: http://bmrc.berkeley.edu/people/chaffee
S: Maintained
DIGI INTL. EPCA DRIVER:
VERSION = 2
PATCHLEVEL = 1
-SUBLEVEL = 60
+SUBLEVEL = 61
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
# CONFIG_QUOTA is not set
# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
# CONFIG_FAT_FS is not set
# CONFIG_MSDOS_FS is not set
-# CONFIG_VFAT_FS is not set
# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
# CONFIG_ROOT_NFS is not set
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
# CONFIG_HPFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_UFS_FS is not set
# CONFIG_MAC_PARTITION is not set
+#
+# Native Language Support
+#
+# CONFIG_NLS is not set
+
#
# Character devices
#
tristate 'Second extended fs support' CONFIG_EXT2_FS
tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
-if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
- if [ "$CONFIG_ISO9660_FS" = "y" -o "$CONFIG_ISO9660_FS" = "m" ]; then
- bool 'Microsoft Joliet cdrom extensions' CONFIG_JOLIET
- fi
-
- # msdos filesystems
- dep_tristate 'DOS FAT fs support' CONFIG_FAT_FS $CONFIG_NLS
- dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
- dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
- dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+if [ "$CONFIG_ISO9660_FS" != "n" ]; then
+ bool 'Microsoft Joliet cdrom extensions' CONFIG_JOLIET
fi
+# msdos filesystems
+tristate 'DOS FAT fs support' CONFIG_FAT_FS
+dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+
bool '/proc filesystem support' CONFIG_PROC_FS
if [ "$CONFIG_INET" = "y" ]; then
tristate 'NFS filesystem support' CONFIG_NFS_FS
return 0;
}
-static int try_to_fill_dentry(struct dentry * dentry, struct super_block * sb, struct autofs_sb_info *sbi)
+static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi)
{
struct inode * inode;
struct autofs_dir_ent *ent;
dentry->d_inode = inode;
}
- if (S_ISDIR(dentry->d_inode->i_mode)) {
- while (dentry == dentry->d_mounts)
- schedule();
+ /* If this is a directory that isn't a mount point, bitch at the
+ daemon and fix it in user space */
+ if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) {
+ return !autofs_wait(sbi, &dentry->d_name);
}
autofs_update_usage(&sbi->dirhash,ent);
sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
/* Pending dentry */
- if (dentry->d_flags & DCACHE_AUTOFS_PENDING) {
+ if ( dentry->d_flags & DCACHE_AUTOFS_PENDING ) {
if (autofs_oz_mode(sbi))
return 1;
-
- return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ else
+ return try_to_fill_dentry(dentry, dir->i_sb, sbi);
}
/* Negative dentry.. invalidate if "old" */
if (!dentry->d_inode)
return (dentry->d_time - jiffies <= AUTOFS_NEGATIVE_TIMEOUT);
+
+ /* Check for a non-mountpoint directory */
+ if ( S_ISDIR(dentry->d_inode->i_mode) && dentry->d_mounts == dentry ) {
+ if (autofs_oz_mode(sbi))
+ return 1;
+ else
+ return try_to_fill_dentry(dentry, dir->i_sb, sbi);
+ }
/* Update the usage list */
ent = (struct autofs_dir_ent *) dentry->d_time;
}
static struct dentry_operations autofs_dentry_operations = {
- autofs_revalidate,
+ autofs_revalidate, /* d_revalidate */
NULL, /* d_hash */
NULL, /* d_compare */
};
return dentry_hashtable + (hash & D_HASHMASK);
}
-static inline struct dentry * __dlookup(struct list_head *head, struct dentry * parent, struct qstr * name)
+struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
{
- struct list_head *tmp = head->next;
- int len = name->len;
- int hash = name->hash;
+ unsigned int len = name->len;
+ unsigned int hash = name->hash;
const unsigned char *str = name->name;
+ struct list_head *head = d_hash(parent,hash);
+ struct list_head *tmp = head->next;
while (tmp != head) {
struct dentry * dentry = list_entry(tmp, struct dentry, d_hash);
if (memcmp(dentry->d_name.name, str, len))
continue;
}
- return dget(dentry->d_mounts);
+ return dget(dentry);
}
return NULL;
}
-struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
-{
- return __dlookup(d_hash(dir, name->hash), dir, name);
-}
-
/*
* An insecure source has sent us a dentry, here we verify it.
*
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/locks.h>
+#include <linux/config.h>
#include <asm/uaccess.h>
}
}
+#ifdef CONFIG_JOLIET
if (inode->i_sb->u.isofs_sb.s_joliet_level) {
len = get_joliet_filename(de, inode, tmpname);
p = tmpname;
- } else {
+ } else
+#endif
+ /* if not joliet */ {
map = 1;
if (inode->i_sb->u.isofs_sb.s_rock) {
len = get_rock_ridge_filename(de, tmpname, inode);
static int parse_options(char *options, struct iso9660_options * popt)
{
- char *this_char,*value,*p;
- int len;
+ char *this_char,*value;
popt->map = 'n';
popt->rock = 'y';
#ifdef CONFIG_JOLIET
if (!strcmp(this_char,"iocharset")) {
+ char *p;
+ int len;
+
p = value;
while (*value && *value != ',') value++;
len = value - p;
int joliet_level = 0;
struct iso9660_options opt;
int orig_zonesize;
- char * p;
struct iso_primary_descriptor * pri = NULL;
struct iso_directory_record * rootp;
struct iso_supplementary_descriptor *sec = NULL;
MOD_DEC_USE_COUNT;
return NULL;
}
-#ifdef CONFIG_JOLIET
+
s->u.isofs_sb.s_joliet_level = joliet_level;
+
+#ifdef CONFIG_JOLIET
if (joliet_level) {
/* Note: In theory, it is possible to have Rock Ridge
* extensions mixed with Joliet. All character strings
opt.iocharset = NULL;
}
} else if (opt.utf8 == 0) {
+ char * p;
p = opt.iocharset ? opt.iocharset : "iso8859-1";
s->u.isofs_sb.s_nls_iocharset = load_nls(p);
if (! s->u.isofs_sb.s_nls_iocharset) {
int error = dir->i_op->lookup(dir, dentry);
result = ERR_PTR(error);
if (!error)
- result = dget(dentry->d_mounts);
- dput(dentry);
+ result = dentry;
}
}
up(&dir->i_sem);
return dget(result);
}
-/* In difference to the former version, lookup() no longer eats the dir. */
-static inline struct dentry * lookup(struct dentry * dir, struct qstr * name)
-{
- struct dentry * result;
-
- result = reserved_lookup(dir, name);
- if (result)
- goto done;
-
- result = cached_lookup(dir, name);
- if (result)
- goto done;
-
- result = real_lookup(dir, name);
-
-done:
- return result;
-}
-
static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
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.
*
}
}
- dentry = lookup(base, &this);
- if (IS_ERR(dentry))
- break;
+ /* This does the actual lookups.. */
+ dentry = reserved_lookup(base, &this);
+ if (!dentry) {
+ dentry = cached_lookup(base, &this);
+ if (!dentry) {
+ dentry = real_lookup(base, &this);
+ if (IS_ERR(dentry))
+ break;
+ }
+ }
+
+ /* Check mountpoints.. */
+ dentry = follow_mount(dentry);
if (!follow)
break;
{
struct nfs_dirent *cache = dircache;
int i;
- int freed = 0;
for (i = NFS_MAX_DIRCACHE; i--; cache++) {
if (sb && sb->s_dev != cache->dev)
if (cache->entry) {
free_page((unsigned long) cache->entry);
cache->entry = NULL;
- freed++;
}
}
-#ifdef NFS_PARANOIA
-if (freed)
-printk("nfs_invalidate_dircache_sb: freed %d pages from %s\n",
-freed, kdevname(sb->s_dev));
-#endif
}
/*
struct inode *inode;
int error = -EACCES;
+ nfs_invalidate_dircache(dir);
inode = nfs_fhget(dir->i_sb, fhandle, fattr);
if (inode) {
- nfs_invalidate_dircache(dir);
d_instantiate(dentry, inode);
nfs_renew_times(dentry);
error = 0;
{
struct qstr sqstr;
struct dentry *sdentry;
+ unsigned long hash;
int i, error;
sqstr.name = silly;
sqstr.len = slen;
- sqstr.hash = init_name_hash();
+ hash = init_name_hash();
for (i= 0; i < slen; i++)
- sqstr.hash = partial_name_hash(silly[i], sqstr.hash);
- sqstr.hash = end_name_hash(sqstr.hash);
+ hash = partial_name_hash(silly[i], hash);
+ sqstr.hash = end_name_hash(hash);
sdentry = d_lookup(parent, &sqstr);
if (!sdentry) {
sdentry = d_alloc(parent, &sqstr);
return -EIO; /* No need to silly rename. */
}
+#ifdef NFS_PARANOIA
+if (!dentry->d_inode)
+printk("NFS: silly-renaming %s/%s, negative dentry??\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
return -EBUSY; /* don't allow to unlink silly inode -- nope,
* think a bit: silly DENTRY, NOT inode --
error = nfs_proc_remove(NFS_SERVER(dir),
NFS_FH(dir), dentry->d_name.name);
if (error < 0)
- printk("NFS " __FUNCTION__ " failed (err = %d)\n",
- -error);
+ printk("NFS: can't silly-delete %s/%s, error=%d\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name, error);
if (dentry->d_inode) {
if (dentry->d_inode->i_nlink)
dentry->d_inode->i_nlink --;
- } else
+ } else {
+#ifdef NFS_PARANOIA
printk("nfs_silly_delete: negative dentry %s/%s\n",
dentry->d_parent->d_name.name,
dentry->d_name.name);
+#endif
+ }
nfs_invalidate_dircache(dir);
- /*
- * The dentry is unhashed, but we want to make it negative.
- */
- d_delete(dentry);
+ }
+ /*
+ * Check whether to expire the dentry ...
+ */
+ else {
+ unsigned long age = jiffies - dentry->d_time;
+ if (age > 10*HZ)
+ d_drop(dentry);
}
}
return -ENOENT;
}
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ goto out;
error = nfs_sillyrename(dir, dentry);
if (error && error != -EBUSY) {
+#ifdef NFS_PARANOIA
+if (dentry->d_count > 1)
+printk("nfs_unlink: dentry %s/%s, d_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+if (dentry->d_inode && dentry->d_inode->i_count > 1)
+printk("nfs_unlink: dentry %s/%s, inode i_count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_inode->i_count);
+#endif
+ /* N.B. should check for d_count > 1 and fail */
error = nfs_proc_remove(NFS_SERVER(dir),
NFS_FH(dir), dentry->d_name.name);
if (!error) {
d_delete(dentry);
}
}
-
+out:
return error;
}
-static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+static int
+nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct nfs_sattr sattr;
int error;
return -ENOENT;
}
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ goto out;
if (strlen(symname) > NFS_MAXPATHLEN)
- return -ENAMETOOLONG;
+ goto out;
sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
*/
d_drop(dentry);
}
+out:
return error;
}
-static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
+static int
+nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
{
int error;
return -ENOENT;
}
+ error = -ENAMETOOLONG;
if (dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
+ goto out;
+
+ /*
+ * The NFS server may want to use a new fileid for the link,
+ * so we can't reuse the existing inode for the new dentry.
+ * To force a new lookup after the link operation, we can just
+ * drop the new dentry, as long as it's not busy. (See above.)
+ */
+ error = -EBUSY;
+ if (dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+printk("nfs_link: dentry %s/%s busy, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
+#endif
+ goto out;
+ }
+ d_drop(dentry);
error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode), NFS_FH(dir),
dentry->d_name.name);
if (!error) {
nfs_invalidate_dircache(dir);
+#if 0
inode->i_count ++;
inode->i_nlink ++; /* no need to wait for nfs_refresh_inode() */
d_instantiate(dentry, inode);
- error = 0;
+#endif
}
+out:
return error;
}
* implementation that only depends on the dcache stuff instead of
* using the inode layer
*
+ * Unfortunately, things are a little more complicated than indicated
+ * above. The NFS server may decide to use a new fileid for the renamed
+ * file, so we can't link the new name to the old inode. Otherwise, the
+ * server might reuse the fileid after the old file has been removed,
+ * which would leave the new dentry holding an invalid fileid (possibly
+ * leading to file corruption). To handle this consider these cases:
+ * (1) within-directory:
+ * -- no problem, just use nfs_proc_rename
+ * (2) cross-directory, only one user for old and new dentry:
+ * -- drop both dentries to force new lookups, then use rename
+ * (3) cross-directory, multiple users for old, one user for new:
+ * -- drop new dentry, silly-rename old dentry and make a link
+ * (4) cross-directory, multiple users for new dentry:
+ * -- sorry, we're busy.
*/
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
- int error;
-
- dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n",
- old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name,
- new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name);
+ int update = 1, error;
+#ifdef NFS_DEBUG_VERBOSE
+printk("nfs_rename: old %s/%s, count=%d, new %s/%s, count=%d\n",
+old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count,
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,new_dentry->d_count);
+#endif
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
printk("nfs_rename: old inode is NULL or not a directory\n");
return -ENOENT;
return -ENOENT;
}
- if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN)
- return -ENAMETOOLONG;
-
- if (new_dir != old_dir) {
- error = nfs_sillyrename(old_dir, old_dentry);
-
- if (error == -EBUSY) {
- return -EBUSY;
- } else if (error == 0) { /* did silly rename stuff */
- error = nfs_link(old_dentry->d_inode,
- new_dir, new_dentry);
-
- return error;
- }
- /* no need for silly rename, proceed as usual */
- }
+ error = -ENAMETOOLONG;
+ if (old_dentry->d_name.len > NFS_MAXNAMLEN ||
+ new_dentry->d_name.len > NFS_MAXNAMLEN)
+ goto out;
+ /*
+ * Examine the cases as noted above.
+ */
+ if (new_dir == old_dir)
+ goto simple_case;
+ error = -EBUSY;
+ if (new_dentry->d_count > 1) {
+#ifdef NFS_PARANOIA
+printk("nfs_rename: new dentry %s/%s busy, count=%d\n",
+new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
+new_dentry->d_count);
+#endif
+ goto out;
+ }
+ d_drop(new_dentry);
+ if (old_dentry->d_count > 1)
+ goto complex_case;
+ d_drop(old_dentry);
+ update = 0;
+
+ /* no need for silly rename, proceed as usual */
+simple_case:
error = nfs_proc_rename(NFS_SERVER(old_dir),
NFS_FH(old_dir), old_dentry->d_name.name,
NFS_FH(new_dir), new_dentry->d_name.name);
- if (!error) {
- nfs_invalidate_dircache(old_dir);
- nfs_invalidate_dircache(new_dir);
- /*
- * We know these paths are still valid ...
- */
- nfs_renew_times(old_dentry);
- nfs_renew_times(new_dentry->d_parent);
+ if (error)
+ goto out;
+ nfs_invalidate_dircache(new_dir);
+ nfs_invalidate_dircache(old_dir);
- /* Update the dcache */
+ /* Update the dcache if needed */
+ if (update)
d_move(old_dentry, new_dentry);
- }
+ goto out;
+
+ /*
+ * We don't need to update the dcache in this case ... the
+ * new dentry has been dropped, and the old one silly-renamed.
+ */
+complex_case:
+ error = nfs_sillyrename(old_dir, old_dentry);
+ if (error)
+ goto out;
+ nfs_invalidate_dircache(old_dir);
+
+ error = nfs_link(old_dentry->d_inode, new_dir, new_dentry);
+ if (error)
+ goto out;
+ nfs_invalidate_dircache(new_dir);
+#ifdef NFS_PARANOIA
+printk("nfs_rename: dentry %s/%s linked to %s/%s, old count=%d\n",
+new_dentry->d_parent->d_name.name,new_dentry->d_name.name,
+old_dentry->d_parent->d_name.name,old_dentry->d_name.name,old_dentry->d_count);
+#endif
+
+out:
return error;
}
# define IS_SWAPFILE(inode) (0)
#endif
-
+/*
+ * Flush all dirty pages, and check for write errors.
+ *
+ * Note that since the file close operation is called only by the
+ * _last_ process to close the file, we need to flush _all_ dirty
+ * pages. This also means that there is little sense in checking
+ * for errors for this specific process -- we should probably just
+ * clear all errors.
+ */
static int
nfs_file_close(struct inode *inode, struct file *file)
{
- int status;
+ int status, error;
dfprintk(VFS, "nfs: close(%x/%ld)\n", inode->i_dev, inode->i_ino);
- if ((status = nfs_flush_dirty_pages(inode, 0, 0)) < 0)
- return status;
- return nfs_write_error(inode);
+ status = nfs_flush_dirty_pages(inode, 0, 0, 0);
+ error = nfs_write_error(inode);
+ if (!status)
+ status = error;
+ return status;
}
static ssize_t
nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
- int status;
+ ssize_t result;
dfprintk(VFS, "nfs: read(%x/%ld, %lu@%lu)\n",
inode->i_dev, inode->i_ino, count,
(unsigned long) *ppos);
- if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
- return status;
- return generic_file_read(file, buf, count, ppos);
+ result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (!result)
+ result = generic_file_read(file, buf, count, ppos);
+ return result;
}
static int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{
- int status;
struct inode *inode = file->f_dentry->d_inode;
+ int status;
dfprintk(VFS, "nfs: mmap(%x/%ld)\n", inode->i_dev, inode->i_ino);
- if ((status = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
- return status;
- return generic_file_mmap(file, vma);
+ status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (!status)
+ status = generic_file_mmap(file, vma);
+ return status;
}
-static int nfs_fsync(struct file *file, struct dentry *dentry)
+/*
+ * Flush any dirty pages for this process, and check for write errors.
+ * The return status from this call provides a reliable indication of
+ * whether any write errors occurred for this process.
+ */
+static int
+nfs_fsync(struct file *file, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
+ int status, error;
+
dfprintk(VFS, "nfs: fsync(%x/%ld)\n", inode->i_dev, inode->i_ino);
- return nfs_flush_dirty_pages(inode, 0, 0);
+ status = nfs_flush_dirty_pages(inode, current->pid, 0, 0);
+ error = nfs_write_error(inode);
+ if (!status)
+ status = error;
+ return status;
}
/*
nfs_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
struct inode * inode = file->f_dentry->d_inode;
- int result;
+ ssize_t result;
dfprintk(VFS, "nfs: write(%x/%ld (%d), %lu@%lu)\n",
inode->i_dev, inode->i_ino, inode->i_count,
printk("NFS: attempt to write to active swap file!\n");
return -EBUSY;
}
- if ((result = nfs_revalidate_inode(NFS_SERVER(inode), inode)) < 0)
- return result;
+ result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+ if (result)
+ goto out;
+
+ /* N.B. This should be impossible now -- inodes can't change mode */
if (!S_ISREG(inode->i_mode)) {
printk("nfs_file_write: write to non-file, mode %07o\n",
inode->i_mode);
return -EINVAL;
}
- if (count <= 0)
- return 0;
-
- /* Return error from previous async call */
- if ((result = nfs_write_error(inode)) < 0)
- return result;
-
- return generic_file_write(file, buf, count, ppos);
+ result = count;
+ if (!count)
+ goto out;
+
+ /* Check for an error from a previous async call */
+ result = nfs_write_error(inode);
+ if (!result)
+ result = generic_file_write(file, buf, count, ppos);
+out:
+ return result;
}
/*
int
nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{
+ struct inode * inode = filp->f_dentry->d_inode;
int status;
- struct inode * inode;
dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%ld:%ld)\n",
- filp->f_dentry->d_inode->i_dev, filp->f_dentry->d_inode->i_ino,
+ inode->i_dev, inode->i_ino,
fl->fl_type, fl->fl_flags,
fl->fl_start, fl->fl_end);
- if (!(inode = filp->f_dentry->d_inode))
+ if (!inode)
return -EINVAL;
/* No mandatory locks over NFS */
* been killed by a signal, that is). */
if (cmd == F_SETLK && fl->fl_type == F_UNLCK
&& !signal_pending(current)) {
- status = nfs_flush_dirty_pages(inode,
+ status = nfs_flush_dirty_pages(inode, current->pid,
fl->fl_start, fl->fl_end == NLM_OFFSET_MAX? 0 :
fl->fl_end - fl->fl_start + 1);
if (status < 0)
static void
nfs_delete_inode(struct inode * inode)
{
+ int failed;
+
dprintk("NFS: delete_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
/*
* Flush out any pending write requests ...
*/
if (NFS_WRITEBACK(inode) != NULL) {
unsigned long timeout = jiffies + 5*HZ;
- printk("NFS: invalidating pending RPC requests\n");
+ printk("NFS: inode %ld, invalidating pending RPC requests\n",
+ inode->i_ino);
nfs_invalidate_pages(inode);
while (NFS_WRITEBACK(inode) != NULL && jiffies < timeout) {
- current->state = TASK_UNINTERRUPTIBLE;
+ current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + HZ/10;
schedule();
}
printk("NFS: Arghhh, stuck RPC requests!\n");
}
- if (check_failed_request(inode))
- printk("NFS: inode had failed requests\n");
+ failed = check_failed_request(inode);
+ if (failed)
+ printk("NFS: inode %ld had %d failed requests\n",
+ inode->i_ino, failed);
clear_inode(inode);
}
* Find a failed write request by pid
*/
static struct nfs_wreq *
-find_failed_request(struct inode *inode, pid_t pid, int all)
+find_failed_request(struct inode *inode, pid_t pid)
{
struct nfs_wreq *head, *req;
- if (!(req = head = nfs_failed_requests))
- return NULL;
- do {
- if (req->wb_inode == inode && (all || req->wb_pid == pid))
+ req = head = nfs_failed_requests;
+ while (req != NULL) {
+ if (req->wb_inode == inode && (pid == 0 || req->wb_pid == pid))
return req;
- } while ((req = WB_NEXT(req)) != head);
+ if ((req = WB_NEXT(req)) == head)
+ break;
+ }
return NULL;
}
struct nfs_wreq * req;
int found = 0;
- while ((req = find_failed_request(inode, 0, 1)) != NULL) {
+ while ((req = find_failed_request(inode, 0)) != NULL) {
remove_failed_request(req);
found++;
}
}
/* Create the write request. */
- if (!(req = create_write_request(inode, page, offset, count))) {
- status = -ENOBUFS;
+ status = -ENOBUFS;
+ req = create_write_request(inode, page, offset, count);
+ if (!req)
goto done;
- }
/* Copy data to page buffer. */
+ /* N.B. should check for fault here ... */
copy_from_user(page_addr + offset, buffer, count);
/* Schedule request */
transfer_page_lock(req);
/* rpc_execute(&req->wb_task); */
if (sync) {
+ /* N.B. if signalled, result not ready? */
wait_on_write_request(req);
if ((count = nfs_write_error(inode)) < 0)
status = count;
if (rqoffset < end && offset < rqend
&& (pid == 0 || req->wb_pid == pid)) {
- if (!WB_HAVELOCK(req))
+ if (!WB_HAVELOCK(req)) {
+#ifdef NFS_PARANOIA
+printk("nfs_flush: flushing inode=%ld, %d @ %lu\n",
+req->wb_inode->i_ino, req->wb_bytes, rqoffset);
+#endif
nfs_flush_request(req);
+ }
last = req;
}
+ } else {
+#ifdef NFS_PARANOIA
+printk("nfs_flush_pages: in progress inode=%ld, %d @ %lu\n",
+req->wb_inode->i_ino, req->wb_bytes, rqoffset);
+#endif
}
if (invalidate)
req->wb_flags |= NFS_WRITE_INVALIDATE;
/*
* Cancel all writeback requests, both pending and in progress.
+ *
+ * N.B. This doesn't seem to wake up the tasks -- are we sure
+ * they will eventually complete? Also, this could overwrite a
+ * failed status code from an already-completed task.
*/
static void
nfs_cancel_dirty(struct inode *inode, pid_t pid)
req = head = NFS_WRITEBACK(inode);
while (req != NULL) {
- if (req->wb_pid == pid) {
+ /* N.B. check for task already finished? */
+ if (pid == 0 || req->wb_pid == pid) {
req->wb_flags |= NFS_WRITE_CANCELLED;
rpc_exit(&req->wb_task, 0);
}
* this isn't used by the nlm module yet.
*/
int
-nfs_flush_dirty_pages(struct inode *inode, off_t offset, off_t len)
+nfs_flush_dirty_pages(struct inode *inode, pid_t pid, off_t offset, off_t len)
{
struct nfs_wreq *last = NULL;
- int result = 0;
+ int result = 0, cancel = 0;
dprintk("NFS: flush_dirty_pages(%x/%ld for pid %d %ld/%ld)\n",
inode->i_dev, inode->i_ino, current->pid,
offset, len);
+ if (IS_SOFT && signalled()) {
+ nfs_cancel_dirty(inode, pid);
+ cancel = 1;
+ }
+
for (;;) {
if (IS_SOFT && signalled()) {
- nfs_cancel_dirty(inode, current->pid);
+ if (!cancel)
+ nfs_cancel_dirty(inode, pid);
result = -ERESTARTSYS;
break;
}
- /* Flush all pending writes for this pid and file region */
- last = nfs_flush_pages(inode, current->pid, offset, len, 0);
+ /* Flush all pending writes for the pid and file region */
+ last = nfs_flush_pages(inode, pid, offset, len, 0);
if (last == NULL)
break;
wait_on_write_request(last);
* Flush out any pending write requests and flag that they be discarded
* after the write is complete.
*
- * This function is called from nfs_revalidate_inode just before it calls
+ * This function is called from nfs_refresh_inode just before it calls
* invalidate_inode_pages. After nfs_flush_pages returns, we can be sure
* that all dirty pages are locked, so that invalidate_inode_pages does
* not throw away any dirty pages.
dprintk("nfs: checking for write error inode %04x/%ld\n",
inode->i_dev, inode->i_ino);
- req = find_failed_request(inode, current->pid, 0);
+ req = find_failed_request(inode, current->pid);
if (req) {
dprintk("nfs: write error %d inode %04x/%ld\n",
req->wb_task.tk_status, inode->i_dev, inode->i_ino);
* application by adding the request to the failed
* requests list.
*/
- if (find_failed_request(inode, req->wb_pid, 0))
+ if (find_failed_request(inode, req->wb_pid))
status = 0;
clear_bit(PG_uptodate, &page->flags);
} else if (!WB_CANCELLED(req)) {
mainmenu_option next_comment
comment 'Native Language Support'
-tristate 'Native language support (Unicode, codepages)' CONFIG_NLS
+# msdos and Joliet want NLS
+if [ "$CONFIG_JOLIET" = "y" -o "$CONFIG_FAT_FS" != "n" ]; then
+ define_bool CONFIG_NLS y
+else
+ define_bool CONFIG_NLS n
+fi
-if [ "$CONFIG_NLS" = "y" -o "$CONFIG_NLS" = "m" ]; then
- dep_tristate 'Codepage 437' CONFIG_NLS_CODEPAGE_437 $CONFIG_NLS
- dep_tristate 'Codepage 737' CONFIG_NLS_CODEPAGE_737 $CONFIG_NLS
- dep_tristate 'Codepage 775' CONFIG_NLS_CODEPAGE_775 $CONFIG_NLS
- dep_tristate 'Codepage 850' CONFIG_NLS_CODEPAGE_850 $CONFIG_NLS
- dep_tristate 'Codepage 852' CONFIG_NLS_CODEPAGE_852 $CONFIG_NLS
- dep_tristate 'Codepage 855' CONFIG_NLS_CODEPAGE_855 $CONFIG_NLS
- dep_tristate 'Codepage 857' CONFIG_NLS_CODEPAGE_857 $CONFIG_NLS
- dep_tristate 'Codepage 860' CONFIG_NLS_CODEPAGE_860 $CONFIG_NLS
- dep_tristate 'Codepage 861' CONFIG_NLS_CODEPAGE_861 $CONFIG_NLS
- dep_tristate 'Codepage 862' CONFIG_NLS_CODEPAGE_862 $CONFIG_NLS
- dep_tristate 'Codepage 863' CONFIG_NLS_CODEPAGE_863 $CONFIG_NLS
- dep_tristate 'Codepage 864' CONFIG_NLS_CODEPAGE_864 $CONFIG_NLS
- dep_tristate 'Codepage 865' CONFIG_NLS_CODEPAGE_865 $CONFIG_NLS
- dep_tristate 'Codepage 866' CONFIG_NLS_CODEPAGE_866 $CONFIG_NLS
- dep_tristate 'Codepage 869' CONFIG_NLS_CODEPAGE_869 $CONFIG_NLS
- dep_tristate 'Codepage 874' CONFIG_NLS_CODEPAGE_874 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-1' CONFIG_NLS_ISO8859_1 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-2' CONFIG_NLS_ISO8859_2 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-3' CONFIG_NLS_ISO8859_3 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-4' CONFIG_NLS_ISO8859_4 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-5' CONFIG_NLS_ISO8859_5 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-6' CONFIG_NLS_ISO8859_6 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-7' CONFIG_NLS_ISO8859_7 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-8' CONFIG_NLS_ISO8859_8 $CONFIG_NLS
- dep_tristate 'NLS ISO 8859-9' CONFIG_NLS_ISO8859_9 $CONFIG_NLS
- dep_tristate 'NLS KOI8-R' CONFIG_NLS_KOI8_R $CONFIG_NLS
+if [ "$CONFIG_NLS" = "y" ]; then
+ tristate 'Codepage 437' CONFIG_NLS_CODEPAGE_437
+ tristate 'Codepage 737' CONFIG_NLS_CODEPAGE_737
+ tristate 'Codepage 775' CONFIG_NLS_CODEPAGE_775
+ tristate 'Codepage 850' CONFIG_NLS_CODEPAGE_850
+ tristate 'Codepage 852' CONFIG_NLS_CODEPAGE_852
+ tristate 'Codepage 855' CONFIG_NLS_CODEPAGE_855
+ tristate 'Codepage 857' CONFIG_NLS_CODEPAGE_857
+ tristate 'Codepage 860' CONFIG_NLS_CODEPAGE_860
+ tristate 'Codepage 861' CONFIG_NLS_CODEPAGE_861
+ tristate 'Codepage 862' CONFIG_NLS_CODEPAGE_862
+ tristate 'Codepage 863' CONFIG_NLS_CODEPAGE_863
+ tristate 'Codepage 864' CONFIG_NLS_CODEPAGE_864
+ tristate 'Codepage 865' CONFIG_NLS_CODEPAGE_865
+ tristate 'Codepage 866' CONFIG_NLS_CODEPAGE_866
+ tristate 'Codepage 869' CONFIG_NLS_CODEPAGE_869
+ tristate 'Codepage 874' CONFIG_NLS_CODEPAGE_874
+ tristate 'NLS ISO 8859-1' CONFIG_NLS_ISO8859_1
+ tristate 'NLS ISO 8859-2' CONFIG_NLS_ISO8859_2
+ tristate 'NLS ISO 8859-3' CONFIG_NLS_ISO8859_3
+ tristate 'NLS ISO 8859-4' CONFIG_NLS_ISO8859_4
+ tristate 'NLS ISO 8859-5' CONFIG_NLS_ISO8859_5
+ tristate 'NLS ISO 8859-6' CONFIG_NLS_ISO8859_6
+ tristate 'NLS ISO 8859-7' CONFIG_NLS_ISO8859_7
+ tristate 'NLS ISO 8859-8' CONFIG_NLS_ISO8859_8
+ tristate 'NLS ISO 8859-9' CONFIG_NLS_ISO8859_9
+ tristate 'NLS KOI8-R' CONFIG_NLS_KOI8_R
fi
endmenu
MOD_LIST_NAME := NLS_MODULES
-ifeq ($(CONFIG_NLS),y)
-NLS += nls_base.o
-O_TARGET = nls.o
-OX_OBJS = $(NLS)
-else
- ifeq ($(CONFIG_NLS),m)
- MX_OBJS += nls_base.o
- endif
-endif
+NLS = nls_base.o
ifeq ($(CONFIG_NLS_CODEPAGE_437),y)
NLS += nls_cp437.o
endif
endif
+O_TARGET = nls.o
+OX_OBJS = $(NLS)
+
include $(TOPDIR)/Rules.make
*
*/
-#define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
#include <linux/version.h>
#include <linux/module.h>
#include <linux/string.h>
#ifdef CONFIG_NLS_CODEPAGE_874
init_nls_cp874();
#endif
-#if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
- return 0;
-#else
- return register_symtab(&nls_syms);
+#ifdef CONFIG_NLS_KOI8_R
+ init_nls_koi8_r();
#endif
+ return 0;
}
#ifdef MODULE
* entries are coming in order and are added to the end.
*/
void
-smb_add_to_cache(struct cache_head * cachep, struct dirent *entry, off_t fpos)
+smb_add_to_cache(struct cache_head * cachep, struct cache_dirent *entry,
+ off_t fpos)
{
struct inode * inode = get_cache_inode(cachep);
struct cache_index * index;
struct cache_block * block;
unsigned long page_off;
- unsigned int nent, offset, len = entry->d_reclen;
+ unsigned int nent, offset, len = entry->len;
unsigned int needed = len + sizeof(struct cache_entry);
#ifdef SMBFS_DEBUG_VERBOSE
offset = index->space +
index->num_entries * sizeof(struct cache_entry);
block = index->block;
- memcpy(&block->cb_data.names[offset], entry->d_name, len);
+ memcpy(&block->cb_data.names[offset], entry->name, len);
block->cb_data.table[nent].namelen = len;
block->cb_data.table[nent].offset = offset;
- block->cb_data.table[nent].ino = entry->d_ino;
+ block->cb_data.table[nent].ino = entry->ino;
cachep->entries++;
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_add_to_cache: added entry %s, len=%d, pos=%ld, entries=%d\n",
#include <linux/sched.h>
#include <linux/errno.h>
-#include <linux/stat.h>
#include <linux/kernel.h>
-#include <linux/malloc.h>
-#include <linux/mm.h>
#include <linux/smb_fs.h>
#include <linux/smbno.h>
-#include <linux/errno.h>
-
-#include <asm/uaccess.h>
-#include <asm/semaphore.h>
#define SMBFS_PARANOIA 1
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */
+#define SMBFS_MAX_AGE 5*HZ
static ssize_t smb_dir_read(struct file *, char *, size_t, loff_t *);
static int smb_readdir(struct file *, void *, filldir_t);
* If a dentry already exists, we have to give the cache entry
* the correct inode number. This is needed for getcwd().
*/
-static unsigned long
+static void
smb_find_ino(struct dentry *dentry, struct cache_dirent *entry)
{
struct dentry * new_dentry;
struct qstr qname;
- unsigned long ino = 0;
+ /* N.B. Make cache_dirent name a qstr! */
qname.name = entry->name;
qname.len = entry->len;
qname.hash = hash_it(qname.name, qname.len);
{
struct inode * inode = new_dentry->d_inode;
if (inode)
- ino = inode->i_ino;
+ entry->ino = inode->i_ino;
dput(new_dentry);
}
- if (!ino)
- ino = smb_invent_inos(1);
- return ino;
+ if (!entry->ino)
+ entry->ino = smb_invent_inos(1);
}
static int
struct cache_head *cachep;
int result;
- pr_debug("smb_readdir: filp->f_pos = %d\n", (int) filp->f_pos);
- pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
- dir->i_ino, c_ino);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_readdir: reading %s/%s, f_pos=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, (int) filp->f_pos);
+#endif
/*
* Make sure our inode is up-to-date.
*/
/*
* Get the cache pointer ...
*/
+ result = -EIO;
cachep = smb_get_dircache(dentry);
if (!cachep)
goto out;
{
result = smb_refill_dircache(cachep, dentry);
if (result)
- goto up_and_out;
+ goto out_free;
}
+ result = 0;
switch ((unsigned int) filp->f_pos)
{
case 0:
if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
- goto up_and_out;
+ goto out_free;
filp->f_pos = 1;
case 1:
if (filldir(dirent, "..", 2, 1,
dentry->d_parent->d_inode->i_ino) < 0)
- goto up_and_out;
+ goto out_free;
filp->f_pos = 2;
}
* Check whether to look up the inode number.
*/
if (!entry->ino)
- {
- entry->ino = smb_find_ino(dentry, entry);
- }
+ smb_find_ino(dentry, entry);
if (filldir(dirent, entry->name, entry->len,
filp->f_pos, entry->ino) < 0)
break;
filp->f_pos += 1;
}
- result = 0;
/*
* Release the dircache.
*/
-up_and_out:
+out_free:
smb_free_dircache(cachep);
out:
return result;
/*
* This is the callback when the dcache has a lookup hit.
*/
-static int smb_lookup_validate(struct dentry * dentry)
+static int
+smb_lookup_validate(struct dentry * dentry)
{
struct inode * inode = dentry->d_inode;
unsigned long age = jiffies - dentry->d_time;
* we believe in dentries for 5 seconds. (But each
* successful server lookup renews the timestamp.)
*/
- valid = age < 5 * HZ || IS_ROOT(dentry);
+ valid = (age <= SMBFS_MAX_AGE) || IS_ROOT(dentry);
#ifdef SMBFS_DEBUG_VERBOSE
if (!valid)
-printk("smb_lookup_validate: %s/%s not valid, age=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, age)
+printk("smb_lookup_validate: %s/%s not valid, age=%lu\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, age);
#endif
if (inode)
/*
* This is the callback from dput() when d_count is going to 0.
- * We use this to close files and unhash dentries with bad inodes.
+ * We use this to unhash dentries with bad inodes and close files.
*/
-static void smb_delete_dentry(struct dentry * dentry)
+static void
+smb_delete_dentry(struct dentry * dentry)
{
+ if ((jiffies - dentry->d_time) > SMBFS_MAX_AGE)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_delete_dentry: %s/%s expired, d_time=%lu, now=%lu\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_time, jiffies);
+#endif
+ d_drop(dentry);
+ }
+
if (dentry->d_inode)
{
if (is_bad_inode(dentry->d_inode))
* are all valid, so we want to update the dentry timestamps.
* N.B. Move this to dcache?
*/
-void smb_renew_times(struct dentry * dentry)
+void
+smb_renew_times(struct dentry * dentry)
{
- for (;;) {
+ for (;;)
+ {
dentry->d_time = jiffies;
if (dentry == dentry->d_parent)
break;
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;
}
{
int error;
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_create: creating %s/%s, mode=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, mode);
+#endif
error = -ENAMETOOLONG;
if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;
*
*/
-#define SMBFS_DCACHE_EXT 1
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/smb_fs.h>
-#include <linux/smbno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/dcache.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define SMBFS_PARANOIA 1
/* #define SMBFS_DEBUG_VERBOSE 1 */
-#ifndef SMBFS_DCACHE_EXT
-#define shrink_dcache_sb(sb) shrink_dcache()
-#endif
-extern void smb_renew_times(struct dentry *);
-extern int close_fp(struct file *filp);
-
+static void smb_read_inode(struct inode *);
static void smb_put_inode(struct inode *);
static void smb_delete_inode(struct inode *);
-static void smb_read_inode(struct inode *);
static void smb_put_super(struct super_block *);
-static int smb_statfs(struct super_block *, struct statfs *, int);
+static int smb_statfs(struct super_block *, struct statfs *, int);
static struct super_operations smb_sops =
{
invalidate_inodes(SB_of(server));
}
+/*
+ * This is called when we want to check whether the inode
+ * has changed on the server. If it has changed, we must
+ * invalidate our local caches.
+ */
int
smb_revalidate_inode(struct inode *inode)
{
}
/*
- * Save the last modified time, then refresh the inode
+ * Save the last modified time, then refresh the inode.
+ * (Note: a size change should have a different mtime.)
*/
last_time = inode->i_mtime;
error = smb_refresh_inode(inode);
- if (!error)
+ if (error || inode->i_mtime != last_time)
{
- if (inode->i_mtime != last_time)
- {
#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
((struct dentry *)inode->u.smbfs_i.dentry)->d_parent->d_name.name,
((struct dentry *)inode->u.smbfs_i.dentry)->d_name.name,
(long) last_time, (long) inode->i_mtime);
#endif
- if (!S_ISDIR(inode->i_mode))
- invalidate_inode_pages(inode);
- else
- smb_invalid_dir_cache(inode);
- }
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
}
out:
return error;
/*
* No need to worry about unhashing the dentry: the
* lookup validation will see that the inode is bad.
- * But we may need to invalidate the caches ...
+ * But we do want to invalidate the caches ...
*/
- invalidate_inode_pages(inode);
- smb_invalid_dir_cache(inode);
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
error = -EIO;
}
}
if (server->conn_pid)
kill_proc(server->conn_pid, SIGTERM, 0);
+ kfree(server->mnt);
if (server->packet)
smb_vfree(server->packet);
sb->s_dev = 0;
struct super_block *
smb_read_super(struct super_block *sb, void *raw_data, int silent)
{
- struct smb_mount_data *data = (struct smb_mount_data *)raw_data;
+ struct smb_mount_data *mnt, *data = (struct smb_mount_data *) raw_data;
struct smb_fattr root;
kdev_t dev = sb->s_dev;
struct inode *root_inode;
sb->u.smbfs_sb.conn_pid = 0;
sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
sb->u.smbfs_sb.generation = 0;
- sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
- sb->u.smbfs_sb.packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
+ sb->u.smbfs_sb.packet_size = smb_round_length(SMB_INITIAL_PACKET_SIZE);
+ sb->u.smbfs_sb.packet = smb_vmalloc(sb->u.smbfs_sb.packet_size);
if (!sb->u.smbfs_sb.packet)
goto out_no_mem;
- sb->u.smbfs_sb.m = *data;
- sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode &
- (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFREG;
- sb->u.smbfs_sb.m.dir_mode = (sb->u.smbfs_sb.m.dir_mode &
- (S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;
+ mnt = kmalloc(sizeof(struct smb_mount_data), GFP_KERNEL);
+ if (!mnt)
+ goto out_no_mount;
+ *mnt = *data;
+ mnt->version = 0; /* dynamic flags */
+#ifdef CONFIG_SMB_WIN95
+ mnt->version |= 1;
+#endif
+ mnt->file_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->file_mode |= S_IFREG;
+ mnt->dir_mode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+ mnt->dir_mode |= S_IFDIR;
+ sb->u.smbfs_sb.mnt = mnt;
+
/*
* Keep the super block locked while we get the root inode.
*/
out_no_root:
printk(KERN_ERR "smb_read_super: get root inode failed\n");
iput(root_inode);
+ kfree(sb->u.smbfs_sb.mnt);
+out_no_mount:
smb_vfree(sb->u.smbfs_sb.packet);
goto out_unlock;
out_no_mem:
printk("smb_read_super: could not alloc packet\n");
- goto out_unlock;
+out_unlock:
+ unlock_super(sb);
+ goto out_fail;
out_wrong_data:
- printk(KERN_ERR "smb_read_super: wrong data argument."
- " Recompile smbmount.\n");
+ printk("smb_read_super: need mount version %d\n", SMB_MOUNT_VERSION);
goto out_fail;
out_no_data:
printk("smb_read_super: missing data argument\n");
- goto out_fail;
-out_unlock:
- unlock_super(sb);
out_fail:
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
{
struct smb_sb_info *server = SMB_SERVER(inode);
struct dentry *dentry = inode->u.smbfs_i.dentry;
+ unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
int error, refresh = 0;
error = -EIO;
goto out;
error = -EPERM;
- if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid)))
+ if ((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->mnt->uid))
goto out;
- if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid)))
+ if ((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->mnt->gid))
goto out;
- if (((attr->ia_valid & ATTR_MODE) &&
- (attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
+ if ((attr->ia_valid & ATTR_MODE) && (attr->ia_mode & ~mask))
goto out;
if ((attr->ia_valid & ATTR_SIZE) != 0)
#include <linux/errno.h>
#include <linux/fs.h>
-#include <linux/smb_fs.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
#include <linux/mm.h>
+#include <linux/smb_fs.h>
+#include <linux/smb_mount.h>
#include <asm/uaccess.h>
smb_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
+ int result = -EINVAL;
+
switch (cmd)
{
case SMB_IOC_GETMOUNTUID:
- return put_user(SMB_SERVER(inode)->m.mounted_uid,
+ result = put_user(SMB_SERVER(inode)->mnt->mounted_uid,
(uid_t *) arg);
+ break;
case SMB_IOC_NEWCONN:
{
struct smb_conn_opt opt;
- int result;
if (arg == 0)
{
/* The process offers a new connection upon SIGUSR1 */
- return smb_offerconn(SMB_SERVER(inode));
+ result = smb_offerconn(SMB_SERVER(inode));
}
-
- if ((result = verify_area(VERIFY_READ, (uid_t *) arg,
- sizeof(opt))) != 0)
+ else
{
- return result;
+ result = -EFAULT;
+ if (!copy_from_user(&opt, (void *)arg, sizeof(opt)))
+ result = smb_newconn(SMB_SERVER(inode), &opt);
}
- copy_from_user(&opt, (void *)arg, sizeof(opt));
-
- return smb_newconn(SMB_SERVER(inode), &opt);
+ break;
}
default:
- return -EINVAL;
}
+ return result;
}
* by Riccardo Facchetti
*/
-#include <linux/config.h>
#include <linux/fs.h>
-#include <linux/smbno.h>
-#include <linux/smb_fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/fcntl.h>
#include <linux/dcache.h>
#include <linux/dirent.h>
+#include <linux/smb_fs.h>
+#include <linux/smbno.h>
+#include <linux/smb_mount.h>
-#include <asm/uaccess.h>
#include <asm/string.h>
+#define SMBFS_PARANOIA 1
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+/* #define pr_debug printk */
+
#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN)
#define SMB_CMD(packet) (*(packet+8))
#define SMB_WCT(packet) (*(packet+SMB_HEADER_LEN - 1))
#define SMB_DIRINFO_SIZE 43
#define SMB_STATUS_SIZE 21
-#define SMBFS_PARANOIA 1
-/* #define SMBFS_DEBUG_VERBOSE 1 */
-/* #define pr_debug printk */
-
-extern void smb_renew_times(struct dentry *);
-
static inline int
min(int a, int b)
{
}
static void
-str_upper(char *name)
+str_upper(char *name, int len)
{
- while (*name)
+ while (len--)
{
if (*name >= 'a' && *name <= 'z')
*name -= ('a' - 'A');
}
static void
-str_lower(char *name)
+str_lower(char *name, int len)
{
- while (*name)
+ while (len--)
{
if (*name >= 'A' && *name <= 'Z')
*name += ('a' - 'A');
buf += smb_build_path(dir, name, buf);
if (server->opt.protocol <= SMB_PROTOCOL_COREPLUS)
- str_upper(start);
+ str_upper(start, buf - start);
return buf;
}
int error;
error = -EACCES;
- if (!suser() && (current->uid != server->m.mounted_uid))
+ if ((current->uid != server->mnt->mounted_uid) && !suser())
goto out;
if (atomic_read(&server->sem.count) == 1)
{
goto out;
error = -EACCES;
- if (!suser() && (current->uid != server->m.mounted_uid))
+ if ((current->uid != server->mnt->mounted_uid) && !suser())
goto out;
if (atomic_read(&server->sem.count) == 1)
{
}
smb_unlock_server(server);
}
- }
- /* Consider dropping negative dentries? */
-#if 0
- else
- d_drop(dentry);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_close_dentry: closed %s/%s, count=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
#endif
+ }
}
/* In smb_proc_read and smb_proc_write we do not retry, because the
smb_lock_server(server);
#if SMBFS_DEBUG_VERBOSE
-{struct dentry * dentry = ino->u.smbfs_i.dentry;
printk("smb_proc_write: file %s/%s, count=%d@%ld, packet_size=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name,
+((struct dentry *)ino->u.smbfs_i.dentry)->d_parent->d_name.name,
+((struct dentry *)ino->u.smbfs_i.dentry)->d_name.name,
count, offset, server->packet_size);
-}
#endif
p = smb_setup_header(server, SMBwrite, 5, count + 3);
WSET(server->packet, smb_vwv0, ino->u.smbfs_i.fileid);
WSET(p, 0, count);
memcpy(p+2, data, count);
- if ((result = smb_request_ok(server, SMBwrite, 1, 0)) >= 0)
+ result = smb_request_ok(server, SMBwrite, 1, 0);
+ if (result >= 0)
result = WVAL(server->packet, smb_vwv0);
smb_unlock_server(server);
-
return result;
}
if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
goto out;
}
smb_proc_close(server, WVAL(server->packet, smb_vwv0), CURRENT_TIME);
if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
+ goto out;
}
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
+ goto out;
}
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
+ goto out;
}
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
+ goto out;
}
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
smb_proc_trunc(struct smb_sb_info *server, __u16 fid, __u32 length)
{
char *p;
- char *buf;
int result;
smb_lock_server(server);
retry:
- buf = server->packet;
p = smb_setup_header(server, SMBwrite, 5, 0);
- WSET(buf, smb_vwv0, fid);
- WSET(buf, smb_vwv1, 0);
- DSET(buf, smb_vwv2, length);
- WSET(buf, smb_vwv4, 0);
+ WSET(server->packet, smb_vwv0, fid);
+ WSET(server->packet, smb_vwv1, 0);
+ DSET(server->packet, smb_vwv2, length);
+ WSET(server->packet, smb_vwv4, 0);
*p++ = 4;
*p++ = 0;
smb_setup_bcc(server, p);
if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0)
{
if (smb_retry(server))
- {
goto retry;
- }
+ goto out;
}
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
memset(fattr, 0, sizeof(*fattr));
fattr->f_nlink = 1;
- fattr->f_uid = server->m.uid;
- fattr->f_gid = server->m.gid;
+ fattr->f_uid = server->mnt->uid;
+ fattr->f_gid = server->mnt->gid;
fattr->f_blksize = 512;
}
static void
smb_finish_dirent(struct smb_sb_info *server, struct smb_fattr *fattr)
{
- fattr->f_mode = server->m.file_mode;
+ fattr->f_mode = server->mnt->file_mode;
if (fattr->attr & aDIR)
{
- fattr->f_mode = server->m.dir_mode;
+ fattr->f_mode = server->mnt->dir_mode;
fattr->f_size = 512;
}
smb_finish_dirent(server, fattr);
}
-
+/*
+ * Note that we are now returning the name as a reference to avoid
+ * an extra copy, and that the upper/lower casing is done in place.
+ */
static __u8 *
-smb_decode_dirent(struct smb_sb_info *server, __u8 *p, struct dirent *entry)
+smb_decode_dirent(struct smb_sb_info *server, __u8 *p,
+ struct cache_dirent *entry)
{
int len;
+ /*
+ * SMB doesn't have a concept of inode numbers ...
+ */
+ entry->ino = 0;
+
p += SMB_STATUS_SIZE; /* reserved (search_status) */
- len = strlen(p + 9);
+ entry->name = p + 9;
+ len = strlen(entry->name);
if (len > 12)
{
len = 12;
}
- memcpy(entry->d_name, p + 9, len);
-#ifdef SMBFS_TRIM_BLANKS
/*
* Trim trailing blanks for Pathworks servers
*/
- while (len > 2 && entry->d_name[len-1] == ' ')
+ while (len > 2 && entry->name[len-1] == ' ')
len--;
-#endif
- entry->d_name[len] = '\0';
- entry->d_reclen = len;
- entry->d_ino = 0; /* no inode number available */
+ entry->len = len;
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->d_name);
+ str_upper(entry->name, len);
break;
case SMB_CASE_LOWER:
- str_lower(entry->d_name);
+ str_lower(entry->name, len);
break;
default:
break;
}
- pr_debug("smb_decode_dirent: name = %s\n", entry->name);
+ pr_debug("smb_decode_dirent: len=%d, name=%s\n", len, entry->name);
return p + 22;
}
char status[SMB_STATUS_SIZE];
static struct qstr mask = { "*.*", 3, 0 };
- pr_debug("smb_proc_readdir_short: %d @ %d\n", cache_size, fpos);
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_proc_readdir_short: %s/%s, pos=%d\n",
+dir->d_parent->d_name.name, dir->d_name.name, fpos);
+#endif
smb_lock_server(server);
for (i = 0; i < count; i++)
{
- struct dirent this_ent, *entry = &this_ent;
+ struct cache_dirent this_ent, *entry = &this_ent;
p = smb_decode_dirent(server, p, entry);
- if (entries_seen == 2 && entry->d_name[0] == '.')
+ if (entries_seen == 2 && entry->name[0] == '.')
{
- if (entry->d_reclen == 1)
+ if (entry->len == 1)
continue;
- if (entry->d_name[1] == '.' &&
- entry->d_reclen == 2)
+ if (entry->name[1] == '.' && entry->len == 2)
continue;
}
if (entries_seen >= fpos)
entries_seen);
smb_add_to_cache(cachep, entry, entries_seen);
entries++;
- }
+ } else
+ {
#ifdef SMBFS_DEBUG_VERBOSE
-else
printk("smb_proc_readdir: skipped, seen=%d, i=%d, fpos=%d\n",
entries_seen, i, fpos);
#endif
+ }
entries_seen++;
}
}
/*
* Interpret a long filename structure using the specified info level:
- * level 1 -- Win NT, Win 95, OS/2
- * level 2 -- OS/2
+ * level 1 -- Win NT, Win 95, OS/2
* level 259 -- File name and length only, Win NT, Win 95
- * level 260 -- Win NT, Win 95
* There seem to be numerous inconsistencies and bugs in implementation.
+ *
+ * We return a reference to the name string to avoid copying, and perform
+ * any needed upper/lower casing in place. Note!! Level 259 entries may
+ * not have any space beyond the name, so don't try to write a null byte!
*/
static char *
smb_decode_long_dirent(struct smb_sb_info *server, char *p,
- struct dirent *entry, int level)
+ struct cache_dirent *entry, int level)
{
char *result;
- unsigned int len;
+ unsigned int len = 0;
/*
* SMB doesn't have a concept of inode numbers ...
*/
- entry->d_ino = 0;
+ entry->ino = 0;
switch (level)
{
case 1:
len = *((unsigned char *) p + 26);
- entry->d_reclen = len;
- strncpy(entry->d_name, p + 27, len);
- entry->d_name[len] = '\0';
-
+ entry->len = len;
+ entry->name = p + 27;
result = p + 28 + len;
break;
case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */
- /*
- * This info level returns just the file name and length,
- * which is all we need right now.
- */
result = p + DVAL(p, 0);
/* DVAL(p, 4) should be resume key? Seems to be 0 .. */
len = DVAL(p, 8);
if (len > 255)
len = 255;
- strncpy(entry->d_name, p + 12, len);
+ entry->name = p + 12;
/*
* Kludge alert: Win NT 4.0 adds a trailing null byte and
* counts it in the name length, but Win 95 doesn't. Hence
* we test for a trailing null and decrement the length ...
*/
- if (len && entry->d_name[len-1] == '\0')
+ if (len && entry->name[len-1] == '\0')
len--;
- entry->d_name[len] = '\0';
- entry->d_reclen = len;
+ entry->len = len;
#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_decode_long_dirent: info 259, len=%d, name=%s\n",
-len, entry->d_name);
+printk("smb_decode_long_dirent: info 259 at %p, len=%d, name=%s\n",
+p, len, entry->name);
#endif
break;
switch (server->opt.case_handling)
{
case SMB_CASE_UPPER:
- str_upper(entry->d_name);
+ str_upper(entry->name, len);
break;
case SMB_CASE_LOWER:
- str_lower(entry->d_name);
+ str_lower(entry->name, len);
break;
default:
break;
* Check whether to change the info level. There appears to be
* a bug in Win NT 4.0's handling of info level 1, whereby it
* truncates the directory scan for certain patterns of files.
- * Hence we use level 259 for NT. (Win 95 uses this too?)
+ * Hence we use level 259 for NT. (And Win 95 as well ...)
*/
if (server->opt.protocol >= SMB_PROTOCOL_NT1)
info_level = 259;
WSET(param, 10, 8 + 4 + 2); /* resume required +
close on end +
continue */
-#ifdef CONFIG_SMB_WIN95
- /* Windows 95 is not able to deliver answers
- to FIND_NEXT fast enough, so sleep 0.2 seconds */
- current->timeout = jiffies + HZ / 5;
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- current->timeout = 0;
-#endif
+ if (server->mnt->version & 1)
+ {
+ /* Windows 95 is not able to deliver answers
+ * to FIND_NEXT fast enough, so sleep 0.2 sec
+ */
+ current->timeout = jiffies + HZ / 5;
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ current->timeout = 0;
+ }
}
result = smb_trans2_request(server, command,
lastname = resp_data + ff_lastname;
switch (info_level)
{
- case 260:
+ case 259:
if (ff_lastname < resp_data_len)
mask_len = resp_data_len - ff_lastname;
break;
p = resp_data;
for (i = 0; i < ff_searchcount; i++)
{
- struct dirent this_ent, *entry = &this_ent;
+ struct cache_dirent this_ent, *entry = &this_ent;
p = smb_decode_long_dirent(server, p, entry,
info_level);
pr_debug("smb_readdir_long: got %s\n", entry->name);
/* ignore . and .. from the server */
- if (entries_seen == 2 && entry->d_name[0] == '.')
+ if (entries_seen == 2 && entry->name[0] == '.')
{
- if (entry->d_reclen == 1)
+ if (entry->len == 1)
continue;
- if (entry->d_name[1] == '.' &&
- entry->d_reclen == 2)
+ if (entry->name[1] == '.' && entry->len == 2)
continue;
}
if (entries_seen >= fpos)
}
result = -ENOENT;
if (resp_data_len < 22)
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_proc_getattr_trans2: not enough data for %s, len=%d\n",
+¶m[6], resp_data_len);
+#endif
goto out;
+ }
attr->f_ctime = date_dos2unix(WVAL(resp_data, 2),
WVAL(resp_data, 0));
smb_init_dirent(server, fattr);
/*
- * N.B. Why would we want to fall back to xxx_core on error?
- * If the file doesn't exist (a very common case), the core
- * protocol won't find it either.
- */
- if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
+ * Win 95 is painfully slow at returning trans2 getattr info ...
+ */
+ if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2 &&
+ !(server->mnt->version & 1))
result = smb_proc_getattr_trans2(server, dir, name, fattr);
else
result = smb_proc_getattr_core(server, dir, name, fattr);
return result;
}
-
/* In core protocol, there is only 1 time to be set, we use
entry->f_mtime, to make touch work. */
static int
*p++ = 0;
smb_setup_bcc(server, p);
- if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0)
+ result = smb_request_ok(server, SMBsetatr, 0, 0);
+ if (result < 0)
+ {
if (smb_retry(server))
goto retry;
-
+ goto out;
+ }
+ result = 0;
+out:
smb_unlock_server(server);
return result;
}
goto retry;
goto out;
}
+ result = 0;
if (server->rcls != 0)
result = -smb_errno(server->rcls, server->err);
out:
smb_unlock_server(server);
- return 0;
+ return result;
}
int
{
int result;
- /*
- * N.B. Why would we want to fall back to xxx_core on error?
- */
if (server->opt.protocol >= SMB_PROTOCOL_LANMAN2)
result = smb_proc_setattr_trans2(server, dir, fattr);
else
*/
#include <linux/sched.h>
-#include <linux/smb_fs.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
-#include <linux/stat.h>
#include <linux/in.h>
#include <linux/net.h>
#include <linux/mm.h>
#include <net/scm.h>
#include <net/ip.h>
+#include <linux/smb_fs.h>
#include <linux/smb.h>
#include <linux/smbno.h>
while (1)
{
+ result = -EIO;
if (sk->dead)
{
+#ifdef SMBFS_PARANOIA
printk("smb_data_callback: sock dead!\n");
- return;
+#endif
+ break;
}
+
result = _recvfrom(socket, (void *) peek_buf, 1,
MSG_PEEK | MSG_DONTWAIT);
if (result == -EAGAIN)
return smb_len(peek_buf);
}
+/*
+ * Since we allocate memory in increments of PAGE_SIZE,
+ * round up the packet length to the next multiple.
+ */
+int
+smb_round_length(int len)
+{
+ return (len + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+}
+
/*
* smb_receive
* fs points to the correct segment
smb_receive(struct smb_sb_info *server)
{
struct socket *socket = server_sock(server);
+ unsigned char * packet = server->packet;
int len, result;
unsigned char peek_buf[4];
*/
if (len + 4 > server->packet_size)
{
- char * packet;
- pr_debug("smb_receive: Increase packet size from %d to %d\n",
- server->packet_size, len + 4);
+ int new_len = smb_round_length(len + 4);
+
+#ifdef SMBFS_PARANOIA
+printk("smb_receive: Increase packet size from %d to %d\n",
+server->packet_size, new_len);
+#endif
result = -ENOMEM;
- packet = smb_vmalloc(len + 4);
+ packet = smb_vmalloc(new_len);
if (packet == NULL)
goto out;
smb_vfree(server->packet);
server->packet = packet;
- server->packet_size = len + 4;
+ server->packet_size = new_len;
}
- memcpy(server->packet, peek_buf, 4);
- result = smb_receive_raw(socket, server->packet + 4, len);
+ memcpy(packet, peek_buf, 4);
+ result = smb_receive_raw(socket, packet + 4, len);
if (result < 0)
{
#ifdef SMBFS_DEBUG_VERBOSE
#endif
goto out;
}
- server->rcls = *(server->packet+9);
- server->err = WVAL(server->packet, 11);
+ server->rcls = *(packet+9);
+ server->err = WVAL(packet, 11);
#ifdef SMBFS_DEBUG_VERBOSE
if (server->rcls != 0)
}
/*
- * This routine needs a lot of work. We should check whether the packet
- * is all one part before allocating a new one, and should try first to
- * copy to a temp buffer before allocating.
- * The final server->packet should be the larger of the two.
+ * This routine checks first for "fast track" processing, as most
+ * packets won't need to be copied. Otherwise, it allocates a new
+ * packet to hold the incoming data.
+ *
+ * Note that the final server packet must be the larger of the two;
+ * server packets aren't allowed to shrink.
*/
static int
smb_receive_trans2(struct smb_sb_info *server,
int *ldata, unsigned char **data,
- int *lparam, unsigned char **param)
+ int *lparm, unsigned char **parm)
{
- int total_data = 0;
- int total_param = 0;
+ unsigned char *inbuf, *base, *rcv_buf = NULL;
+ unsigned int parm_disp, parm_offset, parm_count, parm_tot, parm_len = 0;
+ unsigned int data_disp, data_offset, data_count, data_tot, data_len = 0;
+ unsigned int total_p = 0, total_d = 0, buf_len = 0;
int result;
- unsigned char *rcv_buf;
- int buf_len;
- int data_len = 0;
- int param_len = 0;
-
- if ((result = smb_receive(server)) < 0)
- {
- return result;
- }
- if (server->rcls != 0)
- {
- *param = *data = server->packet;
- *ldata = *lparam = 0;
- return 0;
- }
- total_data = WVAL(server->packet, smb_tdrcnt);
- total_param = WVAL(server->packet, smb_tprcnt);
-
- pr_debug("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
-
- if ((total_data > TRANS2_MAX_TRANSFER)
- || (total_param > TRANS2_MAX_TRANSFER))
- {
- pr_debug("smb_receive_trans2: data/param too long\n");
- return -EIO;
- }
- buf_len = total_data + total_param;
- if (server->packet_size > buf_len)
- {
- buf_len = server->packet_size;
- }
- if ((rcv_buf = smb_vmalloc(buf_len)) == NULL)
- {
- pr_debug("smb_receive_trans2: could not alloc data area\n");
- return -ENOMEM;
- }
- *param = rcv_buf;
- *data = rcv_buf + total_param;
while (1)
{
- unsigned char *inbuf = server->packet;
-
- if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
- > total_param)
+ result = smb_receive(server);
+ if (result < 0)
+ goto out;
+ inbuf = server->packet;
+ if (server->rcls != 0)
{
- pr_debug("smb_receive_trans2: invalid parameters\n");
- result = -EIO;
- goto fail;
+ *parm = *data = inbuf;
+ *ldata = *lparm = 0;
+ goto out;
}
- memcpy(*param + WVAL(inbuf, smb_prdisp),
- smb_base(inbuf) + WVAL(inbuf, smb_proff),
- WVAL(inbuf, smb_prcnt));
- param_len += WVAL(inbuf, smb_prcnt);
-
- if (WVAL(inbuf, smb_drdisp) + WVAL(inbuf, smb_drcnt)
- > total_data)
+ /*
+ * Extract the control data from the packet.
+ */
+ data_tot = WVAL(inbuf, smb_tdrcnt);
+ parm_tot = WVAL(inbuf, smb_tprcnt);
+ parm_disp = WVAL(inbuf, smb_prdisp);
+ parm_offset = WVAL(inbuf, smb_proff);
+ parm_count = WVAL(inbuf, smb_prcnt);
+ data_disp = WVAL(inbuf, smb_drdisp);
+ data_offset = WVAL(inbuf, smb_droff);
+ data_count = WVAL(inbuf, smb_drcnt);
+ base = smb_base(inbuf);
+
+ /*
+ * Assume success and increment lengths.
+ */
+ parm_len += parm_count;
+ data_len += data_count;
+
+ if (!rcv_buf)
{
- pr_debug("smb_receive_trans2: invalid data block\n");
- result = -EIO;
- goto fail;
+ /*
+ * Check for fast track processing ... just this packet.
+ */
+ if (parm_count == parm_tot && data_count == data_tot)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_receive_trans2: fast track, parm=%u %u %u, data=%u %u %u\n",
+parm_disp, parm_offset, parm_count, data_disp, data_offset, data_count);
+#endif
+ *parm = base + parm_offset;
+ *data = base + data_offset;
+ goto success;
+ }
+
+ if (parm_tot > TRANS2_MAX_TRANSFER ||
+ data_tot > TRANS2_MAX_TRANSFER)
+ goto out_too_long;
+
+ /*
+ * Save the total parameter and data length.
+ */
+ total_d = data_tot;
+ total_p = parm_tot;
+
+ buf_len = total_d + total_p;
+ if (server->packet_size > buf_len)
+ buf_len = server->packet_size;
+ buf_len = smb_round_length(buf_len);
+
+ rcv_buf = smb_vmalloc(buf_len);
+ if (!rcv_buf)
+ goto out_no_mem;
+ *parm = rcv_buf;
+ *data = rcv_buf + total_p;
}
- pr_debug("disp: %d, off: %d, cnt: %d\n",
- WVAL(inbuf, smb_drdisp), WVAL(inbuf, smb_droff),
- WVAL(inbuf, smb_drcnt));
+ else if (data_tot > total_d || parm_tot > total_p)
+ goto out_data_grew;
- memcpy(*data + WVAL(inbuf, smb_drdisp),
- smb_base(inbuf) + WVAL(inbuf, smb_droff),
- WVAL(inbuf, smb_drcnt));
- data_len += WVAL(inbuf, smb_drcnt);
-
- if ((WVAL(inbuf, smb_tdrcnt) > total_data)
- || (WVAL(inbuf, smb_tprcnt) > total_param))
- {
- pr_debug("smb_receive_trans2: data/params grew!\n");
- result = -EIO;
- goto fail;
- }
- /* the total lengths might shrink! */
- total_data = WVAL(inbuf, smb_tdrcnt);
- total_param = WVAL(inbuf, smb_tprcnt);
+ if (parm_disp + parm_count > total_p)
+ goto out_bad_parm;
+ if (data_disp + data_count > total_d)
+ goto out_bad_data;
+ memcpy(*parm + parm_disp, base + parm_offset, parm_count);
+ memcpy(*data + data_disp, base + data_offset, data_count);
#ifdef SMBFS_PARANOIA
-if ((data_len >= total_data || param_len >= total_param) &&
- !(data_len >= total_data && param_len >= total_param))
-printk("smb_receive_trans2: dlen=%d, tdata=%d, plen=%d, tlen=%d\n",
-data_len, total_data, param_len, total_param);
+printk("smb_receive_trans2: copied, parm=%u of %u, data=%u of %u\n",
+parm_len, parm_tot, data_len, data_tot);
#endif
- /* shouldn't this be an OR test? don't want to overrun */
- if ((data_len >= total_data) && (param_len >= total_param))
- {
+ /*
+ * Check whether we've received all of the data. Note that
+ * we use the packet totals -- total lengths might shrink!
+ */
+ if (data_len >= data_tot && parm_len >= parm_tot)
break;
- }
- if ((result = smb_receive(server)) < 0)
- {
- goto fail;
- }
- result = -EIO;
- if (server->rcls != 0)
- goto fail;
}
- *ldata = data_len;
- *lparam = param_len;
+ /*
+ * Install the new packet. Note that it's possible, though
+ * unlikely, that the new packet could be smaller than the
+ * old one, in which case we just copy the data.
+ */
+ inbuf = server->packet;
+ if (buf_len >= server->packet_size)
+ {
+ server->packet_size = buf_len;
+ server->packet = rcv_buf;
+ rcv_buf = inbuf;
+ } else
+ {
#ifdef SMBFS_PARANOIA
-if (buf_len < server->packet_size)
-printk("smb_receive_trans2: changing packet, old size=%d, new size=%d\n",
+printk("smb_receive_trans2: copying data, old size=%d, new size=%u\n",
server->packet_size, buf_len);
#endif
- smb_vfree(server->packet);
- server->packet = rcv_buf;
- server->packet_size = buf_len;
- return 0;
+ memcpy(inbuf, rcv_buf, parm_len + data_len);
+ }
- fail:
- smb_vfree(rcv_buf);
+success:
+ *ldata = data_len;
+ *lparm = parm_len;
+out:
+ if (rcv_buf)
+ smb_vfree(rcv_buf);
return result;
+
+out_no_mem:
+#ifdef SMBFS_PARANOIA
+ printk("smb_receive_trans2: couldn't allocate data area\n");
+#endif
+ result = -ENOMEM;
+ goto out;
+out_too_long:
+ printk("smb_receive_trans2: data/param too long, data=%d, parm=%d\n",
+ data_tot, parm_tot);
+ goto out_error;
+out_data_grew:
+ printk("smb_receive_trans2: data/params grew!\n");
+ goto out_error;
+out_bad_parm:
+ printk("smb_receive_trans2: invalid parms, disp=%d, cnt=%d, tot=%d\n",
+ parm_disp, parm_count, parm_tot);
+ goto out_error;
+out_bad_data:
+ printk("smb_receive_trans2: invalid data, disp=%d, cnt=%d, tot=%d\n",
+ data_disp, data_count, data_tot);
+out_error:
+ result = -EIO;
+ goto out;
}
/*
}
if (result < 0)
goto bad_conn;
- pr_debug("smb_trans2_request: result = %d\n", result);
out:
return result;
bad_conn:
#ifdef SMBFS_PARANOIA
-printk("smb_trans2_request: connection bad, setting invalid\n");
+printk("smb_trans2_request: result=%d, setting invalid\n", result);
#endif
server->state = CONN_INVALID;
smb_invalidate_inodes(server);
struct buffer_head *old_bh,*new_bh,*dotdot_bh;
struct msdos_dir_entry *old_de,*new_de,*dotdot_de;
loff_t old_offset,new_offset,old_longname_offset;
- int old_slots,old_ino,new_ino,dotdot_ino,ino;
- struct inode *old_inode, *new_inode, *dotdot_inode, *walk;
+ int old_slots,old_ino,new_ino,dotdot_ino;
+ struct inode *old_inode, *new_inode, *dotdot_inode;
+ struct dentry *walk;
int res, is_dir, i;
int locked = 0;
struct slot_info sinfo;
res = -EINVAL;
goto rename_done;
}
- if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
+ walk = new_dentry;
/* prevent moving directory below itself */
- while (walk->i_ino != MSDOS_ROOT_INO) {
- ino = fat_parent_ino(walk,1);
- iput(walk);
- if (ino < 0) {
- res = ino;
- goto rename_done;
- }
- if (ino == old_ino) {
- res = -EINVAL;
- goto rename_done;
- }
- if (!(walk = iget(new_dir->i_sb,ino))) {
- res = -EIO;
- goto rename_done;
- }
+ for (;;) {
+ if (walk == old_dentry) return -EINVAL;
+ if (walk == walk->d_parent) break;
+ walk = walk->d_parent;
}
- iput(walk);
}
res = vfat_find(new_dir,&new_dentry->d_name,1,0,is_dir,&sinfo);
}
if (res > 0) res = 0;
- d_instantiate(new_dentry,new_inode);
- d_delete(old_dentry);
+ if (res == 0) {
+ d_move(old_dentry, new_dentry);
+ }
rename_done:
if (locked)
#ifndef _ASM_SOCKET_H
#define _ASM_SOCKET_H
-#include <linux/types.h>
-#include <asm/ioctl.h>
#include <asm/sockios.h>
/* For setsockoptions(2) */
*/
struct qstr {
const unsigned char * name;
- unsigned int len, hash;
+ unsigned int len;
+ unsigned int hash;
};
/* Name hashing routines. Initial hash value */
return (unsigned int) hash;
}
+/* Compute the hash for a name string. */
+static inline unsigned int full_name_hash(const char * name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
+ while (len--)
+ hash = partial_name_hash(*name++, hash);
+ return end_name_hash(hash);
+}
+
struct dentry {
int d_count;
unsigned int d_flags;
*/
extern int nfs_writepage(struct inode *, struct page *);
extern int nfs_check_error(struct inode *);
-extern int nfs_flush_dirty_pages(struct inode *, off_t, off_t);
+extern int nfs_flush_dirty_pages(struct inode *, pid_t, off_t, off_t);
extern int nfs_truncate_dirty_pages(struct inode *, unsigned long);
extern void nfs_invalidate_pages(struct inode *);
extern int nfs_updatepage(struct inode *, struct page *, const char *,
#undef __FDMASK
#define __FDMASK(d) (1UL << ((d) % __NFDBITS))
-typedef struct fd_set {
+typedef struct {
unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
#endif /* DEBUG_SMB_MALLOC */
-struct smb_sb_info;
+/* linux/fs/smbfs/mmap.c */
+int smb_mmap(struct file *, struct vm_area_struct *);
/* linux/fs/smbfs/file.c */
extern struct inode_operations smb_file_inode_operations;
/* linux/fs/smbfs/dir.c */
extern struct inode_operations smb_dir_inode_operations;
-void smb_init_root(struct smb_sb_info *);
-int smb_stat_root(struct smb_sb_info *);
void smb_renew_times(struct dentry *);
/* linux/fs/smbfs/ioctl.c */
-int smb_ioctl (struct inode * inode, struct file * filp,
- unsigned int cmd, unsigned long arg);
+int smb_ioctl (struct inode *, struct file *, unsigned int, unsigned long);
/* linux/fs/smbfs/inode.c */
struct super_block *smb_read_super(struct super_block *, void *, int);
void smb_init_root_dirent(struct smb_sb_info *server, struct smb_fattr *);
/* linux/fs/smbfs/sock.c */
+int smb_round_length(int);
int smb_valid_socket(struct inode *);
void smb_close_socket(struct smb_sb_info *);
int smb_release(struct smb_sb_info *server);
int *lrdata, unsigned char **rdata,
int *lrparam, unsigned char **rparam);
-/* linux/fs/smbfs/mmap.c */
-int smb_mmap(struct file * file, struct vm_area_struct * vma);
-
/* fs/smbfs/cache.c */
/*
void smb_init_dircache(struct cache_head *);
void smb_free_dircache(struct cache_head *);
int smb_refill_dircache(struct cache_head *, struct dentry *);
-void smb_add_to_cache(struct cache_head *, struct dirent *, off_t);
+void smb_add_to_cache(struct cache_head *, struct cache_dirent *, off_t);
int smb_find_in_cache(struct cache_head *, off_t, struct cache_dirent *);
void smb_invalid_dir_cache(struct inode *);
#include <linux/types.h>
#include <linux/smb.h>
-#include <linux/smb_mount.h>
/* Get the server for the specified dentry */
#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb
enum smb_conn_state state;
struct file * sock_file;
- struct smb_mount_data m;
+ struct smb_mount_data *mnt;
/* Connections are counted. Each time a new socket arrives,
* generation is incremented.
struct mm_struct *mm = current->mm;
lock_kernel();
- retval = mm->brk;
if (brk < mm->end_code)
goto out;
newbrk = PAGE_ALIGN(brk);
oldbrk = PAGE_ALIGN(mm->brk);
- if (oldbrk == newbrk) {
- retval = mm->brk = brk;
- goto out;
- }
+ if (oldbrk == newbrk)
+ goto set_brk;
/* Always allow shrinking brk. */
if (brk <= mm->brk) {
- retval = mm->brk = brk;
- do_munmap(newbrk, oldbrk-newbrk);
+ if (!do_munmap(newbrk, oldbrk-newbrk))
+ goto set_brk;
goto out;
}
/* Check against rlimit and stack.. */
- retval = mm->brk;
rlim = current->rlim[RLIMIT_DATA].rlim_cur;
if (rlim >= RLIM_INFINITY)
rlim = ~0;
goto out;
/* Ok, looks good - let it rip. */
- if(do_mmap(NULL, oldbrk, newbrk-oldbrk,
+ if (do_mmap(NULL, oldbrk, newbrk-oldbrk,
PROT_READ|PROT_WRITE|PROT_EXEC,
- MAP_FIXED|MAP_PRIVATE, 0) == oldbrk)
- mm->brk = brk;
- retval = mm->brk;
+ MAP_FIXED|MAP_PRIVATE, 0) != oldbrk)
+ goto out;
+set_brk:
+ mm->brk = brk;
out:
+ retval = mm->brk;
unlock_kernel();
return retval;
}
{
struct mm_struct * mm = current->mm;
struct vm_area_struct * vma;
- int correct_wcount = 0;
+ int correct_wcount = 0, error;
if ((len = PAGE_ALIGN(len)) == 0)
return addr;
vma->vm_dentry = NULL;
vma->vm_pte = 0;
- do_munmap(addr, len); /* Clear old maps */
+ /* Clear old maps */
+ error = -ENOMEM;
+ if (do_munmap(addr, len))
+ goto free_vma;
/* Check against address space limit. */
if ((mm->total_vm << PAGE_SHIFT) + len
- > current->rlim[RLIMIT_AS].rlim_cur) {
- kmem_cache_free(vm_area_cachep, vma);
- return -ENOMEM;
- }
+ > current->rlim[RLIMIT_AS].rlim_cur)
+ goto free_vma;
/* Private writable mapping? Check memory availability.. */
- if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE) {
- if (!(flags & MAP_NORESERVE) &&
- !vm_enough_memory(len >> PAGE_SHIFT)) {
- kmem_cache_free(vm_area_cachep, vma);
- return -ENOMEM;
- }
- }
+ if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
+ !(flags & MAP_NORESERVE) &&
+ !vm_enough_memory(len >> PAGE_SHIFT))
+ goto free_vma;
+ error = 0;
if (file) {
- int error = 0;
if (vma->vm_flags & VM_DENYWRITE) {
if (file->f_dentry->d_inode->i_writecount > 0)
error = -ETXTBSY;
if (!error)
error = file->f_op->mmap(file, vma);
- if (error) {
- if (correct_wcount)
- file->f_dentry->d_inode->i_writecount++;
- kmem_cache_free(vm_area_cachep, vma);
- return error;
- }
}
+ /* Fix up the count if necessary, then check for an error */
+ if (correct_wcount)
+ file->f_dentry->d_inode->i_writecount++;
+ if (error)
+ goto free_vma;
+ /*
+ * merge_segments may merge our vma, so we can't refer to it
+ * after the call. Save the values we need now ...
+ */
flags = vma->vm_flags;
+ addr = vma->vm_start; /* can addr have changed?? */
insert_vm_struct(mm, vma);
- if (correct_wcount)
- file->f_dentry->d_inode->i_writecount++;
merge_segments(mm, vma->vm_start, vma->vm_end);
- addr = vma->vm_start;
-
- /* merge_segments might have merged our vma, so we can't use it any more */
mm->total_vm += len >> PAGE_SHIFT;
if ((flags & VM_LOCKED) && !(flags & VM_IO)) {
unsigned long start = addr;
} while (len > 0);
}
return addr;
+
+free_vma:
+ kmem_cache_free(vm_area_cachep, vma);
+ return error;
}
/* Get an address range which is currently unmapped.
#include <linux/swapctl.h>
#include <linux/init.h>
-#include <asm/dma.h>
-#include <asm/system.h> /* for cli()/sti() */
-#include <asm/uaccess.h> /* for cop_to/from_user */
#include <asm/bitops.h>
#include <asm/pgtable.h>
return 0;
}
+/*
+ * If swap_map[] reaches 127, the entries are treated as "permanent".
+ */
void swap_duplicate(unsigned long entry)
{
struct swap_info_struct * p;
unsigned long offset, type;
if (!entry)
- return;
- offset = SWP_OFFSET(entry);
+ goto out;
type = SWP_TYPE(entry);
if (type & SHM_SWP_TYPE)
- return;
- if (type >= nr_swapfiles) {
- printk("Trying to duplicate nonexistent swap-page\n");
- return;
- }
+ goto out;
+ if (type >= nr_swapfiles)
+ goto bad_file;
p = type + swap_info;
- if (offset >= p->max) {
- printk("swap_duplicate: weirdness\n");
- return;
- }
- if (!p->swap_map[offset]) {
- printk("swap_duplicate: trying to duplicate unused page\n");
- return;
+ offset = SWP_OFFSET(entry);
+ if (offset >= p->max)
+ goto bad_offset;
+ if (!p->swap_map[offset])
+ goto bad_unused;
+ if (p->swap_map[offset] < 126)
+ p->swap_map[offset]++;
+ else {
+ static int overflow = 0;
+ if (overflow++ < 5)
+ printk("swap_duplicate: entry %08lx map count=%d\n",
+ entry, p->swap_map[offset]);
+ p->swap_map[offset] = 127;
}
- p->swap_map[offset]++;
+out:
return;
+
+bad_file:
+ printk("swap_duplicate: Trying to duplicate nonexistent swap-page\n");
+ goto out;
+bad_offset:
+ printk("swap_duplicate: offset exceeds max\n");
+ goto out;
+bad_unused:
+ printk("swap_duplicate: unused page\n");
+ goto out;
}
#include <linux/malloc.h>
#include <linux/blkdev.h> /* for blk_size */
#include <linux/vmalloc.h>
-#include <linux/dcache.h>
-#include <asm/dma.h>
-#include <asm/system.h> /* for cli()/sti() */
-#include <asm/uaccess.h> /* for copy_to/from_user */
#include <asm/bitops.h>
#include <asm/pgtable.h>
}
}
+/*
+ * If the swap count overflows (swap_map[] == 127), the entry is considered
+ * "permanent" and can't be reclaimed until the swap device is closed.
+ */
void swap_free(unsigned long entry)
{
struct swap_info_struct * p;
unsigned long offset, type;
if (!entry)
- return;
+ goto out;
type = SWP_TYPE(entry);
if (type & SHM_SWP_TYPE)
- return;
- if (type >= nr_swapfiles) {
- printk("Trying to free nonexistent swap-page\n");
- return;
- }
+ goto out;
+ if (type >= nr_swapfiles)
+ goto bad_nofile;
p = & swap_info[type];
+ if (!(p->flags & SWP_USED))
+ goto bad_device;
+ if (p->prio > swap_info[swap_list.next].prio)
+ swap_list.next = swap_list.head;
offset = SWP_OFFSET(entry);
- if (offset >= p->max) {
- printk("swap_free: weirdness\n");
- return;
- }
- if (!(p->flags & SWP_USED)) {
- printk("Trying to free swap from unused swap-device\n");
- return;
- }
+ if (offset >= p->max)
+ goto bad_offset;
if (offset < p->lowest_bit)
p->lowest_bit = offset;
if (offset > p->highest_bit)
p->highest_bit = offset;
if (!p->swap_map[offset])
- printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
- else
+ goto bad_free;
+ if (p->swap_map[offset] < 127) {
if (!--p->swap_map[offset])
nr_swap_pages++;
- if (p->prio > swap_info[swap_list.next].prio) {
- swap_list.next = swap_list.head;
}
+out:
+ return;
+
+bad_nofile:
+ printk("swap_free: Trying to free nonexistent swap-page\n");
+ goto out;
+bad_device:
+ printk("swap_free: Trying to free swap from unused swap-device\n");
+ goto out;
+bad_offset:
+ printk("swap_free: offset exceeds max\n");
+ goto out;
+bad_free:
+ printk("swap_free: swap-space map bad (entry %08lx)\n",entry);
+ goto out;
}
/*
- * Trying to stop swapping from a file is fraught with races, so
- * we repeat quite a bit here when we have to pause. swapoff()
- * isn't exactly timing-critical, so who cares (but this is /really/
- * inefficient, ugh).
- *
- * We return 1 after having slept, which makes the process start over
- * from the beginning for this process..
+ * The swap entry has been read in advance, and we return 1 to indicate
+ * that the page has been used or is no longer needed.
*/
static inline int unuse_pte(struct vm_area_struct * vma, unsigned long address,
pte_t *dir, unsigned long entry, unsigned long page)
if (pte_val(pte) != entry)
return 0;
set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
- flush_tlb_page(vma, address);
++vma->vm_mm->rss;
- swap_free(pte_val(pte));
+ swap_free(entry);
return 1;
}
return 0;
}
-static unsigned long find_swap_entry(int type)
-{
- struct swap_info_struct * p = &swap_info[type];
- int i;
-
- for (i = 1 ; i < p->max ; i++) {
- if (p->swap_map[i] > 0 && p->swap_map[i] != 0x80)
- return SWP_ENTRY(type, i);
- }
- return 0;
-}
-
/*
* We completely avoid races by reading each swap page in advance,
* and then search for the process using it. All the necessary
*/
static int try_to_unuse(unsigned int type)
{
- unsigned long page = 0;
+ struct swap_info_struct * si = &swap_info[type];
struct task_struct *p;
+ unsigned long page = 0;
unsigned long entry;
+ int i;
- /*
- * Find all swap entries in use ...
- */
- while ((entry = find_swap_entry(type)) != 0) {
+ while (1) {
if (!page) {
page = __get_free_page(GFP_KERNEL);
if (!page)
}
/*
- * Read in the page, and then free the swap page.
- */
+ * Find a swap page in use and read it in.
+ */
+ for (i = 1 , entry = 0; i < si->max ; i++) {
+ if (si->swap_map[i] > 0 && si->swap_map[i] != 0x80) {
+ entry = SWP_ENTRY(type, i);
+ break;
+ }
+ }
+ if (!entry)
+ break;
read_swap_page(entry, (char *) page);
read_lock(&tasklist_lock);
unlock:
read_unlock(&tasklist_lock);
if (page) {
- printk("try_to_unuse: didn't find entry %8lx\n",
- entry);
- swap_free(entry);
+ /*
+ * If we couldn't find an entry, there are several
+ * possible reasons: someone else freed it first,
+ * we freed the last reference to an overflowed entry,
+ * or the system has lost track of the use counts.
+ */
+ if (si->swap_map[i] != 0) {
+ if (si->swap_map[i] != 127)
+ printk("try_to_unuse: entry %08lx "
+ "not in use\n", entry);
+ si->swap_map[i] = 0;
+ nr_swap_pages++;
+ }
}
}