diff -urN --exclude-from=exclude linux-2.4.0-test4-smbfsdebug/fs/smbfs/dir.c linux-2.4.0-test4/fs/smbfs/dir.c
--- linux-2.4.0-test4-smbfsdebug/fs/smbfs/dir.c	Sat Jul 15 15:53:16 2000
+++ linux-2.4.0-test4/fs/smbfs/dir.c	Sat Jul 15 15:22:23 2000
@@ -62,6 +62,20 @@
 	VERBOSE("reading %s/%s, f_pos=%d\n",
 		DENTRY_PATH(dentry),  (int) filp->f_pos);
 
+	result = 0;
+	switch ((unsigned int) filp->f_pos)
+	{
+	case 0:
+		if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
+			goto out;
+		filp->f_pos = 1;
+	case 1:
+		if (filldir(dirent, "..", 2, 1,
+				dentry->d_parent->d_inode->i_ino) < 0)
+			goto out;
+		filp->f_pos = 2;
+	}
+
 	/*
 	 * Make sure our inode is up-to-date.
 	 */
@@ -75,10 +89,16 @@
 	cachep = smb_get_dircache(dentry);
 	if (!cachep)
 		goto out;
+
 	/*
 	 * Make sure the cache is up-to-date.
+	 *
+	 * To detect changes on the server we refill on each "new" access.
+	 *
+	 * Directory mtime would be nice to use for finding changes,
+	 * unfortunately some servers (NT4) doesn't update on local changes.
 	 */
-	if (!cachep->valid)
+	if (!cachep->valid || filp->f_pos == 2)
 	{
 		result = smb_refill_dircache(cachep, dentry);
 		if (result)
@@ -86,18 +106,6 @@
 	}
 
 	result = 0;
-	switch ((unsigned int) filp->f_pos)
-	{
-	case 0:
-		if (filldir(dirent, ".", 1, 0, dir->i_ino) < 0)
-			goto out_free;
-		filp->f_pos = 1;
-	case 1:
-		if (filldir(dirent, "..", 2, 1,
-				dentry->d_parent->d_inode->i_ino) < 0)
-			goto out_free;
-		filp->f_pos = 2;
-	}
 
 	while (1)
 	{
@@ -220,12 +228,6 @@
 	return valid;
 }
 
-/*
- * XXX: It would be better to use the tolower from linux/ctype.h,
- * but _ctype is needed and it is not exported.
- */
-#define tolower(c) (((c) >= 'A' && (c) <= 'Z') ? (c)-('A'-'a') : (c))
-
 static int 
 smb_hash_dentry(struct dentry *dir, struct qstr *this)
 {
@@ -428,7 +430,6 @@
 	if (!d_unhashed(dentry))
 		goto out;
 
-	smb_invalid_dir_cache(dir);
 	error = smb_proc_rmdir(dentry);
 
 out:
@@ -445,7 +446,6 @@
 	 */
 	smb_close(dentry->d_inode);
 
-	smb_invalid_dir_cache(dir);
 	error = smb_proc_unlink(dentry);
 	if (!error)
 		smb_renew_times(dentry);
diff -urN --exclude-from=exclude linux-2.4.0-test4-smbfsdebug/fs/smbfs/file.c linux-2.4.0-test4/fs/smbfs/file.c
--- linux-2.4.0-test4-smbfsdebug/fs/smbfs/file.c	Sat Jul 15 15:54:44 2000
+++ linux-2.4.0-test4/fs/smbfs/file.c	Sat Jul 15 15:22:29 2000
@@ -44,10 +44,6 @@
 	int count = PAGE_SIZE;
 	int result;
 
-	/* We can't replace this with ClearPageError. why? is it a problem? 
-	   fs/buffer.c:brw_page does the same. */
-	/* ClearPageError(page); */
-
 	VERBOSE("file %s/%s, count=%d@%ld, rsize=%d\n",
 		DENTRY_PATH(dentry), count, offset, rsize);
 
@@ -94,10 +90,6 @@
 	struct dentry  *dentry = file->f_dentry;
 
 	DEBUG1("readpage %08lx\n", page_address(page));
-#ifdef SMBFS_PARANOIA
-	if (!PageLocked(page))
-		printk("smb_readpage: page not already locked!\n");
-#endif
 
 	get_page(page);
 	error = smb_readpage_sync(dentry, page);
diff -urN --exclude-from=exclude linux-2.4.0-test4-smbfsdebug/fs/smbfs/proc.c linux-2.4.0-test4/fs/smbfs/proc.c
--- linux-2.4.0-test4-smbfsdebug/fs/smbfs/proc.c	Sat Jul 15 16:26:35 2000
+++ linux-2.4.0-test4/fs/smbfs/proc.c	Sat Jul 15 17:48:10 2000
@@ -44,6 +44,9 @@
 static int
 smb_proc_setattr_core(struct smb_sb_info *server, struct dentry *dentry,
                       __u16 attr);
+static int
+smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir,
+		    struct smb_fattr *fattr);
 
 static void
 str_upper(char *name, int len)
@@ -1051,7 +1054,7 @@
 
       retry:
 	p = smb_setup_header(server, SMBmv, 1, 0);
-	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
+	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
 	*p++ = 4;
 	p = smb_encode_path(server, p, old_dentry, NULL);
 	*p++ = 4;
@@ -1113,10 +1116,37 @@
 	return smb_proc_generic_command(dentry, SMBrmdir);
 }
 
+#if SMBFS_POSIX_UNLINK
+/*
+ * Removes readonly attribute from a file. Used by unlink to give posix
+ * semantics.
+ * Note: called with the server locked.
+ */
+static int
+smb_set_rw(struct dentry *dentry,struct smb_sb_info *server)
+{
+	int result;
+	struct smb_fattr fattr;
+
+	/* first get current attribute */
+	result = smb_proc_do_getattr(server, dentry, &fattr);
+	if (result < 0)
+		return result;
+
+	/* if RONLY attribute is set, remove it */
+	if (fattr.attr & aRONLY) {  /* read only attribute is set */
+		fattr.attr &= ~aRONLY;
+		result = smb_proc_setattr_core(server, dentry, fattr.attr);
+	}
+	return result;
+}
+#endif
+
 int
 smb_proc_unlink(struct dentry *dentry)
 {
 	struct smb_sb_info *server = server_from_dentry(dentry);
+	int flag = 0;
 	char *p;
 	int result;
 
@@ -1131,6 +1161,28 @@
 
 	if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
 	{
+#if SMBFS_POSIX_UNLINK
+		if (result == -EACCES && !flag) {
+			/* Posix semantics is for the read-only state
+			   of a file to be ignored in unlink(). In the
+			   SMB world a unlink() is refused on a
+			   read-only file. To make things easier for
+			   unix users we try to override the files
+			   permission if the unlink fails with the
+			   right error.
+			   This introduces a race condition that could
+			   lead to a file being written by someone who
+			   shouldn't have access, but as far as I can
+			   tell that is unavoidable */
+
+			/* remove RONLY attribute and try again */
+			result = smb_set_rw(dentry,server);
+			if (result == 0) {
+				flag = 1;
+				goto retry;
+			}
+		}
+#endif
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1375,12 +1427,11 @@
 
 /*
  * Interpret a long filename structure using the specified info level:
- *   level 1   -- Win NT, Win 95, OS/2
- *   level 259 -- File name and length only, Win NT, Win 95
+ *   level 1 for anything below NT1 protocol
+ *   level 260 for NT1 protocol
  *
  * 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!
+ * any needed upper/lower casing in place.
  *
  * Bugs Noted:
  * (1) Win NT 4.0 appends a null byte to names and counts it in the length!
@@ -1399,26 +1450,20 @@
 
 	switch (level) {
 	case 1:
-		len = *((unsigned char *) p + 26);
+		len = *((unsigned char *) p + 22);
 		entry->len = len;
-		entry->name = p + 27;
-		result = p + 28 + len;
+		entry->name = p + 23;
+		result = p + 24 + len;
 
 		VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
 			p, entry->len, entry->len, entry->name);
 		break;
-	case 259: /* SMB_FIND_FILE_NAMES_INFO = 0x103 */
-		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;
-		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 ...
-		 */
+	case 260:
+		result = p + WVAL(p, 0);
+		len = DVAL(p, 60);
+		if (len > 255) len = 255;
+		/* NT4 null terminates */
+		entry->name = p + 94;
 		if (len && entry->name[len-1] == '\0')
 			len--;
 		entry->len = len;
@@ -1445,7 +1490,17 @@
 	return result;
 }
 
+/* findfirst/findnext flags */
+#define SMB_CLOSE_AFTER_FIRST (1<<0)
+#define SMB_CLOSE_IF_END (1<<1)
+#define SMB_REQUIRE_RESUME_KEY (1<<2)
+#define SMB_CONTINUE_BIT (1<<3)
+
 /*
+ * Note: samba-2.0.7 (at least) has a very similar routine, cli_list, in
+ * source/libsmb/clilist.c. When looking for smb bugs in the readdir code,
+ * go there for advise.
+ *
  * Bugs Noted:
  * (1) When using Info Level 1 Win NT 4.0 truncates directory listings 
  * for certain patterns of names and/or lengths. The breakage pattern
@@ -1461,14 +1516,13 @@
 	int first, entries, entries_seen;
 
 	/* Both NT and OS/2 accept info level 1 (but see note below). */
-	int info_level = 1;
+	int info_level = 260;
 	const int max_matches = 512;
 
 	unsigned char *resp_data = NULL;
 	unsigned char *resp_param = NULL;
 	int resp_data_len = 0;
 	int resp_param_len = 0;
-	int ff_resume_key = 0; /* this isn't being used */
 	int ff_searchcount = 0;
 	int ff_eos = 0;
 	int ff_lastname = 0;
@@ -1478,14 +1532,10 @@
 	static struct qstr star = { "*", 1, 0 };
 
 	/*
-	 * 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.
+	 * use info level 1 for older servers that don't do 260
 	 */
-	if (server->opt.protocol >= SMB_PROTOCOL_NT1 &&
-	    !(server->mnt->version & SMB_FIX_WIN95))
-		info_level = 259;
+	if (server->opt.protocol < SMB_PROTOCOL_NT1)
+		info_level = 1;
 
 	smb_lock_server(server);
 
@@ -1508,7 +1558,7 @@
 
 	while (ff_eos == 0) {
 		loop_count += 1;
-		if (loop_count > 200) {
+		if (loop_count > 10) {
 			printk(KERN_WARNING "smb_proc_readdir_long: "
 			       "Looping in FIND_NEXT??\n");
 			entries = -EIO;
@@ -1519,9 +1569,7 @@
 			command = TRANSACT2_FINDFIRST;
 			WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
 			WSET(param, 2, max_matches);	/* max count */
-			WSET(param, 4, 8 + 4 + 2);      /* resume required +
-							   close on end +
-							   continue */
+			WSET(param, 4, SMB_CLOSE_IF_END);
 			WSET(param, 6, info_level);
 			DSET(param, 8, 0);
 		} else {
@@ -1533,18 +1581,8 @@
 			WSET(param, 0, ff_dir_handle);	/* search handle */
 			WSET(param, 2, max_matches);	/* max count */
 			WSET(param, 4, info_level);
-			DSET(param, 6, ff_resume_key);  /* ff_resume_key */
-			WSET(param, 10, 8 + 4 + 2);     /* resume required +
-							   close on end +
-							   continue */
-			if (server->mnt->version & SMB_FIX_WIN95)
-			{
-				/* Windows 95 is not able to deliver answers
-				 * to FIND_NEXT fast enough, so sleep 0.2 sec
-				 */
-				current->state = TASK_INTERRUPTIBLE;
-				schedule_timeout(HZ/5);
-			}
+			DSET(param, 6, 0);
+			WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END);
 		}
 
 		result = smb_trans2_request(server, command,
@@ -1562,6 +1600,14 @@
 			break;
 		}
 
+		if (server->rcls == ERRSRV && server->err == ERRerror) {
+			/* a damn Win95 bug - sometimes it clags if you 
+			   ask it too fast */
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(HZ/5);
+			continue;
+                }
+
 		if (server->rcls != 0) { 
 			PARANOIA("name=%s, entries=%d, rcls=%d, err=%d\n",
 				 mask, entries, server->rcls, server->err);
@@ -1589,7 +1635,7 @@
 		if (ff_lastname > 0) {
 			lastname = resp_data + ff_lastname;
 			switch (info_level) {
-			case 259:
+			case 260:
 				if (ff_lastname < resp_data_len)
 					mask_len = resp_data_len - ff_lastname;
 				break;
@@ -1607,7 +1653,6 @@
 				mask_len = 255;
 			if (mask_len)
 				strncpy(mask, lastname, mask_len);
-			ff_resume_key = 0;
 		}
 		mask[mask_len] = 0;
 		VERBOSE("new mask, len=%d@%d, mask=%s\n",
@@ -1640,6 +1685,7 @@
 		VERBOSE("received %d entries, eos=%d\n", ff_searchcount,ff_eos);
 
 		first = 0;
+		loop_count = 0;
 	}
 
 	smb_unlock_server(server);
@@ -1857,13 +1903,15 @@
 	return result;
 }
 
-int
-smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
+/*
+ * Note: called with the server locked
+ */
+static int
+smb_proc_do_getattr(struct smb_sb_info *server, struct dentry *dir,
+		    struct smb_fattr *fattr)
 {
-	struct smb_sb_info *server = server_from_dentry(dir);
 	int result;
 
-	smb_lock_server(server);
 	smb_init_dirent(server, fattr);
 
 	/*
@@ -1886,7 +1934,17 @@
 	}
 
 	smb_finish_dirent(server, fattr);
+	return result;
+}
 
+int
+smb_proc_getattr(struct dentry *dir, struct smb_fattr *fattr)
+{
+	struct smb_sb_info *server = server_from_dentry(dir);
+	int result;
+
+	smb_lock_server(server);
+	result = smb_proc_do_getattr(server, dir, fattr);
 	smb_unlock_server(server);
 	return result;
 }
