update for 2.1.56 smbfs

Bill Hawes (whawes@star.net)
Sat, 20 Sep 1997 10:29:43 -0400


This is a multi-part message in MIME format.
--------------14EDD65EA532BD37241DCB7C
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

I've attached the latest smbfs patch against 2.1.56. The main
improvements are in inode revalidation and refreshing, and smbfs now
cleanly handles the problem of inode mode changes (file to dir or dir to
file).

The patch includes a couple of proposed additions to fs/dcache,
shrink_dcache_sb() and a new unhash_dentries() routine. The latter does
a search of the dentry hash table to unhash all dentries using an inode
that has become invalid (e.g. becasue of a mode change.) Because it's
possible that several dentries could be using a single inode, we want to
drop all of them when the inode goes bad, rather than waiting for the
next use. Any suggestions on this approach?

With these changes smbfs is now quite robust, and except for some minor
timestamp problems seems relatively bug-free. But I'm sure all the
testers out there will find some ...

Regards,
Bill
--------------14EDD65EA532BD37241DCB7C
Content-Type: text/plain; charset=us-ascii; name="smbfs_56-patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="smbfs_56-patch"

--- linux-2.1.56/include/linux/smb_fs_i.h.old Sat Sep 20 08:16:16 1997
+++ linux-2.1.56/include/linux/smb_fs_i.h Mon Sep 15 09:11:55 1997
@@ -22,11 +22,12 @@
* (open == generation).
*/
unsigned int open;
- void * dentry; /* The dentry we were opened with */
__u16 fileid; /* What id to handle a file with? */
__u16 attr; /* Attribute fields, DOS value */

__u16 access; /* Access bits. */
+ time_t oldmtime; /* last time modified */
+ void * dentry; /* The dentry we were opened with */
};

#endif
--- linux-2.1.56/include/linux/smb_fs_sb.h.old Thu Sep 4 12:33:52 1997
+++ linux-2.1.56/include/linux/smb_fs_sb.h Tue Sep 16 10:31:23 1997
@@ -15,6 +15,9 @@
#include <linux/smb.h>
#include <linux/smb_mount.h>

+#define SB_of(server) ((struct super_block *) ((char *)(server) - \
+ (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+
struct smb_sb_info {
enum smb_conn_state state;
struct file * sock_file;
@@ -29,6 +32,7 @@
struct smb_conn_opt opt;

struct semaphore sem;
+ struct wait_queue * wait;

__u32 packet_size;
unsigned char * packet;
--- linux-2.1.56/fs/smbfs/inode.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.56/fs/smbfs/inode.c Sat Sep 20 09:23:56 1997
@@ -18,17 +18,18 @@
#include <linux/stat.h>
#include <linux/errno.h>
#include <linux/locks.h>
-#include <linux/file.h>
-#include <linux/fcntl.h>
#include <linux/malloc.h>
#include <linux/init.h>
+#include <linux/dcache.h>

#include <asm/system.h>
#include <asm/uaccess.h>

-#define SB_of(server) ((struct super_block *) ((char *)(server) - \
- (unsigned long)(&((struct super_block *)0)->u.smbfs_sb)))
+/* #define SMBFS_DEBUG_VERBOSE 1 */
+#define SMBFS_PARANOIA 1

+extern void shrink_dcache_sb(struct super_block *);
+extern void unhash_dentries(struct inode *);
extern int close_fp(struct file *filp);

static void smb_put_inode(struct inode *);
@@ -37,6 +38,7 @@
static void smb_put_super(struct super_block *);
static int smb_statfs(struct super_block *, struct statfs *, int);

+extern struct inode_operations bad_inode_ops;
static struct super_operations smb_sops =
{
smb_read_inode, /* read inode */
@@ -67,7 +69,7 @@
return ino;
}

-static struct smb_fattr *read_fattr;
+static struct smb_fattr *read_fattr = NULL;
static struct semaphore read_semaphore = MUTEX;

struct inode *
@@ -80,6 +82,7 @@
down(&read_semaphore);
read_fattr = fattr;
result = iget(sb, fattr->f_ino);
+ read_fattr = NULL;
up(&read_semaphore);
return result;
}
@@ -87,7 +90,6 @@
static void
smb_set_inode_attr(struct inode *inode, struct smb_fattr *fattr)
{
- inode->i_dev = inode->i_sb->s_dev;
inode->i_mode = fattr->f_mode;
inode->i_nlink = fattr->f_nlink;
inode->i_uid = fattr->f_uid;
@@ -99,6 +101,10 @@
inode->i_atime = fattr->f_atime;
inode->i_blksize= fattr->f_blksize;
inode->i_blocks = fattr->f_blocks;
+ /*
+ * Update the "last time modified" field for revalidation.
+ */
+ inode->u.smbfs_i.oldmtime = inode->i_mtime;
}

static void
@@ -106,15 +112,15 @@
{
pr_debug("smb_iget: %p\n", read_fattr);

- if ((atomic_read(&read_semaphore.count) == 1) ||
- (inode->i_ino != read_fattr->f_ino))
+ if (!read_fattr || inode->i_ino != read_fattr->f_ino)
{
printk("smb_read_inode called from invalid point\n");
return;
}

- smb_set_inode_attr(inode, read_fattr);
+ inode->i_dev = inode->i_sb->s_dev;
memset(&(inode->u.smbfs_i), 0, sizeof(inode->u.smbfs_i));
+ smb_set_inode_attr(inode, read_fattr);

if (S_ISREG(inode->i_mode))
inode->i_op = &smb_file_inode_operations;
@@ -132,58 +138,117 @@
smb_invalidate_inodes(struct smb_sb_info *server)
{
printk("smb_invalidate_inodes\n");
- shrink_dcache(); /* should shrink only this sb */
+ shrink_dcache_sb(SB_of(server));
invalidate_inodes(SB_of(server));
}

int
-smb_revalidate_inode(struct inode *ino)
+smb_revalidate_inode(struct inode *inode)
{
- struct dentry * dentry = ino->u.smbfs_i.dentry;
- int error = 0;
+ time_t last_time;
+ int error;

pr_debug("smb_revalidate_inode\n");
- if (!ino)
- goto bad_no_inode;
- dentry = ino->u.smbfs_i.dentry;
-#if 0
- if (dentry)
+ /*
+ * Save the last modified time, then refresh the inode
+ */
+ last_time = inode->i_mtime;
+ error = smb_refresh_inode(inode);
+ if (!error)
{
- printk("smb_revalidate: checking %s/%s\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
- }
+ if (inode->i_mtime != last_time)
+ {
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_revalidate: %s/%s changed, old=%ld, new=%ld\n",
+inode->u.smbfs_i.dentry->d_parent->d_name.name,
+inode->u.smbfs_i.dentry->d_name.name, (long) last_time, (long) inode->i_mtime);
#endif
-out:
+ if (!S_ISDIR(inode->i_mode))
+ invalidate_inode_pages(inode);
+ else
+ smb_invalid_dir_cache(inode);
+ }
+ }
return error;
-
-bad_no_inode:
- printk("smb_revalidate: no inode!\n");
- error = -EINVAL;
- goto out;
}

+/*
+ * This is called to update the inode attributes after
+ * we've made changes to a file or directory.
+ */
int
-smb_refresh_inode(struct inode *ino)
+smb_refresh_inode(struct inode *inode)
{
- struct dentry * dentry = ino->u.smbfs_i.dentry;
+ struct dentry * dentry;
struct smb_fattr fattr;
int error;

pr_debug("smb_refresh_inode\n");
error = -EIO;
- if (!dentry) {
- printk("smb_refresh_inode: no dentry, can't refresh\n");
+ if (!inode)
+ goto bad_no_inode;
+
+ dentry = inode->u.smbfs_i.dentry;
+ if (!dentry)
+ goto bad_no_dentry;
+ /*
+ * Kludge alert ... for some reason we can't get attributes
+ * for the root directory, so just return success.
+ */
+ error = 0;
+ if (IS_ROOT(dentry))
goto out;
- }

- /* N.B. Should check for changes of important fields! cf. NFS */
error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
if (!error)
{
- smb_set_inode_attr(ino, &fattr);
+ /*
+ * Check whether the type part of the mode changed,
+ * and don't update the attributes if it did.
+ */
+ if ((inode->i_mode & S_IFMT) == (fattr.f_mode & S_IFMT))
+ smb_set_inode_attr(inode, &fattr);
+ else
+ {
+ /*
+ * Big trouble! The inode has become a new object,
+ * so any operations attempted on it are invalid.
+ *
+ * Take several steps to limit damage:
+ * (1) Unhash the dentry to force a new lookup
+ * on future references.
+ * (2) Clear i_nlink so the inode will be released
+ * at iput() time. (Unhash it as well?)
+ * (3) Install the bad_inode_ops vector.
+ */
+#ifdef SMBFS_PARANOIA
+printk("smb_refresh_inode: %s/%s changed mode, %07o to %07o\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+inode->i_mode, fattr.f_mode);
+#endif
+ inode->i_nlink = 0;
+ inode->i_op = &bad_inode_ops;
+ /*
+ * Unhash _all_ dentries using this inode
+ */
+ unhash_dentries(inode);
+ /*
+ * One of these may be needed ...
+ */
+ invalidate_inode_pages(inode);
+ smb_invalid_dir_cache(inode);
+ error = -EIO;
+ }
}
out:
return error;
+
+bad_no_inode:
+ printk("smb_refresh_inode: no inode!\n");
+ goto out;
+bad_no_dentry:
+ printk("smb_refresh_inode: no dentry, can't refresh\n");
+ goto out;
}

/*
@@ -192,10 +257,10 @@
static void
smb_put_inode(struct inode *ino)
{
- struct dentry * dentry;
pr_debug("smb_put_inode: count = %d\n", ino->i_count);

if (ino->i_count > 1) {
+ struct dentry * dentry;
/*
* Check whether the dentry still holds this inode.
* This looks scary, but should work ... d_inode is
@@ -204,7 +269,7 @@
dentry = (struct dentry *) ino->u.smbfs_i.dentry;
if (dentry && dentry->d_inode != ino) {
ino->u.smbfs_i.dentry = NULL;
-#if 1
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_put_inode: cleared dentry for %s/%s (%ld), count=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, ino->i_ino, ino->i_count);
#endif
@@ -263,7 +328,6 @@
struct smb_mount_data *data = (struct smb_mount_data *)raw_data;
struct smb_fattr root;
kdev_t dev = sb->s_dev;
- unsigned char *packet;
struct inode *root_inode;
struct dentry *dentry;

@@ -275,24 +339,24 @@
if (data->version != SMB_MOUNT_VERSION)
goto out_wrong_data;

- packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
- if (!packet)
- goto out_no_mem;
-
lock_super(sb);

sb->s_blocksize = 1024; /* Eh... Is this correct? */
sb->s_blocksize_bits = 10;
sb->s_magic = SMB_SUPER_MAGIC;
- sb->s_dev = dev;
+ sb->s_dev = dev; /* shouldn't need this ... */
sb->s_op = &smb_sops;

sb->u.smbfs_sb.sock_file = NULL;
sb->u.smbfs_sb.sem = MUTEX;
+ sb->u.smbfs_sb.wait = NULL;
sb->u.smbfs_sb.conn_pid = 0;
- sb->u.smbfs_sb.packet = packet;
- sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
+ sb->u.smbfs_sb.state = CONN_INVALID; /* no connection yet */
sb->u.smbfs_sb.generation = 0;
+ sb->u.smbfs_sb.packet_size = SMB_INITIAL_PACKET_SIZE;
+ sb->u.smbfs_sb.packet = smb_vmalloc(SMB_INITIAL_PACKET_SIZE);
+ if (!sb->u.smbfs_sb.packet)
+ goto out_no_mem;

sb->u.smbfs_sb.m = *data;
sb->u.smbfs_sb.m.file_mode = (sb->u.smbfs_sb.m.file_mode &
@@ -300,37 +364,40 @@
sb->u.smbfs_sb.m.dir_mode = (sb->u.smbfs_sb.m.dir_mode &
(S_IRWXU | S_IRWXG | S_IRWXO)) | S_IFDIR;

+ /*
+ * Keep the super block locked while we get the root inode.
+ */
smb_init_root_dirent(&(sb->u.smbfs_sb), &root);
-
- sb->s_root = NULL;
- unlock_super(sb);
-
root_inode = smb_iget(sb, &root);
if (!root_inode)
goto out_no_root;
+
dentry = d_alloc_root(root_inode, NULL);
if (!dentry)
goto out_no_root;
root_inode->u.smbfs_i.dentry = dentry;
sb->s_root = dentry;
+
+ unlock_super(sb);
return sb;

-out_no_data:
- printk("smb_read_super: missing data argument\n");
- goto out;
+out_no_root:
+ iput(root_inode);
+ smb_vfree(sb->u.smbfs_sb.packet);
+ unlock_super(sb);
+ printk(KERN_ERR "smb_read_super: get root inode failed\n");
+ goto out_fail;
+out_no_mem:
+ unlock_super(sb);
+ pr_debug("smb_read_super: could not alloc packet\n");
+ goto out_fail;
out_wrong_data:
printk(KERN_ERR "smb_read_super: wrong data argument."
" Recompile smbmount.\n");
- goto out;
-out_no_mem:
- pr_debug("smb_read_super: could not alloc packet\n");
- goto out;
-out_no_root:
- sb->s_dev = 0; /* iput() might block */
- printk(KERN_ERR "smb_read_super: get root inode failed\n");
- iput(root_inode);
- smb_vfree(packet);
-out:
+ goto out_fail;
+out_no_data:
+ printk("smb_read_super: missing data argument\n");
+out_fail:
sb->s_dev = 0;
MOD_DEC_USE_COUNT;
return NULL;
@@ -355,8 +422,9 @@
int
smb_notify_change(struct inode *inode, struct iattr *attr)
{
+ struct smb_sb_info *server = SMB_SERVER(inode);
struct dentry *dentry = inode->u.smbfs_i.dentry;
- int error;
+ int error, refresh = 0;

error = -EIO;
if (!dentry)
@@ -365,40 +433,52 @@
goto out;
}

+ /*
+ * Make sure our inode is up-to-date ...
+ */
+ error = smb_revalidate_inode(inode);
+ if (error)
+ goto out;
+
if ((error = inode_change_ok(inode, attr)) < 0)
goto out;

error = -EPERM;
- if (((attr->ia_valid & ATTR_UID) &&
- (attr->ia_uid != SMB_SERVER(inode)->m.uid)))
+ if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != server->m.uid)))
goto out;

- if (((attr->ia_valid & ATTR_GID) &&
- (attr->ia_uid != SMB_SERVER(inode)->m.gid)))
+ if (((attr->ia_valid & ATTR_GID) && (attr->ia_uid != server->m.gid)))
goto out;

if (((attr->ia_valid & ATTR_MODE) &&
(attr->ia_mode & ~(S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO))))
goto out;

- /*
- * Assume success and invalidate the parent's dir cache
- */
- smb_invalid_dir_cache(dentry->d_parent->d_inode);
-
if ((attr->ia_valid & ATTR_SIZE) != 0)
{
- if ((error = smb_open(dentry, O_WRONLY)) < 0)
+ error = smb_open(dentry, O_WRONLY);
+ if (error)
goto out;
-
- if ((error = smb_proc_trunc(SMB_SERVER(inode),
- inode->u.smbfs_i.fileid,
- attr->ia_size)) < 0)
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_notify_change: changing %s/%s, old size=%ld, new size=%ld\n",
+dentry->d_parent->d_name.name, dentry->d_name.name,
+(long) inode->i_size, (long) attr->ia_size);
+#endif
+ error = smb_proc_trunc(server, inode->u.smbfs_i.fileid,
+ attr->ia_size);
+ if (error)
goto out;
+ /*
+ * We don't implement a truncate() operation,
+ * so we have to update the page cache here.
+ */
+ if (attr->ia_size < inode->i_size)
+ truncate_inode_pages(inode, attr->ia_size);
+ refresh = 1;
}
+
if ((attr->ia_valid & (ATTR_CTIME | ATTR_MTIME | ATTR_ATIME)) != 0)
{
-
struct smb_fattr fattr;

fattr.attr = 0;
@@ -417,15 +497,20 @@
if ((attr->ia_valid & ATTR_ATIME) != 0)
fattr.f_atime = attr->ia_atime;

- error = smb_proc_setattr(SMB_SERVER(inode), dentry, &fattr);
- if (error >= 0)
- {
- inode->i_ctime = fattr.f_ctime;
- inode->i_mtime = fattr.f_mtime;
- inode->i_atime = fattr.f_atime;
- }
+ error = smb_proc_setattr(server, dentry, &fattr);
+ if (error)
+ goto out;
+ refresh = 1;
}
+ error = 0;
+
out:
+ if (refresh)
+ {
+ /* N.B. Why do this? We haven't deleted an entry ... */
+ smb_invalid_dir_cache(dentry->d_parent->d_inode);
+ smb_refresh_inode(inode);
+ }
return error;
}

@@ -444,6 +529,7 @@

__initfunc(int init_smb_fs(void))
{
+ smb_init_dir_cache();
return register_filesystem(&smb_fs_type);
}

--- linux-2.1.56/fs/smbfs/dir.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.56/fs/smbfs/dir.c Fri Sep 19 18:10:08 1997
@@ -25,12 +25,10 @@

#define this_dir_cached(dir) ((dir->i_sb == c_sb) && (dir->i_ino == c_ino))

-static long
-smb_dir_read(struct inode *inode, struct file *filp,
- char *buf, unsigned long count);
-
-static int
-smb_readdir(struct file *filp, void *dirent, filldir_t filldir);
+static long smb_dir_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count);
+static int smb_readdir(struct file *filp, void *dirent, filldir_t filldir);
+static int smb_dir_open(struct inode *, struct file *);

static int smb_lookup(struct inode *, struct dentry *);
static int smb_create(struct inode *, struct dentry *, int);
@@ -39,6 +37,8 @@
static int smb_unlink(struct inode *, struct dentry *);
static int smb_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);

+static void smb_put_dentry(struct dentry *);
+
static struct file_operations smb_dir_operations =
{
NULL, /* lseek - default */
@@ -48,7 +48,7 @@
NULL, /* poll - default */
smb_ioctl, /* ioctl */
NULL, /* mmap */
- NULL, /* no special open code */
+ smb_dir_open, /* open(struct inode *, struct file *) */
NULL, /* no special release code */
NULL /* fsync */
};
@@ -75,7 +75,6 @@
NULL /* smap */
};

-static void smb_put_dentry(struct dentry *);
static struct dentry_operations smbfs_dentry_operations =
{
NULL, /* revalidate */
@@ -91,6 +90,16 @@
return -EISDIR;
}

+static int
+smb_dir_open(struct inode *dir, struct file *file)
+{
+#ifdef SMBFS_DEBUG_VERBOSE
+printk("smb_dir_open: (%s/%s)\n", file->f_dentry->d_parent->d_name.name,
+file->f_dentry->d_name.name);
+#endif
+ return smb_revalidate_inode(dir);
+}
+
/*
* This is the callback from dput(). We close the file so that
* cached dentries don't keep the file open.
@@ -104,9 +113,11 @@
}

/* Static variables for the dir cache */
+static struct semaphore refill_cache_sem = MUTEX;
static struct smb_dirent *c_entry = NULL;
static struct super_block * c_sb = NULL;
static unsigned long c_ino = 0;
+static time_t c_mtime;
static int c_seen_eof;
static int c_size;
static int c_last_returned_index;
@@ -114,36 +125,31 @@
static struct smb_dirent *
smb_search_in_cache(struct inode *dir, unsigned long f_pos)
{
- int i;

- if (this_dir_cached(dir))
- for (i = 0; i < c_size; i++)
+ if (this_dir_cached(dir) && c_entry[0].f_pos <= f_pos)
{
- if (c_entry[i].f_pos < f_pos)
- continue;
- if (c_entry[i].f_pos == f_pos)
+ unsigned long index = f_pos - c_entry[0].f_pos;
+ if (index < c_size)
{
- c_last_returned_index = i;
- return &(c_entry[i]);
+ c_last_returned_index = index;
+ return &(c_entry[index]);
}
- break;
}
return NULL;
}

/*
- * Compute the hash for a qstr ... move to include/linux/dcache.h?
+ * Compute the hash for a qstr.
+ * N.B. Move to include/linux/dcache.h?
*/
static unsigned int hash_it(const char * name, unsigned int len)
{
- unsigned long hash;
- hash = init_name_hash();
+ unsigned long hash = init_name_hash();
while (len--)
hash = partial_name_hash(*name++, hash);
return end_name_hash(hash);
}

-static struct semaphore refill_cache_sem = MUTEX;
/*
* Called with the refill semaphore held.
*/
@@ -154,6 +160,14 @@
ino_t ino_start;
int i, result;

+ /*
+ * Set the cache directory before we try, in case
+ * another task needs to invalidate it.
+ */
+ retry:
+ c_sb = dir->i_sb;
+ c_ino = dir->i_ino;
+
result = smb_proc_readdir(dentry, f_pos,
SMB_READDIR_CACHE_SIZE, c_entry);
#ifdef SMBFS_DEBUG_VERBOSE
@@ -169,16 +183,28 @@
* N.B. Might not need to for 0 return ... save cache?
*/
c_sb = NULL;
+#if 0
c_ino = 0;
c_seen_eof = 0;
+#endif
goto out;
}

- /* Suppose there are a multiple of cache entries? */
+ /*
+ * Check whether we were invalidated while filling the entries.
+ */
+ if (!this_dir_cached(dir))
+ {
+#ifdef SMBFS_PARANOIA
+printk("smb_refill_dir_cache: dir=%s/%s invalidated, retrying\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+#endif
+ goto retry;
+ }
+
c_seen_eof = (result < SMB_READDIR_CACHE_SIZE);
- c_sb = dir->i_sb;
- c_ino = dir->i_ino;
c_size = result;
+ c_mtime = dir->i_mtime;
c_last_returned_index = 0; /* is this used? */

ino_start = smb_invent_inos(c_size);
@@ -220,10 +246,6 @@
pr_debug("smb_readdir: dir->i_ino = %ld, c_ino = %ld\n",
dir->i_ino, c_ino);

- result = -EBADF;
- if ((dir == NULL) || !S_ISDIR(dir->i_mode))
- goto out;
-
/*
* Check whether the directory cache exists yet
*/
@@ -247,27 +269,32 @@
}
}

- result = 0;
+ /*
+ * Make sure our inode is up-to-date.
+ */
+ result = smb_revalidate_inode(dir);
+ if (result)
+ goto out;
+
+ /*
+ * Since filldir() could block if dirent is paged out,
+ * we hold the refill semaphore while using the cache.
+ */
+ down(&refill_cache_sem);
+
switch ((unsigned int) filp->f_pos)
{
case 0:
if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 1;
case 1:
if (filldir(dirent, "..", 2, 1,
dentry->d_parent->d_inode->i_ino) < 0)
- goto out;
+ goto up_and_out;
filp->f_pos = 2;
}

- /*
- * Since filldir() could block if dirent is paged out,
- * we hold the refill semaphore while using the cache.
- * N.B. It's possible that the directory could change
- * between calls to readdir ... what to do??
- */
- down(&refill_cache_sem);
entry = smb_search_in_cache(dir, filp->f_pos);
if (entry == NULL)
{
@@ -287,16 +314,20 @@
entry = c_entry;
}

- while (entry < &(c_entry[c_size]))
+ /*
+ * It's possible that someone might invalidate our cache,
+ * but it can't be changed while we hold the semaphore.
+ */
+ while (this_dir_cached(dir) && entry < &(c_entry[c_size]))
{
pr_debug("smb_readdir: entry->name = %s\n", entry->name);

if (filldir(dirent, entry->name, entry->len,
entry->f_pos, entry->attr.f_ino) < 0)
break;
-#if SMBFS_PARANOIA
+#ifdef SMBFS_PARANOIA
/* should never happen */
-if (!this_dir_cached(dir) || (entry->f_pos != filp->f_pos))
+if (entry->f_pos != filp->f_pos)
printk("smb_readdir: cache changed!\n");
#endif
filp->f_pos += 1;
@@ -313,10 +344,13 @@
void
smb_init_dir_cache(void)
{
+ refill_cache_sem = MUTEX;
c_entry = NULL;
c_sb = NULL;
+#if 0
c_ino = 0;
c_seen_eof = 0;
+#endif
}

void
@@ -325,8 +359,10 @@
if (this_dir_cached(dir))
{
c_sb = NULL;
+#if 0
c_ino = 0;
c_seen_eof = 0;
+#endif
}
}

@@ -353,7 +389,7 @@
goto out;

error = smb_proc_getattr(d_entry->d_parent, &(d_entry->d_name), &finfo);
-#if SMBFS_PARANOIA
+#ifdef SMBFS_PARANOIA
if (error && error != -ENOENT)
printk("smb_lookup: find %s/%s failed, error=%d\n",
d_entry->d_parent->d_name.name, d_entry->d_name.name, error);
@@ -390,8 +426,6 @@
struct smb_fattr fattr;
int error;

- smb_invalid_dir_cache(dir);
-
error = smb_proc_getattr(dentry->d_parent, &(dentry->d_name), &fattr);
if (!error)
{
@@ -424,6 +458,7 @@
* state. Currently we close it directly again, although this
* is not necessary anymore. */

+ smb_invalid_dir_cache(dir);
error = smb_proc_create(dentry->d_parent, &(dentry->d_name),
0, CURRENT_TIME);
if (!error)
@@ -444,6 +479,7 @@
if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;

+ smb_invalid_dir_cache(dir);
error = smb_proc_mkdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
@@ -459,7 +495,7 @@
int error;

error = -ENAMETOOLONG;
- if (dentry->d_name.len > NFS_MAXNAMLEN)
+ if (dentry->d_name.len > SMB_MAXNAMELEN)
goto out;

/*
@@ -468,8 +504,8 @@
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);

+ smb_invalid_dir_cache(dir);
error = smb_proc_rmdir(dentry->d_parent, &(dentry->d_name));
if (!error)
{
@@ -494,8 +530,8 @@
*/
if (dentry->d_inode)
smb_close(dentry->d_inode);
- smb_invalid_dir_cache(dir);

+ smb_invalid_dir_cache(dir);
error = smb_proc_unlink(dentry->d_parent, &(dentry->d_name));
if (!error)
{
@@ -511,19 +547,6 @@
{
int error;

- error = -ENOTDIR;
- if (!old_dir || !S_ISDIR(old_dir->i_mode))
- {
- printk("smb_rename: old inode is NULL or not a directory\n");
- goto out;
- }
-
- if (!new_dir || !S_ISDIR(new_dir->i_mode))
- {
- printk("smb_rename: new inode is NULL or not a directory\n");
- goto out;
- }
-
error = -ENAMETOOLONG;
if (old_dentry->d_name.len > SMB_MAXNAMELEN ||
new_dentry->d_name.len > SMB_MAXNAMELEN)
@@ -538,10 +561,8 @@
if (new_dentry->d_inode)
smb_close(new_dentry->d_inode);

- /* Assume success and invalidate now */
smb_invalid_dir_cache(old_dir);
smb_invalid_dir_cache(new_dir);
-
error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
new_dentry->d_parent, &(new_dentry->d_name));
/*
@@ -549,22 +570,24 @@
*/
if (error == -EEXIST)
{
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: existing file %s/%s, d_count=%d\n",
new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
new_dentry->d_count);
#endif
error = smb_proc_unlink(new_dentry->d_parent,
&(new_dentry->d_name));
-#ifdef SMBFS_PARANOIA
+#ifdef SMBFS_DEBUG_VERBOSE
printk("smb_rename: after unlink error=%d\n", error);
#endif
- if (error)
- goto out;
- d_delete(new_dentry);
-
- error = smb_proc_mv(old_dentry->d_parent, &(old_dentry->d_name),
- new_dentry->d_parent, &(new_dentry->d_name));
+ if (!error)
+ {
+ d_delete(new_dentry);
+ error = smb_proc_mv(old_dentry->d_parent,
+ &(old_dentry->d_name),
+ new_dentry->d_parent,
+ &(new_dentry->d_name));
+ }
}

/*
--- linux-2.1.56/fs/smbfs/file.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.56/fs/smbfs/file.c Wed Sep 17 13:53:31 1997
@@ -29,6 +29,13 @@
return a < b ? a : b;
}

+static inline void
+smb_unlock_page(struct page *page)
+{
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+}
+
static int
smb_fsync(struct file *file, struct dentry * dentry)
{
@@ -63,17 +70,16 @@
result = smb_open(dentry, O_RDONLY);
if (result < 0)
goto io_error;
- /* Should revalidate inode ... */

do {
if (count < rsize)
rsize = count;

+ result = smb_proc_read(inode, offset, rsize, buffer);
#ifdef SMBFS_DEBUG_VERBOSE
-printk("smb_readpage: reading %s/%s, offset=%ld, buffer=%p, size=%d\n",
-dentry->d_parent->d_name.name, dentry->d_name.name, offset, buffer, rsize);
+printk("smb_readpage: read %s/%s, offset=%ld, rsize=%d, result=%d\n",
+dentry->d_parent->d_name.name, dentry->d_name.name, offset, rsize, result);
#endif
- result = smb_proc_read(inode, offset, rsize, buffer);
if (result < 0)
goto io_error;

@@ -92,8 +98,7 @@
io_error:
if (refresh)
smb_refresh_inode(inode);
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+ smb_unlock_page(page);
return result;
}

@@ -159,9 +164,7 @@
io_error:
if (refresh)
smb_refresh_inode(inode);
-
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+ smb_unlock_page(page);
return written ? written : result;
}

@@ -216,8 +219,7 @@
printk("smb_updatepage: fault at page=%p buffer=%p\n", page, buffer);
result = -EFAULT;
clear_bit(PG_uptodate, &page->flags);
- clear_bit(PG_locked, &page->flags);
- wake_up(&page->wait);
+ smb_unlock_page(page);
goto out;
}

@@ -273,25 +275,24 @@
goto out;
}

- result = smb_revalidate_inode(inode);
- if (result < 0)
- goto out;
-
- result = smb_open(file->f_dentry, O_WRONLY);
- if (result < 0)
- goto out;
-
- result = -EINVAL;
- if (!S_ISREG(inode->i_mode)) {
+ if (!S_ISREG(inode->i_mode))
+ {
printk("smb_file_write: write to non-file, mode %07o\n",
inode->i_mode);
goto out;
}

- result = 0;
- if (count > 0)
+ result = smb_revalidate_inode(inode);
+ if (result)
+ goto out;
+
+ result = smb_open(file->f_dentry, O_WRONLY);
+ if (!result)
{
- result = generic_file_write(inode, file, buf, count);
+ if (count > 0)
+ {
+ result = generic_file_write(inode, file, buf, count);
+ }
}
out:
return result;
@@ -305,10 +306,14 @@
NULL, /* readdir - bad */
NULL, /* poll - default */
smb_ioctl, /* ioctl */
- smb_file_mmap, /* mmap */
+ smb_file_mmap, /* mmap(struct file*, struct vm_area_struct*)*/
NULL, /* open */
NULL, /* release */
- smb_fsync, /* fsync */
+ smb_fsync, /* fsync(struct file*, struct dentry*) */
+ NULL, /* fasync(struct file*, int) */
+ NULL, /* check_media_change(kdev_t dev) */
+ NULL, /* revalidate(kdev_t dev) */
+ NULL /* lock(struct file*, int, struct file_lock*) */
};

struct inode_operations smb_file_inode_operations =
--- linux-2.1.56/fs/smbfs/proc.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.56/fs/smbfs/proc.c Wed Sep 17 15:07:32 1997
@@ -33,7 +33,14 @@
/* #define SMBFS_DEBUG_VERBOSE 1 */
/* #define pr_debug printk */

+/*
+ * Get the server for the specified dentry
+ * N.B. Make this a #define in the smb header
+ */
+#define server_from_dentry(dentry) &dentry->d_sb->u.smbfs_sb
+
static int smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc);
+int smb_valid_socket(struct inode *);
void smb_close_socket(struct smb_sb_info *);

static inline int
@@ -84,15 +91,6 @@
return p + 4;
}

-/*
- * Return the server for the specified dentry
- * N.B. Make this a #define in the smb header
- */
-static struct smb_sb_info * server_from_dentry(struct dentry * dentry)
-{
- return &dentry->d_sb->u.smbfs_sb;
-}
-
static int smb_d_path(struct dentry * entry, char * buf)
{
if (IS_ROOT(entry)) {
@@ -117,9 +115,13 @@
char *start = buf;

if (dir != NULL)
+ {
buf += smb_d_path(dir, buf);
+ *buf = 0;
+ }

- if (name != NULL) {
+ if (name != NULL)
+ {
*buf++ = '\\';
memcpy(buf, name->name, name->len);
buf += name->len;
@@ -365,38 +367,6 @@
up(&(server->sem));
}

-/* smb_request_ok: We expect the server to be locked. Then we do the
- request and check the answer completely. When smb_request_ok
- returns 0, you can be quite sure that everything went well. When
- the answer is <=0, the returned number is a valid unix errno. */
-
-static int
-smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
-{
- int result = 0;
-
- s->rcls = 0;
- s->err = 0;
-
- if (smb_request(s) < 0)
- {
- pr_debug("smb_request failed\n");
- result = -EIO;
- } else if (smb_valid_packet(s->packet) != 0)
- {
- pr_debug("not a valid packet!\n");
- result = -EIO;
- } else if (s->rcls != 0)
- {
- result = -smb_errno(s->rcls, s->err);
- } else if (smb_verify(s->packet, command, wct, bcc) != 0)
- {
- pr_debug("smb_verify failed\n");
- result = -EIO;
- }
- return result;
-}
-
/*
* smb_retry: This function should be called when smb_request_ok has
indicated an error. If the error was indicated because the
@@ -409,6 +379,8 @@
static int
smb_retry(struct smb_sb_info *server)
{
+ struct wait_queue wait = { current, NULL };
+ unsigned long timeout;
int result = 0;

if (server->state != CONN_INVALID)
@@ -425,25 +397,80 @@

printk("smb_retry: signalling process %d\n", server->conn_pid);
kill_proc(server->conn_pid, SIGUSR1, 0);
+#if 0
server->conn_pid = 0;
+#endif

/*
- * Block here until we get a new connection.
- * N.B. This needs to be fixed ... we should wait in an
- * interruptible sleep for CONN_VALID.
+ * Wait here for a new connection.
*/
printk("smb_retry: blocking for new connection\n");
- smb_lock_server(server);
+ timeout = jiffies + 10*HZ;
+ add_wait_queue(&server->wait, &wait);
+ while (1)
+ {
+ /* current->state = TASK_INTERRUPTIBLE;*/
+ current->timeout = HZ/5;
+ if (server->state != CONN_INVALID)
+ break;
+ if (jiffies > timeout)
+ {
+ printk("smb_retry: timed out, try again later\n");
+ break;
+ }
+ /* N.B. check signals */
+ schedule();
+ }
+ remove_wait_queue(&server->wait, &wait);
+ current->timeout = 0;
+ current->state = TASK_RUNNING;

if (server->state == CONN_VALID)
{
printk("smb_retry: new connection pid=%d\n", server->conn_pid);
result = 1;
}
+
out:
return result;
}

+/* smb_request_ok: We expect the server to be locked. Then we do the
+ request and check the answer completely. When smb_request_ok
+ returns 0, you can be quite sure that everything went well. When
+ the answer is <=0, the returned number is a valid unix errno. */
+
+static int
+smb_request_ok(struct smb_sb_info *s, int command, int wct, int bcc)
+{
+ int result = 0;
+
+ s->rcls = 0;
+ s->err = 0;
+
+ /* Make sure we have a connection */
+ if (s->state != CONN_VALID && !smb_retry(s))
+ {
+ result = -EIO;
+ } else if (smb_request(s) < 0)
+ {
+ pr_debug("smb_request failed\n");
+ result = -EIO;
+ } else if (smb_valid_packet(s->packet) != 0)
+ {
+ pr_debug("not a valid packet!\n");
+ result = -EIO;
+ } else if (s->rcls != 0)
+ {
+ result = -smb_errno(s->rcls, s->err);
+ } else if (smb_verify(s->packet, command, wct, bcc) != 0)
+ {
+ pr_debug("smb_verify failed\n");
+ result = -EIO;
+ }
+ return result;
+}
+
/*
* This is called with the server locked after a successful smb_newconn().
* It installs the new connection pid, sets server->state to CONN_VALID,
@@ -458,21 +485,22 @@
error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_offerconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }

server->conn_pid = current->pid;
server->state = CONN_VALID;
+ wake_up_interruptible(&server->wait);
printk("smb_offerconn: state valid, pid=%d\n", server->conn_pid);
error = 0;

- /*
- * The initial call may be made without the server locked?
- */
out:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_offerconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
return error;
}

@@ -488,15 +516,21 @@

error = -EBADF;
if (opt->fd >= NR_OPEN || !(filp = current->files->fd[opt->fd]))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
- if (!S_ISSOCK(filp->f_dentry->d_inode->i_mode))
- goto out_unlock;
+ goto out;
+ if (!smb_valid_socket(filp->f_dentry->d_inode))
+ goto out;

error = -EACCES;
if (!suser() && (current->uid != server->m.mounted_uid))
- goto out_unlock;
+ goto out;
+ if (atomic_read(&server->sem.count) == 1)
+ {
+ printk("smb_newconn: server not locked, count=%d\n",
+ atomic_read(&server->sem.count));
+#if 0
+ goto out;
+#endif
+ }

/*
* Make sure the old socket is closed
@@ -513,17 +547,6 @@

out:
return error;
-
- /*
- * Unlock now if an error occurred.
- */
-out_unlock:
- if (atomic_read(&server->sem.count) != 1)
- smb_unlock_server(server);
- else
- printk("smb_newconn: server not locked, count=%d\n",
- atomic_read(&server->sem.count));
- goto out;
}

/* smb_setup_header: We completely set up the packet. You only have to
@@ -577,46 +600,43 @@
}

/*
- * We're called with the server locked, and we leave it that way. We
- * try maximum permissions.
+ * We're called with the server locked, and we leave it that way.
+ * Set the permissions to be consistent with the desired access.
*/

static int
-smb_proc_open(struct smb_sb_info *server, struct dentry *dir)
+smb_proc_open(struct smb_sb_info *server, struct dentry *dir, int wish)
{
struct inode *ino = dir->d_inode;
+ int mode, read_write = 0x42, read_only = 0x40;
int error;
char *p;

+ mode = read_write;
+#if 0
+ if (!(wish & (O_WRONLY | O_RDWR)))
+ mode = read_only;
+#endif
+
retry:
p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x42); /* read/write */
+ WSET(server->packet, smb_vwv0, mode);
WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
*p++ = 4;
p = smb_encode_path(server, p, dir, NULL);
smb_setup_bcc(server, p);

- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ error = smb_request_ok(server, SMBopen, 7, 0);
+ if (error != 0)
{
if (smb_retry(server))
goto retry;

- if ((error != -EACCES) && (error != -ETXTBSY)
- && (error != -EROFS))
- goto out;
-
- p = smb_setup_header(server, SMBopen, 2, 0);
- WSET(server->packet, smb_vwv0, 0x40); /* read only */
- WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
- *p++ = 4;
- p = smb_encode_path(server, p, dir, NULL);
- smb_setup_bcc(server, p);
-
- if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0)
+ if (mode == read_write &&
+ (error == -EACCES || error == -ETXTBSY || error == -EROFS))
{
- if (smb_retry(server))
- goto retry;
- goto out;
+ mode = read_only;
+ goto retry;
}
}
/* We should now have data in vwv[0..6]. */
@@ -629,7 +649,6 @@
ino->u.smbfs_i.open = server->generation;

pr_debug("smb_proc_open: entry->access = %d\n", ino->u.smbfs_i.access);
-out:
return error;
}

@@ -654,7 +673,7 @@
{
struct smb_sb_info *server = SMB_SERVER(i);
smb_lock_server(server);
- result = smb_proc_open(server, dir);
+ result = smb_proc_open(server, dir, wish);
smb_unlock_server(server);
if (result)
{
@@ -739,7 +758,7 @@
result = smb_request_ok(server, SMBread, 5, -1);
if (result < 0)
goto out;
-#if 0
+#if SMBFS_DEBUG_VERBOSE
printk("smb_proc_read: file %s/%s, result=%d, packet=%p\n",
dentry->d_parent->d_name.name, dentry->d_name.name, result, server->packet);
#endif
@@ -1003,6 +1022,7 @@
smb_init_dirent(server, fattr);
fattr->attr = aDIR;
fattr->f_ino = 1;
+ fattr->f_mtime = CURRENT_TIME;
smb_finish_dirent(server, fattr);
}

--- linux-2.1.56/fs/smbfs/sock.c.old Sat Sep 20 08:16:15 1997
+++ linux-2.1.56/fs/smbfs/sock.c Mon Sep 15 13:19:22 1997
@@ -126,18 +126,26 @@
}
}

+int
+smb_valid_socket(struct inode * inode)
+{
+ return (inode && S_ISSOCK(inode->i_mode) &&
+ inode->u.socket_i.type == SOCK_STREAM);
+}
+
static struct socket *
server_sock(struct smb_sb_info *server)
{
struct file *file;
- struct inode *inode;

- if (server &&
- (file = server->sock_file) &&
- (inode = file->f_dentry->d_inode) &&
- S_ISSOCK(inode->i_mode) &&
- inode->u.socket_i.type == SOCK_STREAM)
- return &(inode->u.socket_i);
+ if (server && (file = server->sock_file))
+ {
+#ifdef SMBFS_PARANOIA
+ if (!smb_valid_socket(file->f_dentry->d_inode))
+ printk("smb_server_sock: bad socket!\n");
+#endif
+ return &file->f_dentry->d_inode->u.socket_i;
+ }
return NULL;
}

--- linux-2.1.56/fs/dcache.c.old Sat Sep 20 08:16:14 1997
+++ linux-2.1.56/fs/dcache.c Sat Sep 20 08:37:47 1997
@@ -94,6 +94,36 @@
}
}

+extern void unhash_dentries(struct inode *); /* move to dcache.h */
+/*
+ * Unhash all dentries for the specified inode. This is called when
+ * an inode has become invalid and no further use is possible.
+ *
+ * We have to search the entire hash table, but fortunately this will
+ * be used only rarely.
+ */
+void unhash_dentries(struct inode * bad_inode)
+{
+ struct list_head *head = dentry_hashtable;
+ struct list_head *tmp;
+ int i = D_HASHSIZE;
+
+ while (i--) {
+ tmp = head->next;
+ while (tmp != head) {
+ struct dentry * dentry;
+ dentry = list_entry(tmp, struct dentry, d_hash);
+ tmp = tmp->next;
+ if (dentry->d_inode == bad_inode) {
+printk("unhash_dentries: unhashing %s/%s\n",
+dentry->d_parent->d_name.name, dentry->d_name.name);
+ d_drop(dentry);
+ }
+ }
+ head++;
+ }
+}
+
/*
* Try to invalidate the dentry if it turns out to be
* possible. If there are other users of the dentry we
@@ -119,13 +149,13 @@
* something (at which point we need to unuse
* all dentries).
*/
-void prune_dcache(int count)
+static void __prune_dcache(struct list_head *head, unsigned int count)
{
for (;;) {
struct dentry *dentry;
- struct list_head *tmp = dentry_unused.prev;
+ struct list_head *tmp = head->prev;

- if (tmp == &dentry_unused)
+ if (tmp == head)
break;
list_del(tmp);
INIT_LIST_HEAD(tmp);
@@ -146,6 +176,53 @@
if (!--count)
break;
}
+ }
+}
+
+void prune_dcache(int count)
+{
+ __prune_dcache(&dentry_unused, count);
+}
+
+/* N.B. move to include/linux/dcache.h */
+extern void shrink_dcache_sb(struct super_block *);
+/*
+ * Shrink the dcache for the specified super block.
+ * This allows us to unmount a device without disturbing
+ * the dcache for the other devices.
+ *
+ * We move the selected dentries to a temporary list,
+ * and then shrink that in the usual manner.
+ */
+void shrink_dcache_sb(struct super_block * sb)
+{
+ struct list_head *tmp, *prev;
+ struct dentry *dentry;
+ LIST_HEAD(temp);
+
+repeat:
+ prev = dentry_unused.prev;
+ while (prev != &dentry_unused) {
+ tmp = prev;
+ prev = tmp->prev;
+ dentry = list_entry(tmp, struct dentry, d_lru);
+ /*
+ * If the dentry is in use, we can always remove it.
+ * This makes less work for the next pass ...
+ */
+ if (dentry->d_count) {
+ list_del(tmp);
+ INIT_LIST_HEAD(tmp);
+ continue;
+ }
+ if (dentry->d_sb == sb) {
+ list_del(tmp);
+ list_add(tmp, &temp);
+ }
+ }
+ if (!list_empty(&temp)) {
+ __prune_dcache(&temp, 0);
+ goto repeat;
}
}

--------------14EDD65EA532BD37241DCB7C--