patch-2.2.18 linux/fs/smbfs/proc.c

Next file: linux/fs/smbfs/sock.c
Previous file: linux/fs/smbfs/inode.c
Back to the patch index
Back to the overall index

diff -u --new-file --recursive --exclude-from /usr/src/exclude v2.2.17/fs/smbfs/proc.c linux/fs/smbfs/proc.c
@@ -16,6 +16,7 @@
 #include <linux/fcntl.h>
 #include <linux/dcache.h>
 #include <linux/dirent.h>
+#include <linux/nls.h>
 
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
@@ -48,6 +49,19 @@
 		    struct smb_sb_info *server);
 
 
+static inline void
+smb_lock_server(struct smb_sb_info *server)
+{
+	down(&(server->sem));
+}
+
+static inline void
+smb_unlock_server(struct smb_sb_info *server)
+{
+	up(&(server->sem));
+}
+
+
 /* reverse a string inline. This is used by the dircache walking routines */
 static void reverse_string(char *buf, int len) {
 	char c;
@@ -60,6 +74,100 @@
 	}
 }
 
+/* no conversion, just a wrapper for memcpy. */
+static int convert_memcpy(char *output, int olen,
+			  const char *input, int ilen,
+			  struct nls_table *nls_from,
+			  struct nls_table *nls_to)
+{
+	memcpy(output, input, ilen);
+	return ilen;
+}
+
+/* convert from one "codepage" to another (possibly being utf8). */
+static int convert_cp(char *output, int olen,
+		      const char *input, int ilen,
+		      struct nls_table *nls_from,
+		      struct nls_table *nls_to)
+{
+	int len = 0;
+	int n;
+	unsigned char ch;
+	unsigned char cl;
+
+	/* we don't need this, but we are a bit paranoid ... */
+	if (!nls_from || !nls_to) {
+		PARANOIA("nls_from=%p, nls_to=%p\n", nls_from, nls_to);
+		return convert_memcpy(output, olen, input, ilen, NULL, NULL);
+	}
+
+	while (ilen > 0) {
+		/* convert by changing to unicode and back to the new cp */
+		n = -1;
+		nls_from->char2uni((unsigned char *)input, &n, &cl, &ch);
+		if (n < 0)
+			goto out;
+		input += n;
+		ilen -= n;
+
+		n = -1;
+		nls_to->uni2char(ch, cl, output, olen, &n);
+		if (n < 0)
+			goto out;
+		output += n;
+		olen -= n;
+
+		len += n;
+	}
+out:
+	return len;
+}
+
+static int setcodepage(struct smb_sb_info *server,
+		       struct nls_table **p, char *name)
+{
+	struct nls_table *nls;
+
+	if (!name || !*name) {
+		nls = NULL;
+	} else if ( (nls = load_nls(name)) == NULL) {
+		printk (KERN_ERR "smbfs: failed to load nls '%s'\n", name);
+		return -EINVAL;
+	}
+
+	/* if already set, unload the previous one. */
+	if (*p)
+		unload_nls(*p);
+	*p = nls;
+
+	return 0;
+}
+
+/* Handles all changes to codepage settings. */
+int smb_setcodepage(struct smb_sb_info *server, struct smb_nls_codepage *cp)
+{
+	int n;
+
+	smb_lock_server(server);
+
+	n = setcodepage(server, &server->local_nls, cp->local_name);
+	if (n != 0)
+		goto out;
+	n = setcodepage(server, &server->remote_nls, cp->remote_name);
+	if (n != 0)
+		setcodepage(server, &server->local_nls, NULL);
+
+out:
+	if (server->local_nls != NULL && server->remote_nls != NULL)
+		server->convert = convert_cp;
+	else
+		server->convert = convert_memcpy;
+
+	smb_unlock_server(server);
+	return n;
+}
+
+
 /*****************************************************************************/
 /*                                                                           */
 /*  Encoding/Decoding section                                                */
@@ -81,12 +189,14 @@
 }
 
 /*
- * smb_build_path: build the path to entry and name storing it in buf.
+ * smb_encode_path: build the path to entry and name storing it in buf.
  * The path returned will have the trailing '\0'.
  */
-static int smb_build_path(struct dentry * entry, struct qstr * name, char * buf)
+static int smb_encode_path(struct smb_sb_info *server, char * buf,
+			   struct dentry * entry, struct qstr * name)
 {
 	char *path = buf;
+	int len;
 
 	if (entry == NULL)
 		goto test_name_and_out;
@@ -106,9 +216,16 @@
 	 * and store it in reversed order [see reverse_string()]
 	 */
 	for (;;) {
-		memcpy(path, entry->d_name.name, entry->d_name.len);
-		reverse_string(path, entry->d_name.len);
-		path += entry->d_name.len;
+		if (entry->d_name.len > SMB_MAXNAMELEN)
+			return -ENAMETOOLONG;
+		if (path - buf + entry->d_name.len > SMB_MAXPATHLEN)
+			return -ENAMETOOLONG;
+
+		len = server->convert(path, SMB_MAXNAMELEN, 
+				      entry->d_name.name, entry->d_name.len,
+				      server->local_nls, server->remote_nls);
+		reverse_string(path, len);
+		path += len;
 
 		*(path++) = '\\';
 
@@ -124,22 +241,16 @@
 	if (name != NULL) {
 		*(path++) = '\\';
 name_and_out:
-		memcpy(path, name->name, name->len);
-		path += name->len;
+		len = server->convert(path, SMB_MAXNAMELEN, 
+				      name->name, name->len,
+				      server->local_nls, server->remote_nls);
+		path += len;
 	}
 out:
 	*(path++) = '\0';
 	return (path-buf);
 }
 
-static char *smb_encode_path(struct smb_sb_info *server, char *buf,
-			     struct dentry *dir, struct qstr *name)
-{
-	buf += smb_build_path(dir, name, buf);
-
-	return buf;
-}
-
 /* The following are taken directly from msdos-fs */
 
 /* Linear day numbers of the respective 1sts in non-leap years. */
@@ -274,22 +385,16 @@
 }
 
 /*
- * Returns the maximum read or write size for the current packet size
- * and max_xmit value.
+ * Returns the maximum read or write size for the "payload". Making all of the
+ * packet fit within the negotiated max_xmit size.
+ *
  * N.B. Since this value is usually computed before locking the server,
  * the server's packet size must never be decreased!
  */
-static int
+static inline int
 smb_get_xmitsize(struct smb_sb_info *server, int overhead)
 {
-	int size = server->packet_size;
-
-	/*
-	 * Start with the smaller of packet size and max_xmit ...
-	 */
-	if (size > server->opt.max_xmit)
-		size = server->opt.max_xmit;
-	return size - overhead;
+	return server->opt.max_xmit - overhead;
 }
 
 /*
@@ -434,24 +539,12 @@
 	return EIO;
 }
 
-static inline void
-smb_lock_server(struct smb_sb_info *server)
-{
-	down(&(server->sem));
-}
-
-static inline void
-smb_unlock_server(struct smb_sb_info *server)
-{
-	up(&(server->sem));
-}
-
 /*
  * smb_retry: This function should be called when smb_request_ok has
-   indicated an error. If the error was indicated because the
-   connection was killed, we try to reconnect. If smb_retry returns 0,
-   the error was indicated for another reason, so a retry would not be
-   of any use.
+ * indicated an error. If the error was indicated because the
+ * connection was killed, we try to reconnect. If smb_retry returns 0,
+ * the error was indicated for another reason, so a retry would not be
+ * of any use.
  * N.B. The server must be locked for this call.
  */
 static int
@@ -465,8 +558,7 @@
 
 	smb_close_socket(server);
 
-	if (pid == 0)
-	{
+	if (pid == 0) {
 		printk(KERN_ERR "smb_retry: no connection process\n");
 		server->state = CONN_RETRIED;
 		goto out;
@@ -481,26 +573,35 @@
 	 * Note: use the "priv" flag, as a user process may need to reconnect.
 	 */
 	error = kill_proc(pid, SIGUSR1, 1);
-	if (error)
-	{
+	if (error) {
 		printk(KERN_ERR "smb_retry: signal failed, error=%d\n", error);
 		goto out_restore;
 	}
-	VERBOSE("signalled pid %d, waiting for new connection\n",
-		server->conn_pid);
+	VERBOSE("signalled pid %d, waiting for new connection\n", pid);
 
 	/*
 	 * Wait for the new connection.
 	 */
+#ifdef SMB_RETRY_INTR
 	interruptible_sleep_on_timeout(&server->wait,  5*HZ);
 	if (signal_pending(current))
 		printk(KERN_INFO "caught signal\n");
+#else
+	/*
+	 * We don't want to be interrupted. For example, what if 'current'
+	 * already has received a signal? sleep_on would terminate immediately
+	 * and smbmount would not be able to re-establish connection.
+	 *
+	 * smbmount should be able to reconnect later, but it can't because
+	 * it will get an -EIO on attempts to open the mountpoint!
+	 */
+	sleep_on_timeout(&server->wait, 5*HZ);
+#endif
 
 	/*
 	 * Check for a valid connection.
 	 */
-	if (server->state == CONN_VALID)
-	{
+	if (server->state == CONN_VALID) {
 		PARANOIA("sucessful, new pid=%d, generation=%d\n",
 			 server->conn_pid, server->generation);
 		result = 1;
@@ -599,8 +700,13 @@
 	    !capable(CAP_SYS_ADMIN))
 		goto out;
 
-	if (opt->protocol < SMB_PROTOCOL_NT1) {
-		printk(KERN_NOTICE "smbfs: protocols older than NT1 are not suppported\n");
+	/*
+	 * Support NT1 & LANMAN2, they are not that different and
+	 * some people are still using LANMAN2 (OS/2)
+	 */
+	if (opt->protocol < SMB_PROTOCOL_LANMAN2) {
+		printk(KERN_NOTICE "smbfs: protocols older than LANMAN2 are not"
+		       " suppported [%d]\n", opt->protocol);
 		goto out;
 	}
 
@@ -626,9 +732,10 @@
 
 	/* now that we have an established connection we can detect the server
 	   type and enable bug workarounds */
-	if ((server->opt.max_xmit < 0x1000) &&
+	if (server->opt.protocol == SMB_PROTOCOL_NT1 &&
+	    (server->opt.max_xmit < 0x1000) &&
 	    !(server->opt.capabilities & SMB_CAP_NT_SMBS)) {
-		server->mnt->version |= SMB_FIX_WIN95;
+		server->mnt->flags |= SMB_MOUNT_WIN95;
 #ifdef SMBFS_DEBUG_VERBOSE
 		printk(KERN_NOTICE "smb_newconn: detected WIN95 server\n");
 #endif
@@ -638,8 +745,29 @@
 		server->opt.protocol, server->opt.max_xmit, server->conn_pid,
 		server->opt.capabilities);
 
+	/* Make sure we can fit a message of the negotiated size in our
+	   packet buffer. */
+	if (server->opt.max_xmit > server->packet_size) {
+		int len = smb_round_length(server->opt.max_xmit);
+		char *buf = smb_vmalloc(len);
+		if (buf) {
+			server->packet = buf;
+			server->packet_size = len;
+		} else {
+			/* else continue with the too small buffer? */
+			PARANOIA("Failed to allocate new packet buffer: "
+				 "max_xmit=%d, packet_size=%d\n",
+				 server->opt.max_xmit, server->packet_size);
+			server->opt.max_xmit = server->packet_size;
+		}
+	}
+
 out:
+#ifdef SMB_RETRY_INTR
 	wake_up_interruptible(&server->wait);
+#else
+	wake_up(&server->wait);
+#endif
 	return error;
 
 out_putf:
@@ -706,7 +834,7 @@
 {
 	struct inode *ino = dentry->d_inode;
 	int mode, read_write = 0x42, read_only = 0x40;
-	int error;
+	int res;
 	char *p;
 
 	/*
@@ -716,29 +844,33 @@
 	if (!(ino->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
 		mode = read_only;
 #if 0
+	/* FIXME: why is this code not in? below we fix it so that a caller
+	   wanting RO doesn't get RW. smb_revalidate_inode does some 
+	   optimization based on access mode. tail -f needs it to be correct. */
 	if (!(wish & (O_WRONLY | O_RDWR)))
 		mode = read_only;
 #endif
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBopen, 2, 0);
 	WSET(server->packet, smb_vwv0, mode);
 	WSET(server->packet, smb_vwv1, aSYSTEM | aHIDDEN | aDIR);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	res = smb_encode_path(server, p, dentry, NULL);
+	if (res < 0)
+		goto out;
+	p += res;
 	smb_setup_bcc(server, p);
 
-	error = smb_request_ok(server, SMBopen, 7, 0);
-	if (error != 0)
-	{
+	res = smb_request_ok(server, SMBopen, 7, 0);
+	if (res != 0) {
 		if (smb_retry(server))
 			goto retry;
 
 		if (mode == read_write &&
-		    (error == -EACCES || error == -ETXTBSY || error == -EROFS))
-		{
+		    (res == -EACCES || res == -ETXTBSY || res == -EROFS)) {
 			VERBOSE("%s/%s R/W failed, error=%d, retrying R/O\n",
-				DENTRY_PATH(dentry), error);
+				DENTRY_PATH(dentry), res);
 			mode = read_only;
 			goto retry;
 		}
@@ -751,10 +883,12 @@
 	/* smb_vwv2 has mtime */
 	/* smb_vwv4 has size  */
 	ino->u.smbfs_i.access = (WVAL(server->packet, smb_vwv6) & SMB_ACCMASK);
+	if (!(wish & (O_WRONLY | O_RDWR)))
+		ino->u.smbfs_i.access = SMB_O_RDONLY;
 	ino->u.smbfs_i.open = server->generation;
 
 out:
-	return error;
+	return res;
 }
 
 /*
@@ -768,8 +902,7 @@
 	int result;
 
 	result = -ENOENT;
-	if (!inode)
-	{
+	if (!inode) {
 		printk(KERN_ERR "smb_open: no inode for dentry %s/%s\n",
 		       DENTRY_PATH(dentry));
 		goto out;
@@ -780,16 +913,14 @@
 	 * currently open, we can be sure that the file isn't about
 	 * to be closed. (See smb_close_dentry() below.)
 	 */
-	if (!smb_is_open(inode))
-	{
+	if (!smb_is_open(inode)) {
 		struct smb_sb_info *server = SMB_SERVER(inode);
 		smb_lock_server(server);
 		result = 0;
 		if (!smb_is_open(inode))
 			result = smb_proc_open(server, dentry, wish);
 		smb_unlock_server(server);
-		if (result)
-		{
+		if (result) {
 			PARANOIA("%s/%s open failed, result=%d\n",
 				 DENTRY_PATH(dentry), result);
 			goto out;
@@ -805,8 +936,7 @@
 	 */
 	result = 0;
 	if (inode->u.smbfs_i.access != wish && 
-	    inode->u.smbfs_i.access != SMB_O_RDWR)
-	{
+	    inode->u.smbfs_i.access != SMB_O_RDWR) {
 		PARANOIA("%s/%s access denied, access=%x, wish=%x\n",
 			 DENTRY_PATH(dentry), inode->u.smbfs_i.access, wish);
 		result = -EACCES;
@@ -871,6 +1001,8 @@
 		/*
 		 * Force a revalidation after closing 
 		 */
+		if (server->opt.protocol < SMB_PROTOCOL_NT1)
+			ino->u.smbfs_i.oldmtime = 0;
 		ino->u.smbfs_i.closed = jiffies;
 	}
 	return result;
@@ -1029,31 +1161,33 @@
 {
 	struct smb_sb_info *server = server_from_dentry(dentry);
 	char *p;
-	int error;
+	int result;
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBcreate, 3, 0);
 	WSET(server->packet, smb_vwv0, attr);
 	DSET(server->packet, smb_vwv1, utc2local(server, ctime));
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	error = smb_request_ok(server, SMBcreate, 1, 0);
-	if (error < 0)
-	{
+	result = smb_request_ok(server, SMBcreate, 1, 0);
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
 	}
 	*fileid = WVAL(server->packet, smb_vwv0);
-	error = 0;
+	result = 0;
 
 out:
 	smb_unlock_server(server);
-	return error;
+	return result;
 }
 
 int
@@ -1065,17 +1199,22 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBmv, 1, 0);
 	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN | aDIR);
 	*p++ = 4;
-	p = smb_encode_path(server, p, old_dentry, NULL);
+	result = smb_encode_path(server, p, old_dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	*p++ = 4;
-	p = smb_encode_path(server, p, new_dentry, NULL);
+	result = smb_encode_path(server, p, new_dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0)
-	{
+	if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1098,15 +1237,17 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, command, 0, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
 	result = smb_request_ok(server, command, 0, 0);
-	if (result < 0)
-	{
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1165,15 +1306,17 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBunlink, 1, 0);
 	WSET(server->packet, smb_vwv0, aSYSTEM | aHIDDEN);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
-	if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0)
-	{
+	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
@@ -1216,17 +1359,16 @@
 	smb_lock_server(server);
 
       retry:
-	p = smb_setup_header(server, SMBwrite, 5, 0);
+	p = smb_setup_header(server, SMBwrite, 5, 3);
 	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)
-	{
+	*p++ = 1;
+	WSET(p, 0, 0);
+
+	if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
@@ -1292,7 +1434,7 @@
  */
 static char *
 smb_decode_long_dirent(struct smb_sb_info *server, char *p,
-		       struct cache_dirent *entry)
+		       struct cache_dirent *entry, int level)
 {
 	char *result;
 	unsigned int len = 0;
@@ -1302,19 +1444,39 @@
 	 */
 	entry->ino = 0;
 
-	result = p + WVAL(p, 0);
-	len = DVAL(p, 60);
-	if (len > SMB_MAXNAMELEN)
-		len = SMB_MAXNAMELEN;
-	/* NT4 null terminates */
-	entry->name = p + 94;
-	if (len && entry->name[len-1] == '\0')
-		len--;
-	entry->len = len;
-
-	VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
-		p, entry->len, entry->len, entry->name);
+	switch (level) {
+	case 1:
+		len = *((unsigned char *) p + 22);
+		entry->name = p + 23;
+		result = p + 24 + len;
+
+		VERBOSE("info 1 at %p, len=%d, name=%.*s\n",
+			p, len, len, entry->name);
+		break;
+	case 260:
+		result = p + WVAL(p, 0);
+		len = DVAL(p, 60);
+		if (len > SMB_MAXNAMELEN)
+			len = SMB_MAXNAMELEN;
+		/* NT4 null terminates */
+		entry->name = p + 94;
+		if (len && entry->name[len-1] == '\0')
+			len--;
+
+		VERBOSE("info 260 at %p, len=%d, name=%.*s\n",
+			p, len, len, entry->name);
+		break;
+	default:
+		PARANOIA("Unknown info level %d\n", level);
+		result = p + WVAL(p, 0);
+		goto out;
+	}
 
+	entry->len = server->convert(server->name_buf, SMB_MAXNAMELEN,
+				     entry->name, len,
+				     server->remote_nls, server->local_nls);
+	entry->name = server->name_buf;
+out:
 	return result;
 }
 
@@ -1335,10 +1497,12 @@
 smb_proc_readdir_long(struct smb_sb_info *server, struct dentry *dir, int fpos,
 		      void *cachep)
 {
-	unsigned char *p;
+	unsigned char *p, *lastname;
 	char *mask, *param = server->temp_buf;
 	__u16 command;
 	int first, entries, entries_seen;
+
+	/* Both NT and OS/2 accept info level 1 (but see note below). */
 	int info_level = 260;
 	const int max_matches = 512;
 
@@ -1348,19 +1512,30 @@
 	int resp_param_len = 0;
 	int ff_searchcount = 0;
 	int ff_eos = 0;
+	int ff_lastname = 0;
 	int ff_dir_handle = 0;
 	int loop_count = 0;
 	int mask_len, i, result;
 	static struct qstr star = { "*", 1, 0 };
 
+	/*
+	 * use info level 1 for older servers that don't do 260
+	 */
+	if (server->opt.protocol < SMB_PROTOCOL_NT1)
+		info_level = 1;
+
 	smb_lock_server(server);
 
-      retry:
+retry:
 	/*
 	 * Encode the initial path
 	 */
 	mask = param + 12;
-	mask_len = smb_encode_path(server, mask, dir, &star) - mask;
+	mask_len = smb_encode_path(server, mask, dir, &star) - 1;
+	if (mask_len < 0) {
+		entries = mask_len;
+		goto unlock_return;
+	}
 	first = 1;
 
 	VERBOSE("starting fpos=%d, mask=%s\n", fpos, mask);
@@ -1386,15 +1561,10 @@
 			command = TRANSACT2_FINDFIRST;
 			WSET(param, 0, aSYSTEM | aHIDDEN | aDIR);
 			WSET(param, 2, max_matches);	/* max count */
-			WSET(param, 4, 
-			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
+			WSET(param, 4, SMB_CLOSE_IF_END); 
 			WSET(param, 6, info_level);
 			DSET(param, 8, 0);
 		} else {
-			/* we don't need the mask after the first bit */
-			mask_len = 0;
-			mask[0] = 0;
-
 			command = TRANSACT2_FINDNEXT;
 
 			VERBOSE("handle=0x%X, mask=%s\n",
@@ -1404,8 +1574,7 @@
 			WSET(param, 2, max_matches);	/* max count */
 			WSET(param, 4, info_level);
 			DSET(param, 6, 0);       	/* ff_resume_key */
-			WSET(param, 10,
-			     SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
+			WSET(param, 10, SMB_CONTINUE_BIT|SMB_CLOSE_IF_END); 
 		}
 
 		result = smb_trans2_request(server, command,
@@ -1444,14 +1613,52 @@
 			ff_dir_handle = WVAL(resp_param, 0);
 			ff_searchcount = WVAL(resp_param, 2);
 			ff_eos = WVAL(resp_param, 4);
+			ff_lastname = WVAL(resp_param, 8);
 		} else {
 			ff_searchcount = WVAL(resp_param, 0);
 			ff_eos = WVAL(resp_param, 2);
+			ff_lastname = WVAL(resp_param, 6);
 		}
 
 		if (ff_searchcount == 0)
 			break;
 
+		/*
+		 * We might need the lastname for continuations.
+		 *
+		 * Note that some servers (win95) point to the filename and
+		 * others (NT4, Samba using NT1) to the dir entry. We assume
+		 * here that those who do not point to a filename do not need
+		 * this info to continue the listing.
+		 */
+		mask_len = 0;
+		if (ff_lastname > 0 && ff_lastname < resp_data_len) {
+			lastname = resp_data + ff_lastname;
+			switch (info_level) {
+			case 260:
+				mask_len = resp_data_len - ff_lastname;
+				break;
+			case 1:
+				/* lastname points to a length byte */
+				mask_len = *lastname++;
+				if (ff_lastname + 1 + mask_len > resp_data_len)
+					mask_len = resp_data_len - ff_lastname - 1;
+				break;
+			}
+			/*
+			 * Update the mask string for the next message.
+			 */
+			if (mask_len < 0)
+				mask_len = 0;
+			if (mask_len > 255)
+				mask_len = 255;
+			if (mask_len)
+				strncpy(mask, lastname, mask_len);
+		}
+		mask[mask_len] = 0;
+		mask_len = strlen(mask);	/* find the actual string len */
+
+
 		/* Now we are ready to parse smb directory entries. */
 
 		/* point to the data bytes */
@@ -1470,7 +1677,7 @@
 				goto unlock_return;
 			}
 
-			p = smb_decode_long_dirent(server, p, entry);
+			p = smb_decode_long_dirent(server, p, entry,info_level);
 
 			/* ignore . and .. from the server */
 			if (entries_seen == 2 && entry->name[0] == '.') {
@@ -1517,10 +1724,13 @@
 	int result;
 	char *p;
 
-      retry:
+retry:
 	p = smb_setup_header(server, SMBgetatr, 0, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dir, NULL);
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	smb_setup_bcc(server, p);
 
 	if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) {
@@ -1571,31 +1781,32 @@
 	int resp_param_len = 0;
 	int result;
 
-      retry:
+retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
 	DSET(param, 2, 0);
-	p = smb_encode_path(server, param + 6, dir, NULL);
+	p = param + 6;
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 
 	result = smb_trans2_request(server, TRANSACT2_QPATHINFO,
 				    0, NULL, p - param, param,
 				    &resp_data_len, &resp_data,
 				    &resp_param_len, &resp_param);
-	if (result < 0)
-	{
+	if (result < 0) {
 		if (smb_retry(server))
 			goto retry;
 		goto out;
 	}
-	if (server->rcls != 0)
-	{
+	if (server->rcls != 0) {
 		VERBOSE("for %s: result=%d, rcls=%d, err=%d\n",
 			&param[6], result, server->rcls, server->err);
 		result = -smb_errno(server);
 		goto out;
 	}
 	result = -ENOENT;
-	if (resp_data_len < 22)
-	{
+	if (resp_data_len < 22) {
 		PARANOIA("not enough data for %s, len=%d\n",
 			 &param[6], resp_data_len);
 		goto out;
@@ -1605,7 +1816,7 @@
 	 * Kludge alert: Win 95 swaps the date and time field,
 	 * contrary to the CIFS docs and Win NT practice.
 	 */
-	if (server->mnt->version & SMB_FIX_WIN95) {
+	if (server->mnt->flags & SMB_MOUNT_WIN95) {
 		off_date = 2;
 		off_time = 0;
 	}
@@ -1649,7 +1860,7 @@
 	 * Select whether to use core or trans2 getattr.
 	 * Win 95 appears to break with the trans2 getattr.
 	 */
-	if (server->mnt->version & SMB_FIX_WIN95) {
+	if (server->mnt->flags & SMB_MOUNT_WIN95) {
 		result = smb_proc_getattr_core(server, dir, fattr);
 	} else {
 		result = smb_proc_getattr_trans2(server, dir, fattr);
@@ -1702,7 +1913,10 @@
 	WSET(server->packet, smb_vwv6, 0);
 	WSET(server->packet, smb_vwv7, 0);
 	*p++ = 4;
-	p = smb_encode_path(server, p, dentry, NULL);
+	result = smb_encode_path(server, p, dentry, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 	*p++ = 4;
 	*p++ = 0;
 	smb_setup_bcc(server, p);
@@ -1803,7 +2017,11 @@
       retry:
 	WSET(param, 0, 1);	/* Info level SMB_INFO_STANDARD */
 	DSET(param, 2, 0);
-	p = smb_encode_path(server, param + 6, dir, NULL);
+	p = param + 6;
+	result = smb_encode_path(server, p, dir, NULL);
+	if (result < 0)
+		goto out;
+	p += result;
 
 	WSET(data, 0, 0); /* creation time */
 	WSET(data, 2, 0);
@@ -1867,7 +2085,7 @@
 
 	smb_lock_server(server);
 	/* setting the time on a Win95 server fails (tridge) */
-	if (!(server->mnt->version & SMB_FIX_WIN95)) {
+	if (!(server->mnt->flags & SMB_MOUNT_WIN95)) {
 		if (smb_is_open(inode) &&
 		    inode->u.smbfs_i.access != SMB_O_RDONLY)
 			result = smb_proc_setattr_ext(server, inode, fattr);
@@ -1907,7 +2125,7 @@
 
 	smb_lock_server(server);
 
-      retry:
+retry:
 	smb_setup_header(server, SMBdskattr, 0, 0);
 
 	if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)