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

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

diff -u --recursive --new-file v1.3.6/linux/fs/smbfs/proc.c linux/fs/smbfs/proc.c
@@ -0,0 +1,1917 @@
+/*
+ *  proc.c
+ *
+ *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
+ *
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+
+#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/stat.h>
+#include <linux/fcntl.h>
+#include <asm/segment.h>
+#include <asm/string.h>
+
+#define ARCH i386
+#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_BCC(packet)  smb_bcc(packet)
+#define SMB_BUF(packet)  ((packet) + SMB_HEADER_LEN + SMB_WCT(packet) * 2 + 2)
+
+#define SMB_DIRINFO_SIZE 43
+#define SMB_STATUS_SIZE  21
+
+#define HI_WORD(l) ((word)(l >> 16))
+#define LO_WORD(l) ((word)(l % 0xFFFF))
+
+void smb_printerr(int class, int num);
+int smb_request(struct smb_server *);
+static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Encoding/Decoding section                                                */
+/*                                                                           */
+/*****************************************************************************/
+
+static byte *
+smb_encode_word(byte *p, word data)
+{
+#if (ARCH == i386)
+        *((word *)p) = data;
+#else
+	p[0] = data & 0x00ffU;
+	p[1] = (data & 0xff00U) >> 8;
+#error "Non-Intel"
+#endif
+	return &p[2];
+}
+
+static byte *
+smb_decode_word(byte *p, word *data)
+{
+#if (ARCH == i386)
+	*data = *(word *)p;
+#else
+	*data = (word) p[0] | p[1] << 8;
+#endif 
+	return &p[2];
+}
+
+static byte *
+smb_decode_dword(byte *p, dword *data)
+{
+#if (ARCH == i386)
+	*data = *((dword *)p);
+#else
+	*data = (dword)p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
+#endif
+	return &p[4];
+}
+
+static byte *
+smb_encode_smb_length(byte *p, dword len)
+{
+	p[0] = p[1] = 0;
+	p[2] = (len & 0xFF00) >> 8;
+	p[3] = (len & 0xFF);
+	if (len > 0xFFFF)
+		p[1] |= 0x01;
+	return &p[4];
+}
+
+static byte *
+smb_encode_dialect(byte *p, const byte *name, int len)
+{
+	*p ++ = 2;
+	strcpy(p, name);
+	return p + len + 1;
+}
+
+static byte *
+smb_encode_ascii(byte *p, const byte *name, int len)
+{
+	*p ++ = 4;
+	strcpy(p, name);
+	return p + len + 1;
+}
+
+static byte *
+smb_encode_vblock(byte *p, const byte *data, word len, int fs)
+{
+	*p ++ = 5;
+	p = smb_encode_word(p, len);
+	if (fs)
+		memcpy_fromfs(p, data, len);
+	else
+		memcpy(p, data, len);
+	return p + len;
+}
+
+static byte *
+smb_decode_data(byte *p, byte *data, word *data_len, int fs)
+{
+        word len;
+
+	if (!(*p == 1 || *p == 5)) {
+                printk("smb_decode_data: Warning! Data block not starting "
+                       "with 1 or 5\n");
+        }
+
+        len = WVAL(p, 1);
+        p += 3;
+
+        if (fs)
+                memcpy_tofs(data, p, len);
+        else
+                memcpy(data, p, len);
+
+        *data_len = len;
+
+        return p + len;
+}
+
+static byte *
+smb_name_mangle(byte *p, const byte *name)
+{
+	int len, pad = 0;
+
+	len = strlen(name);
+
+	if (len < 16)
+		pad = 16 - len;
+
+	*p ++ = 2 * (len + pad);
+
+	while (*name) {
+		*p ++ = (*name >> 4) + 'A';
+		*p ++ = (*name & 0x0F) + 'A';
+		name ++;
+	}
+	while (pad --) {
+		*p ++ = 'C';
+		*p ++ = 'A';
+	}
+	*p++ = '\0';
+	
+	return p;
+}
+
+/* The following are taken directly from msdos-fs */
+
+/* Linear day numbers of the respective 1sts in non-leap years. */
+
+static int day_n[] = { 0,31,59,90,120,151,181,212,243,273,304,334,0,0,0,0 };
+		  /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */
+
+
+extern struct timezone sys_tz;
+
+static int
+utc2local(int time)
+{
+        return time - sys_tz.tz_minuteswest*60;
+}
+
+static int
+local2utc(int time)
+{
+        return time + sys_tz.tz_minuteswest*60;
+}
+
+/* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */
+
+static int
+date_dos2unix(unsigned short time,unsigned short date)
+{
+	int month,year,secs;
+
+	month = ((date >> 5) & 15)-1;
+	year = date >> 9;
+	secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400*
+	    ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 &&
+	    month < 2 ? 1 : 0)+3653);
+			/* days since 1.1.70 plus 80's leap day */
+	return local2utc(secs);
+}
+
+
+/* Convert linear UNIX date to a MS-DOS time/date pair. */
+
+static void
+date_unix2dos(int unix_date,unsigned short *time, unsigned short *date)
+{
+	int day,year,nl_day,month;
+
+	unix_date = utc2local(unix_date);
+	*time = (unix_date % 60)/2+(((unix_date/60) % 60) << 5)+
+	    (((unix_date/3600) % 24) << 11);
+	day = unix_date/86400-3652;
+	year = day/365;
+	if ((year+3)/4+365*year > day) year--;
+	day -= (year+3)/4+365*year;
+	if (day == 59 && !(year & 3)) {
+		nl_day = day;
+		month = 2;
+	}
+	else {
+		nl_day = (year & 3) || day <= 59 ? day : day-1;
+		for (month = 0; month < 12; month++)
+			if (day_n[month] > nl_day) break;
+	}
+	*date = nl_day-day_n[month-1]+1+(month << 5)+(year << 9);
+}
+
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Support section.                                                         */
+/*                                                                           */
+/*****************************************************************************/
+
+dword
+smb_len(byte *packet) 
+{
+	return ((packet[1] & 0x1) << 16L) | (packet[2] << 8L) | (packet[3]);
+}
+
+static word
+smb_bcc(byte *packet)
+{
+	int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word);
+#if (ARCH == i386)
+	return *((word *)((byte *)packet + pos));
+#else
+	return packet[pos] | packet[pos+1] << 8;
+#endif
+}
+
+/* smb_valid_packet: We check if packet fulfills the basic
+   requirements of a smb packet */
+
+static int
+smb_valid_packet(byte *packet)
+{
+        DDPRINTK("len: %ld, wct: %d, bcc: %d\n",
+                 smb_len(packet), SMB_WCT(packet), SMB_BCC(packet));
+	return (   packet[4] == 0xff
+                && packet[5] == 'S'
+                && packet[6] == 'M'
+                && packet[7] == 'B'
+                && (smb_len(packet) + 4 == SMB_HEADER_LEN
+                    + SMB_WCT(packet) * 2 + SMB_BCC(packet)));
+}
+
+/* smb_verify: We check if we got the answer we expected, and if we
+   got enough data. If bcc == -1, we don't care. */
+
+static int
+smb_verify(byte *packet, int command, int wct, int bcc)
+{
+	return (SMB_CMD(packet) == command &&
+		SMB_WCT(packet) >= wct &&
+		(bcc == -1 || SMB_BCC(packet) == bcc)) ? 0 : -EIO;
+}
+
+static int
+smb_errno(int errcls, int error)
+{
+
+#if DEBUG_SMB > 1
+	if (errcls) {
+		printk("smb_errno: ");
+		smb_printerr(errcls, error);
+		printk("\n");
+	}
+#endif
+
+	if (errcls == ERRDOS) 
+		switch (error) {
+			case ERRbadfunc:    return EINVAL;
+			case ERRbadfile:    return ENOENT;
+			case ERRbadpath:    return ENOENT;
+			case ERRnofids:     return EMFILE;
+			case ERRnoaccess:   return EACCES;
+			case ERRbadfid:     return EBADF;
+			case ERRbadmcb:     return EREMOTEIO;
+			case ERRnomem:      return ENOMEM;
+			case ERRbadmem:     return EFAULT;
+			case ERRbadenv:     return EREMOTEIO;
+			case ERRbadformat:  return EREMOTEIO;
+			case ERRbadaccess:  return EACCES;
+			case ERRbaddata:    return E2BIG;
+			case ERRbaddrive:   return ENXIO;
+			case ERRremcd:      return EREMOTEIO;
+			case ERRdiffdevice: return EXDEV;
+			case ERRnofiles:    return 0;
+			case ERRbadshare:   return ETXTBSY;
+			case ERRlock:       return EDEADLOCK;
+			case ERRfilexists:  return EEXIST;
+			case 87:            return 0; /* Unknown error!! */
+			default:            return EIO;
+		}
+	else if (errcls == ERRSRV) 
+		switch (error) {
+			case ERRerror: return ENFILE;
+			case ERRbadpw: return EINVAL;
+			case ERRbadtype: return EIO;
+			case ERRaccess: return EACCES;
+			default: return EIO;
+		}
+	else if (errcls == ERRHRD) 
+		switch (error) {
+			case ERRnowrite: return EROFS; 
+			case ERRbadunit: return ENODEV;
+			case ERRnotready: return EUCLEAN;
+			case ERRbadcmd: return EIO;
+			case ERRdata: return EIO;
+			case ERRbadreq: return ERANGE;
+			case ERRbadshare: return ETXTBSY;
+			case ERRlock: return EDEADLOCK;
+			default: return EIO;
+		}
+	else if (errcls == ERRCMD) 
+	        return EIO;
+	return 0;
+}
+
+#if DEBUG_SMB > 0
+static char
+print_char(char c)
+{
+	if ((c < ' ') || (c > '~'))
+		return '.';
+	return c;
+}
+
+static void
+smb_dump_packet(byte *packet) {
+	int i, j, len;
+        int errcls, error;
+
+        errcls = (int)packet[9];
+        error  = (int)(int)(packet[11]|packet[12]<<8);
+
+	printk("smb_len = %d  valid = %d    \n", 
+	       len = smb_len(packet), smb_valid_packet(packet));
+	printk("smb_cmd = %d  smb_wct = %d  smb_bcc = %d\n", 
+	       packet[8], SMB_WCT(packet), SMB_BCC(packet)); 
+	printk("smb_rcls = %d smb_err = %d\n", errcls, error);
+
+	if (errcls) {
+		smb_printerr(errcls, error);
+		printk("\n");
+	}
+
+        if (len > 100)
+                len = 100;
+	
+	for (i = 0; i < len; i += 10) {
+		printk("%03d:", i);
+		for (j = i; j < i+10; j++)
+			if (j < len)
+				printk("%02x ", packet[j]);
+			else
+				printk("   ");
+		printk(": ");
+	        for (j = i; j < i+10; j++)
+			if (j < len)
+				printk("%c", print_char(packet[j]));
+		printk("\n");
+	}
+}
+#endif
+
+static void
+smb_lock_server(struct smb_server *server)
+{
+        while (server->lock)
+		sleep_on(&server->wait);
+	server->lock = 1;
+}
+
+static void
+smb_unlock_server(struct smb_server *server)
+{
+        if (server->lock != 1) {
+                printk("smb_unlock_server: was not locked!\n");
+        }
+
+        server->lock = 0;
+        wake_up(&server->wait);
+}
+        
+/* 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_server *s, int command, int wct, int bcc)
+{
+        int result = 0;
+        s->rcls = 0;
+        s->err  = 0;
+
+        if (smb_request(s) < 0) {
+                DPRINTK("smb_request failed\n");
+                result = -EIO;
+        }
+        else if (smb_valid_packet(s->packet) != 0) {
+                DPRINTK("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) {
+                DPRINTK("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
+   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. */
+
+static int
+smb_retry(struct smb_server *server)
+{
+        if (server->state != CONN_INVALID) {
+                return 0;
+        }
+        
+        if (smb_release(server) < 0) {
+                DPRINTK("smb_retry: smb_release failed\n");
+                server->state = CONN_RETRIED;
+                return 0;
+        }
+        if(smb_proc_reconnect(server) < 0) {
+                DPRINTK("smb_proc_reconnect failed\n");
+                server->state = CONN_RETRIED;
+                return 0;
+        }
+
+        server->state = CONN_VALID;
+        return 1;
+}
+
+static int
+smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc)
+{
+        int result = smb_request_ok(s, command, wct, bcc);
+
+        smb_unlock_server(s);
+
+        return result;
+}
+        
+/* smb_setup_header: We completely set up the packet. You only have to
+   insert the command-specific fields */
+
+static byte *
+smb_setup_header(struct smb_server *server, byte command, word wct, word bcc)
+{
+	dword xmit_len = SMB_HEADER_LEN + wct * sizeof(word) + bcc + 2;
+	byte *p = server->packet;
+        byte *buf = server->packet;
+
+	p = smb_encode_smb_length(p, xmit_len);
+
+        BSET(p,0,0xff);
+        BSET(p,1,'S');
+        BSET(p,2,'M');
+        BSET(p,3,'B');
+        BSET(p,4,command);
+
+        p += 5;
+	memset(p, '\0', 19);
+	p += 19;
+        p += 8;
+
+        WSET(buf, smb_tid, server->tid);
+        WSET(buf, smb_pid, server->pid);
+        WSET(buf, smb_uid, server->server_uid);
+        WSET(buf, smb_mid, server->mid);
+
+        if (server->protocol > PROTOCOL_CORE) {
+                BSET(buf, smb_flg, 0x8);
+                WSET(buf, smb_flg2, 0x3);
+        }
+        
+	*p++ = wct;		/* wct */
+        p += 2*wct;
+        WSET(p, 0, bcc);
+        return p+2;
+}
+
+/* smb_setup_header_exclusive waits on server->lock and locks the
+   server, when it's free. You have to unlock it manually when you're
+   finished with server->packet! */
+
+static byte *
+smb_setup_header_exclusive(struct smb_server *server,
+                           byte command, word wct, word bcc)
+{
+        smb_lock_server(server);
+        return smb_setup_header(server, command, wct, bcc);
+}
+
+
+/*****************************************************************************/
+/*                                                                           */
+/*  File operation section.                                                  */
+/*                                                                           */
+/*****************************************************************************/
+
+int
+smb_proc_open(struct smb_server *server, const char *pathname, int len,
+              struct smb_dirent *entry)
+{
+	int error;
+	char* p;
+        char* buf = server->packet;
+	const word o_attr = aSYSTEM | aHIDDEN | aDIR;
+
+        DPRINTK("smb_proc_open: path=%s\n", pathname);
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBopen, 2, 2 + len);
+        WSET(buf, smb_vwv0, 0x42); /* read/write */
+        WSET(buf, smb_vwv1, o_attr);
+        smb_encode_ascii(p, pathname, len);
+
+        if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) {
+
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                
+                if (error != -EACCES) {
+                        smb_unlock_server(server);
+                        return error;
+                }
+
+                p = smb_setup_header(server, SMBopen, 2, 2 + len);
+                WSET(buf, smb_vwv0, 0x40); /* read only */
+                WSET(buf, smb_vwv1, o_attr);
+                smb_encode_ascii(p, pathname, len);
+
+                if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) {
+                        if (smb_retry(server)) {
+                                goto retry;
+                        }
+                        smb_unlock_server(server);
+                        return error;
+                }
+        }
+
+	/* We should now have data in vwv[0..6]. */
+
+        entry->fileid = WVAL(buf, smb_vwv0);
+        entry->attr   = WVAL(buf, smb_vwv1);
+        entry->ctime = entry->atime =
+                entry->mtime = local2utc(DVAL(buf, smb_vwv2));
+        entry->size   = DVAL(buf, smb_vwv4);
+        entry->access = WVAL(buf, smb_vwv6);
+
+        smb_unlock_server(server);
+
+	entry->access &= 3;
+        DPRINTK("smb_proc_open: entry->access = %d\n", entry->access);
+	return 0;
+}
+
+/* smb_proc_close: in finfo->mtime we can send a modification time to
+   the server */
+int
+smb_proc_close(struct smb_server *server, struct smb_dirent *finfo)
+{
+        char *buf = server->packet;
+
+	smb_setup_header_exclusive(server, SMBclose, 3, 0);
+        WSET(buf, smb_vwv0, finfo->fileid);
+        DSET(buf, smb_vwv1, utc2local(finfo->mtime));
+
+        return smb_request_ok_unlock(server, SMBclose, 0, 0);
+}
+
+/* In smb_proc_read and smb_proc_write we do not retry, because the
+   file-id would not be valid after a reconnection. */
+
+/* smb_proc_read: fs indicates if it should be copied with
+   memcpy_tofs. */
+
+int
+smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, 
+              off_t offset, long count, char *data, int fs)
+{
+	word returned_count, data_len;
+        char *buf = server->packet;
+        int error;
+
+	smb_setup_header_exclusive(server, SMBread, 5, 0);
+
+        WSET(buf, smb_vwv0, finfo->fileid);
+        WSET(buf, smb_vwv1, count);
+        DSET(buf, smb_vwv2, offset);
+        WSET(buf, smb_vwv4, 0);
+	
+	if ((error = smb_request_ok(server, SMBread, 5, -1)) < 0) {
+                smb_unlock_server(server);
+		return error;
+        }
+
+	returned_count = WVAL(buf, smb_vwv0);
+	
+	smb_decode_data(SMB_BUF(server->packet), data, &data_len, fs);
+
+        smb_unlock_server(server);
+
+	if (returned_count != data_len) {
+		printk("smb_proc_read: Warning, returned_count != data_len\n");
+                printk("smb_proc_read: ret_c=%d, data_len=%d\n",
+                       returned_count, data_len);
+        }
+
+        return data_len;
+}
+
+int
+smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
+               off_t offset, int count, char *data)
+{
+        int res = 0;
+        char *buf = server->packet;
+        byte *p;
+
+	p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3);
+        WSET(buf, smb_vwv0, finfo->fileid);
+        WSET(buf, smb_vwv1, count);
+        DSET(buf, smb_vwv2, offset);
+        WSET(buf, smb_vwv4, 0);
+
+        *p++ = 1;
+        WSET(p, 0, count);
+        memcpy_fromfs(p+2, data, count);
+
+	if ((res = smb_request_ok(server, SMBwrite, 1, 0)) >= 0) {
+                res = WVAL(buf, smb_vwv0);
+        }
+
+        smb_unlock_server(server);
+
+	return res;
+}
+
+/* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */
+
+static int
+smb_proc_do_create(struct smb_server *server, const char *path, int len, 
+                   struct smb_dirent *entry, word command)
+{
+	int error;
+	char *p;
+        char *buf = server->packet;
+
+	smb_lock_server(server);
+ retry:
+	p = smb_setup_header(server, command, 3, len + 2);
+        WSET(buf, smb_vwv0, entry->attr);
+        DSET(buf, smb_vwv1, utc2local(entry->ctime));
+	smb_encode_ascii(p, path, len);
+
+	if ((error = smb_request_ok(server, command, 1, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return error;
+        }
+
+        entry->opened = 1;
+        entry->fileid = WVAL(buf, smb_vwv0);
+        smb_unlock_server(server);
+
+        smb_proc_close(server, entry);
+
+	return 0;
+}
+	
+int
+smb_proc_create(struct smb_server *server, const char *path, int len,
+                struct smb_dirent *entry)
+{
+	return smb_proc_do_create(server, path, len, entry, SMBcreate);
+}
+
+int
+smb_proc_mknew(struct smb_server *server, const char *path, int len,
+               struct smb_dirent *entry)
+{
+	return smb_proc_do_create(server, path, len, entry, SMBmknew);
+}
+
+int
+smb_proc_mv(struct smb_server *server, 
+            const char *opath, const int olen,
+            const char *npath, const int nlen)
+{
+	char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBmv, 1, olen + nlen + 4);
+        WSET(buf, smb_vwv0, 0);
+	p = smb_encode_ascii(p, opath, olen);
+	smb_encode_ascii(p, npath, olen);
+
+        if ((result = smb_request_ok(server, SMBmv, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_mkdir(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBmkdir, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBmkdir, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_rmdir(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBrmdir, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBrmdir, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_unlink(struct smb_server *server, const char *path, const int len)
+{
+	char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+	p = smb_setup_header(server, SMBunlink, 1, 2 + len);
+        WSET(buf, smb_vwv0, 0);
+	smb_encode_ascii(p, path, len);
+
+        if ((result = smb_request_ok(server, SMBunlink, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+int
+smb_proc_trunc(struct smb_server *server, word fid, dword length)
+{
+        char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBwrite, 5, 3);
+        WSET(buf, smb_vwv0, fid);
+        WSET(buf, smb_vwv1, 0);
+        DSET(buf, smb_vwv2, length);
+        WSET(buf, smb_vwv4, 0);
+	smb_encode_ascii(p, "", 0);
+	
+        if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+static char *
+smb_decode_dirent(char *p, struct smb_dirent *entry)
+{
+	p += SMB_STATUS_SIZE;                  /* reserved (search_status) */
+        entry->attr = BVAL(p, 0);
+        entry->mtime = entry->atime = entry->ctime =
+                date_dos2unix(WVAL(p, 1), WVAL(p, 3));
+        entry->size = DVAL(p, 5);
+        memcpy(entry->path, p+9, 13);
+	DDPRINTK("smb_decode_dirent: path = %s\n", entry->path);
+	return p + 22;
+}
+
+/* This routine is used to read in directory entries from the network.
+   Note that it is for short directory name seeks, i.e.: protocol <
+   PROTOCOL_LANMAN2 */
+
+static int
+smb_proc_readdir_short(struct smb_server *server, struct inode *dir, int fpos,
+		       int cache_size, struct smb_dirent *entry)
+{
+	char *p;
+        char *buf;
+	int error;
+        int result;
+	int i;
+	int first, total_count;
+        struct smb_dirent *current_entry;
+	word bcc;
+	word count;
+	char status[SMB_STATUS_SIZE];
+	int entries_asked = (server->max_xmit - 100) / SMB_DIRINFO_SIZE;
+	int dirlen = strlen(SMB_FINFO(dir)->path);
+	char mask[dirlen + 5];
+
+	strcpy(mask, SMB_FINFO(dir)->path);
+	strcat(mask, "\\*.*");
+
+ 	DPRINTK("SMB call  readdir %d @ %d\n", cache_size, fpos);        
+	DPRINTK("          mask = %s\n", mask);
+
+        buf = server->packet;
+
+        smb_lock_server(server);
+
+ retry:
+	first = 1;
+        total_count = 0;
+        current_entry = entry;
+	
+	while (1) {
+		if (first == 1) {
+			p = smb_setup_header(server, SMBsearch, 2,
+                                             5 + strlen(mask));
+                        WSET(buf, smb_vwv0, entries_asked);
+                        WSET(buf, smb_vwv1, aDIR);
+			p = smb_encode_ascii(p, mask, strlen(mask));
+			*p ++ = 5;
+		        p = smb_encode_word(p, 0);
+		} else {
+			p = smb_setup_header(server, SMBsearch, 2,
+                                             5 + SMB_STATUS_SIZE);
+                        WSET(buf, smb_vwv0, entries_asked);
+                        WSET(buf, smb_vwv1, aDIR);
+			p = smb_encode_ascii(p, "", 0);
+			p = smb_encode_vblock(p, status, SMB_STATUS_SIZE, 0);
+		}
+		
+		if ((error = smb_request_ok(server, SMBsearch, 1, -1)) < 0) {
+                        if (   (server->rcls == ERRDOS)
+                            && (server->err  == ERRnofiles)) {
+                                result = total_count - fpos;
+                                goto unlock_return;
+                        }
+                        else
+                        {
+                                if (smb_retry(server)) {
+                                        goto retry;
+                                }
+                                result = error;
+                                goto unlock_return;
+                        }
+                }
+
+		p = SMB_VWV(server->packet);
+		p = smb_decode_word(p, &count); /* vwv[0] = count-returned */
+		p = smb_decode_word(p, &bcc);           
+		
+		first = 0;
+		
+		if (count <= 0) {
+			result = total_count - fpos;
+                        goto unlock_return;
+                }
+		if (bcc != count * SMB_DIRINFO_SIZE + 3) {
+			result = -EIO;
+                        goto unlock_return;
+                }
+
+		p += 3; /* Skipping VBLOCK header (5, length lo, length hi). */
+
+		/* Read the last entry into the status field. */
+		memcpy(status,
+                       SMB_BUF(server->packet) + 3 +
+                       (count - 1) * SMB_DIRINFO_SIZE, 
+                       SMB_STATUS_SIZE);
+
+		/* Now we are ready to parse smb directory entries. */
+		
+		for (i = 0; i < count; i ++) {
+			if (total_count < fpos) {
+				p += SMB_DIRINFO_SIZE;
+				DDPRINTK("smb_proc_readdir: skipped entry.\n");
+				DDPRINTK("                  total_count = %d\n"
+                                         "                i = %d, fpos = %d\n",
+                                         total_count, i, fpos);
+                        }
+                        else if (total_count >= fpos + cache_size) {
+                                result = total_count - fpos;
+                                goto unlock_return;
+			}
+			else {
+				p = smb_decode_dirent(p, current_entry);
+				current_entry->f_pos = total_count;
+				DDPRINTK("smb_proc_readdir: entry->f_pos = "
+                                         "%lu\n", entry->f_pos);	
+				current_entry += 1;
+			}
+			total_count += 1;
+		}
+	}
+ unlock_return:
+        smb_unlock_server(server);
+        return result;
+}
+
+/* interpret a long filename structure - this is mostly guesses at the
+   moment.  The length of the structure is returned.  The structure of
+   a long filename depends on the info level. 260 is used by NT and 2
+   is used by OS/2. */
+
+static char *
+smb_decode_long_dirent(char *p, struct smb_dirent *finfo, int level)
+{
+        char *result;
+
+        if (finfo) {
+                /* I have to set times to 0 here, because I do not
+                   have specs about this for all info levels. */
+                finfo->ctime = finfo->mtime = finfo->atime = 0;
+        }
+
+        switch (level)
+        {
+        case 1:                 /* OS/2 understands this */
+                if (finfo)
+                {
+                        DPRINTK("received entry\n");
+                        strcpy(finfo->path,p+27);
+                        finfo->len  = strlen(finfo->path);
+                        finfo->size = DVAL(p,16);
+                        finfo->attr = BVAL(p,24);
+
+                        finfo->ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4));
+                        finfo->atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8));
+                        finfo->mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12));
+                }
+                result = p + 28 + BVAL(p,26);
+                break;
+
+        case 2:                 /* this is what OS/2 uses */
+                if (finfo)
+                {
+                        strcpy(finfo->path,p+31);
+                        finfo->len  = strlen(finfo->path);
+                        finfo->size = DVAL(p,16);
+                        finfo->attr = BVAL(p,24);
+#if 0
+                        finfo->atime = make_unix_date2(p+8);
+                        finfo->mtime = make_unix_date2(p+12);
+#endif
+                }
+                result = p + 32 + BVAL(p,30);
+                break;
+
+        case 260:               /* NT uses this, but also accepts 2 */
+                result = p + WVAL(p,0);
+                if (finfo)
+                {
+                        int namelen;
+                        p += 4; /* next entry offset */
+                        p += 4; /* fileindex */
+                        /* finfo->ctime = interpret_filetime(p);*/
+                        p += 8;
+                        /* finfo->atime = interpret_filetime(p);*/
+                        p += 8;
+                        p += 8; /* write time */
+                        /* finfo->mtime = interpret_filetime(p);*/
+                        p += 8;
+                        finfo->size = DVAL(p,0);
+                        p += 8;
+                        p += 8; /* alloc size */
+                        finfo->attr = BVAL(p,0);
+                        p += 4;
+                        namelen = min(DVAL(p,0), SMB_MAXNAMELEN);
+                        p += 4;
+                        p += 4; /* EA size */
+                        p += 2; /* short name len? */
+                        p += 24; /* short name? */	  
+                        strncpy(finfo->path,p,namelen);
+                        finfo->len = namelen;
+                }
+                break;
+
+        default:
+                DPRINTK("Unknown long filename format %d\n",level);
+                result = p + WVAL(p,0);
+        }
+        return result;
+}
+
+int
+smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos,
+                      int cache_size, struct smb_dirent *entry)
+{
+        int max_matches = 512;
+  
+        /* NT uses 260, OS/2 uses 2. Both accept 1. */
+        int info_level = 1;
+
+	char *p;
+	int i;
+	int first, total_count;
+        struct smb_dirent *current_entry;
+
+        char *resp_data;
+        char *resp_param;
+        int resp_data_len = 0;
+        int resp_param_len=0;
+
+        int attribute = aSYSTEM | aHIDDEN | aDIR;
+        int result;
+
+        int ff_resume_key = 0;
+        int ff_searchcount=0;
+        int ff_eos=0;
+        int ff_lastname=0;
+        int ff_dir_handle=0;
+        int loop_count = 0;
+
+	int dirlen = strlen(SMB_FINFO(dir)->path);
+	char mask[dirlen + 5];
+
+	strcpy(mask, SMB_FINFO(dir)->path);
+	strcat(mask, "\\*");
+
+ 	DPRINTK("SMB call lreaddir %d @ %d\n", cache_size, fpos);        
+	DPRINTK("          mask = %s\n", mask);
+
+        resp_param = NULL;
+        resp_data  = NULL;
+
+        smb_lock_server(server);
+
+ retry:
+
+	first = 1;
+        total_count = 0;
+        current_entry = entry;
+	
+        while (ff_eos == 0)
+        {
+                int masklen = strlen(mask);
+                unsigned char *outbuf = server->packet;
+                
+                loop_count += 1;
+                if (loop_count > 200)
+                {
+                        printk("smb_proc_readdir_long: "
+                               "Looping in FIND_NEXT??\n");
+                        break;
+                }
+
+                smb_setup_header(server, SMBtrans2, 15,
+                                 5 + 12 + masklen + 1);
+
+                WSET(outbuf,smb_tpscnt,12 + masklen +1);
+                WSET(outbuf,smb_tdscnt,0);
+                WSET(outbuf,smb_mprcnt,10); 
+                WSET(outbuf,smb_mdrcnt,TRANS2_MAX_TRANSFER);
+                WSET(outbuf,smb_msrcnt,0);
+                WSET(outbuf,smb_flags,0); 
+                DSET(outbuf,smb_timeout,0);
+                WSET(outbuf,smb_pscnt,WVAL(outbuf,smb_tpscnt));
+                WSET(outbuf,smb_psoff,((SMB_BUF(outbuf)+3) - outbuf)-4);
+                WSET(outbuf,smb_dscnt,0);
+                WSET(outbuf,smb_dsoff,0);
+                WSET(outbuf,smb_suwcnt,1);
+                WSET(outbuf,smb_setup0,
+                     first == 1 ? TRANSACT2_FINDFIRST : TRANSACT2_FINDNEXT);
+
+                p = SMB_BUF(outbuf);
+                *p++=0;         /* put in a null smb_name */
+                *p++='D'; *p++ = ' '; /* this was added because OS/2 does it */
+
+                if (first != 0)
+                {
+                        WSET(p,0,attribute); /* attribute */
+                        WSET(p,2,max_matches); /* max count */
+                        WSET(p,4,8+4+2); /* resume required + close on end +
+                                            continue */
+                        WSET(p,6,info_level); 
+                        DSET(p,8,0);
+                        p += 12;
+                        strncpy(p, mask, masklen);
+                        p += masklen;
+                        *p++ = 0; *p++ = 0;
+                }
+                else
+                {
+                        DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n",
+                                ff_dir_handle,ff_resume_key,ff_lastname,mask);
+                        WSET(p,0,ff_dir_handle);
+                        WSET(p,2,max_matches); /* max count */
+                        WSET(p,4,info_level); 
+                        DSET(p,6,ff_resume_key); /* ff_resume_key */
+                        WSET(p,10,8+4+2); /* resume required + close on end +
+                                             continue */
+                        p += 12;
+                        strncpy(p, mask, masklen);
+                        p += masklen;
+                        *p++ = 0; *p++ = 0;
+                }
+
+                result = smb_trans2_request(server,
+                                            &resp_data_len,&resp_param_len,
+                                            &resp_data,&resp_param);
+
+                if (result < 0) {
+                        if (smb_retry(server)) {
+                                goto retry;
+                        }
+                        DPRINTK("smb_proc_readdir_long: "
+                                "got error from trans2_request\n");
+                        break;
+                }
+
+                if (server->rcls != 0)
+                {
+                        result = -EIO;
+                        break;
+                }
+
+                /* parse out some important return info */
+                p = resp_param;
+                if (first != 0)
+                {
+                        ff_dir_handle = WVAL(p,0);
+                        ff_searchcount = WVAL(p,2);
+                        ff_eos = WVAL(p,4);
+                        ff_lastname = WVAL(p,8);
+                }
+                else
+                {
+                        ff_searchcount = WVAL(p,0);
+                        ff_eos = WVAL(p,2);
+                        ff_lastname = WVAL(p,6);
+                }
+
+                if (ff_searchcount == 0) 
+                        break;
+
+                /* point to the data bytes */
+                p = resp_data;
+
+                /* we might need the lastname for continuations */
+                if (ff_lastname > 0)
+                {
+                        switch(info_level)
+                        {
+                        case 260:
+                                ff_resume_key =0;
+                                strcpy(mask,p+ff_lastname+94);
+                                break;
+                        case 1:
+                                strcpy(mask,p + ff_lastname + 1);
+                                ff_resume_key = 0;
+                                break;
+                        }
+                }
+                else
+                        strcpy(mask,"");
+  
+		/* Now we are ready to parse smb directory entries. */
+		
+		for (i = 0; i < ff_searchcount; i ++) {
+			if (total_count < fpos) {
+				p = smb_decode_long_dirent(p, NULL,
+                                                           info_level);
+				DPRINTK("smb_proc_readdir: skipped entry.\n");
+				DDPRINTK("                  total_count = %d\n"
+                                         "                i = %d, fpos = %d\n",
+                                         total_count, i, fpos);
+                        }
+                        else if (total_count >= fpos + cache_size) {
+                                goto finished;
+			}
+			else {
+				p = smb_decode_long_dirent(p, current_entry,
+                                                           info_level);
+				current_entry->f_pos = total_count;
+				DDPRINTK("smb_proc_readdir: entry->f_pos = "
+                                         "%lu\n", entry->f_pos);	
+				current_entry += 1;
+			}
+			total_count += 1;
+		}
+
+                if (resp_data != NULL) {
+                        smb_kfree_s(resp_data,  0);
+                        resp_data = NULL;
+                }
+                if (resp_param != NULL) {
+                        smb_kfree_s(resp_param, 0);
+                        resp_param = NULL;
+                }
+
+                DPRINTK("received %d entries (eos=%d resume=%d)\n",
+                        ff_searchcount,ff_eos,ff_resume_key);
+
+                first = 0;
+        }
+
+ finished:
+        if (resp_data != NULL) {
+                smb_kfree_s(resp_data,  0);
+                resp_data = NULL;
+        }
+        if (resp_param != NULL) {
+                smb_kfree_s(resp_param, 0);
+                resp_param = NULL;
+        }
+
+        smb_unlock_server(server);
+
+        return total_count - fpos;
+}
+
+int
+smb_proc_readdir(struct smb_server *server, struct inode *dir, int fpos,
+		 int cache_size, struct smb_dirent *entry)
+{
+        if (server->protocol >= PROTOCOL_LANMAN2)
+                return smb_proc_readdir_long(server, dir, fpos, cache_size,
+                                             entry);
+        else
+                return smb_proc_readdir_short(server, dir, fpos, cache_size,
+                                              entry);
+}
+		
+static int
+smb_proc_getattr_core(struct smb_server *server, const char *path, int len, 
+                      struct smb_dirent *entry)
+{
+	int result;
+	char *p;
+        char *buf = server->packet;
+
+        smb_lock_server(server);
+
+        DDPRINTK("smb_proc_getattr: %s\n", path);
+
+ retry:
+	p = smb_setup_header(server, SMBgetatr, 0, 2 + len);
+	smb_encode_ascii(p, path, len);
+	
+	if ((result = smb_request_ok(server, SMBgetatr, 10, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return result;
+        }
+
+        entry->attr         = WVAL(buf, smb_vwv0);
+        entry->ctime = entry->atime = /* The server only tells us 1 time */
+                entry->mtime = local2utc(DVAL(buf, smb_vwv1));
+
+        entry->size         = DVAL(buf, smb_vwv3);
+        smb_unlock_server(server);
+	return 0;
+}
+
+/* smb_proc_getattrE: entry->fid must be valid */
+
+static int
+smb_proc_getattrE(struct smb_server *server, struct smb_dirent *entry)
+{
+        char* buf = server->packet;
+        int result;
+
+        smb_setup_header_exclusive(server, SMBgetattrE, 1, 0);
+        WSET(buf, smb_vwv0, entry->fileid);
+
+        if ((result = smb_request_ok(server, SMBgetattrE, 11, 0)) != 0) {
+                smb_unlock_server(server);
+                return result;
+        }
+
+        entry->ctime = date_dos2unix(WVAL(buf, smb_vwv1), WVAL(buf, smb_vwv0));
+        entry->atime = date_dos2unix(WVAL(buf, smb_vwv3), WVAL(buf, smb_vwv2));
+        entry->mtime = date_dos2unix(WVAL(buf, smb_vwv5), WVAL(buf, smb_vwv4));
+        entry->size  = DVAL(buf, smb_vwv6);
+        entry->attr  = WVAL(buf, smb_vwv10);
+
+        smb_unlock_server(server);
+        return 0;
+}
+
+int
+smb_proc_getattr(struct smb_server *server, const char *path, int len, 
+                 struct smb_dirent *entry)
+{
+        if (server->protocol >= PROTOCOL_LANMAN1) {
+
+                int result = 0;
+                struct smb_dirent temp_entry;
+
+                if ((result=smb_proc_open(server,path,len,
+                                          &temp_entry)) < 0) {
+                        /* We cannot open directories, so we try to use the
+                           core variant */
+                        return smb_proc_getattr_core(server,path,len,entry);
+                }
+
+                if ((result=smb_proc_getattrE(server, &temp_entry)) >= 0) {
+                        entry->attr  = temp_entry.attr;
+                        entry->atime = temp_entry.atime;
+                        entry->mtime = temp_entry.mtime;
+                        entry->ctime = temp_entry.ctime;
+                        entry->size  = temp_entry.size;
+                }
+                
+                smb_proc_close(server, &temp_entry);
+                return result;
+
+        } else {
+                return smb_proc_getattr_core(server, path, len, entry);
+        }
+}
+
+
+/* In core protocol, there is only 1 time to be set, we use
+   entry->mtime, to make touch work. */
+static int
+smb_proc_setattr_core(struct smb_server *server,
+                      const char *path, int len,
+                      struct smb_dirent *new_finfo)
+{
+        char *p;
+        char *buf = server->packet;
+        int result;
+
+        smb_lock_server(server);
+
+ retry:
+        p = smb_setup_header(server, SMBsetatr, 8, 4 + len);
+        WSET(buf, smb_vwv0, new_finfo->attr);
+        DSET(buf, smb_vwv1, utc2local(new_finfo->mtime));
+        p = smb_encode_ascii(p, path, len);
+        p = smb_encode_ascii(p, "", 0);
+
+        if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+        }
+        smb_unlock_server(server);
+        return result;
+}
+
+/* smb_proc_setattrE: we do not retry here, because we rely on fid,
+   which would not be valid after a retry. */
+static int
+smb_proc_setattrE(struct smb_server *server, word fid,
+                  struct smb_dirent *new_entry)
+{
+        char *buf = server->packet;
+        word date, time;
+
+        smb_setup_header_exclusive(server, SMBsetattrE, 7, 0);
+
+        WSET(buf, smb_vwv0, fid);
+
+        date_unix2dos(new_entry->ctime, &time, &date);
+        WSET(buf, smb_vwv1, date);
+        WSET(buf, smb_vwv2, time);
+        
+        date_unix2dos(new_entry->atime, &time, &date);
+        WSET(buf, smb_vwv3, date);
+        WSET(buf, smb_vwv4, time);
+        
+        date_unix2dos(new_entry->mtime, &time, &date);
+        WSET(buf, smb_vwv5, date);
+        WSET(buf, smb_vwv6, time);
+
+        return smb_request_ok_unlock(server, SMBsetattrE, 0, 0);
+}
+
+/* smb_proc_setattr: for protocol >= LANMAN1 we expect the file to be
+   opened for writing. */
+int
+smb_proc_setattr(struct smb_server *server, struct inode *inode,
+                 struct smb_dirent *new_finfo)
+{
+        struct smb_dirent *finfo = SMB_FINFO(inode);
+        int result;
+
+        if (server->protocol >= PROTOCOL_LANMAN1) {
+                if ((result = smb_make_open(inode, O_RDWR)) < 0)
+                        return result;
+                return smb_proc_setattrE(server, finfo->fileid, new_finfo);
+        } else {
+                return smb_proc_setattr_core(server, finfo->path, finfo->len,
+                                             new_finfo);
+        }
+}
+
+int
+smb_proc_dskattr(struct super_block *super, struct smb_dskattr *attr)
+{
+	int error;
+	char *p;
+	struct smb_server *server = &(SMB_SBP(super)->s_server);
+
+        smb_lock_server(server);
+
+ retry:
+	smb_setup_header(server, SMBdskattr, 0, 0);
+	
+	if ((error = smb_request_ok(server, SMBdskattr, 5, 0)) < 0) {
+                if (smb_retry(server)) {
+                        goto retry;
+                }
+                smb_unlock_server(server);
+		return error;
+        }
+	
+	p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, &attr->total);
+	p = smb_decode_word(p, &attr->allocblocks);
+	p = smb_decode_word(p, &attr->blocksize);
+	p = smb_decode_word(p, &attr->free);
+        smb_unlock_server(server);
+	return 0;
+}
+
+/*****************************************************************************/
+/*                                                                           */
+/*  Mount/umount operations.                                                 */
+/*                                                                           */
+/*****************************************************************************/
+
+struct smb_prots {
+	enum smb_protocol prot;
+	char *name;
+};
+
+/* smb_proc_reconnect: We expect the server to be locked, so that you
+   can call the routine from within smb_retry. The socket must be
+   created, like after a user-level socket()-call. It may not be
+   connected. */
+
+int
+smb_proc_reconnect(struct smb_server *server)
+{
+	struct smb_prots prots[] =
+        { { PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"},
+          { PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
+#ifdef LANMAN1
+          { PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
+          { PROTOCOL_LANMAN1,"LANMAN1.0"},
+#endif
+#ifdef LANMAN2
+          { PROTOCOL_LANMAN2,"LM1.2X002"},
+#endif
+          {-1, NULL} };
+	char dev[] = "A:";
+	int i, plength;
+	int max_xmit = 1024;	/* Space needed for first request. */
+        int given_max_xmit = server->m.max_xmit;
+	int result;
+	byte *p;
+
+        if ((result = smb_connect(server)) < 0) {
+                DPRINTK("smb_proc_reconnect: could not smb_connect\n");
+                goto fail;
+        }
+
+        /* Here we assume that the connection is valid */
+        server->state = CONN_VALID;
+
+        if (server->packet != NULL) {
+                smb_kfree_s(server->packet, server->max_xmit);
+        }
+        
+	server->packet = smb_kmalloc(max_xmit, GFP_KERNEL);
+
+	if (server->packet == NULL) {
+		printk("smb_proc_connect: No memory! Bailing out.\n");
+                result = -ENOMEM;
+                goto fail;
+	}
+
+        server->max_xmit = max_xmit;
+
+	/*
+	 * Start with an RFC1002 session request packet.
+	 */
+	p = server->packet + 4;
+
+	p = smb_name_mangle(p, server->m.server_name);
+	p = smb_name_mangle(p, server->m.client_name);
+	
+	smb_encode_smb_length(server->packet,
+                              (void *)p - (void *)(server->packet));
+	
+	server->packet[0] = 0x81; /* SESSION REQUEST */
+
+        if (smb_catch_keepalive(server) < 0) {
+                printk("smb_proc_connect: could not catch_keepalives\n");
+        }
+        
+	if ((result = smb_request(server)) < 0) {
+		printk("smb_proc_connect: Failed to send SESSION REQUEST.\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+	
+	if (server->packet[0] != 0x82) {
+		printk("smb_proc_connect: Did not recieve positive response "
+                       "(err = %x)\n", 
+		       server->packet[0]);
+                smb_dont_catch_keepalive(server);
+#if DEBUG_SMB > 0
+                smb_dump_packet(server->packet);
+#endif
+                result = -EIO;
+                goto fail;
+	}
+
+        DPRINTK("smb_proc_connect: Passed SESSION REQUEST.\n");
+	
+	/* Now we are ready to send a SMB Negotiate Protocol packet. */
+	memset(server->packet, 0, SMB_HEADER_LEN);
+
+	plength = 0;
+	for (i = 0; prots[i].name != NULL; i++) {
+		plength += strlen(prots[i].name) + 2;
+        }
+
+	smb_setup_header(server, SMBnegprot, 0, plength);
+
+	p = SMB_BUF(server->packet);
+	
+	for (i = 0; prots[i].name != NULL; i++) {
+		p = smb_encode_dialect(p,prots[i].name, strlen(prots[i].name));
+        }
+	
+	if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0) {
+         	printk("smb_proc_connect: Failure requesting SMBnegprot\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	} else {
+                DDPRINTK("smb_proc_connect: Request SMBnegprot..");
+        }
+
+        DDPRINTK("Verified!\n");
+
+        p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, (word *)&i);
+        server->protocol = prots[i].prot;
+
+	DPRINTK("smb_proc_connect: Server wants %s protocol.\n",
+                prots[i].name);
+
+        if (server->protocol > PROTOCOL_LANMAN1) {
+
+                word passlen = strlen(server->m.password);
+                word userlen = strlen(server->m.username);
+                
+                DPRINTK("smb_proc_connect: password = %s\n",
+                        server->m.password);
+                DPRINTK("smb_proc_connect: usernam = %s\n",
+                        server->m.username);
+
+                p = smb_decode_word(p, &(server->maxxmt));
+                p = smb_decode_word(p, &(server->maxmux));
+                p = smb_decode_word(p, &(server->maxvcs));
+                p = smb_decode_word(p, &(server->blkmode));
+                p = smb_decode_dword(p, &(server->sesskey));
+
+                smb_setup_header(server, SMBsesssetupX, 10,
+                                 2 + userlen + passlen);
+
+                WSET(server->packet, smb_vwv0, 0x00ff);
+                WSET(server->packet, smb_vwv1, 0);
+                WSET(server->packet, smb_vwv2, given_max_xmit);
+                WSET(server->packet, smb_vwv3, 2);
+                WSET(server->packet, smb_vwv4, server->pid);
+                DSET(server->packet, smb_vwv5, server->sesskey);
+                WSET(server->packet, smb_vwv7, passlen + 1);
+                WSET(server->packet, smb_vwv8, 0);
+                WSET(server->packet, smb_vwv9, 0);
+
+                p = SMB_BUF(server->packet);
+                strcpy(p, server->m.password);
+                p += passlen + 1;
+                strcpy(p, server->m.username);
+
+                if ((result = smb_request_ok(server,SMBsesssetupX,3,0)) < 0) {
+                        DPRINTK("smb_proc_connect: SMBsessetupX failed\n");
+                        smb_dont_catch_keepalive(server);
+                        goto fail;
+                }
+                smb_decode_word(server->packet+32, &(server->server_uid));
+        }
+
+	/* Fine! We have a connection, send a tcon message. */
+
+	smb_setup_header(server, SMBtcon, 0,
+                         6 + strlen(server->m.service) +
+                         strlen(server->m.password) + strlen(dev));
+	p = SMB_BUF(server->packet);
+	p = smb_encode_ascii(p, server->m.service, strlen(server->m.service));
+	p = smb_encode_ascii(p,server->m.password, strlen(server->m.password));
+	p = smb_encode_ascii(p, dev, strlen(dev));
+
+	if ((result = smb_request_ok(server, SMBtcon, 2, 0)) < 0) {
+		DPRINTK("smb_proc_connect: SMBtcon not verified.\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+
+        DDPRINTK("OK! Managed to set up SMBtcon!\n");
+   
+	p = SMB_VWV(server->packet);
+	p = smb_decode_word(p, &server->max_xmit);
+
+        if (server->max_xmit > given_max_xmit)
+                server->max_xmit = given_max_xmit;
+        
+	p = smb_decode_word(p, &server->tid);
+
+	/* Ok, everything is fine. max_xmit does not include */
+	/* the TCP-SMB header of 4 bytes. */
+	server->max_xmit += 4;
+
+	DPRINTK("max_xmit = %d, tid = %d\n", server->max_xmit, server->tid);
+
+	/* Now make a new packet with the correct size. */
+	smb_kfree_s(server->packet, max_xmit); 
+
+	server->packet = smb_kmalloc(server->max_xmit, GFP_KERNEL);
+	if (server->packet == NULL) {
+		printk("smb_proc_connect: No memory left in end of "
+                       "connection phase :-(\n");
+                smb_dont_catch_keepalive(server);
+                goto fail;
+	}
+
+        DPRINTK("smb_proc_connect: Normal exit\n");
+        return 0;
+
+ fail:
+        server->state = CONN_INVALID;
+        return result;
+}
+
+/* smb_proc_reconnect: server->packet is allocated with
+   server->max_xmit bytes if and only if we return >= 0 */
+int
+smb_proc_connect(struct smb_server *server)
+{
+        int result;
+        smb_lock_server(server);
+        result = smb_proc_reconnect(server);
+        if ((result < 0) && (server->packet != NULL)) {
+                smb_kfree_s(server->packet, server->max_xmit);
+                server->packet = NULL;
+        }
+        smb_unlock_server(server);
+        return result;
+}
+	
+int
+smb_proc_disconnect(struct smb_server *server)
+{
+	smb_setup_header_exclusive(server, SMBtdis, 0, 0);
+	return smb_request_ok_unlock(server, SMBtdis, 0, 0);
+}
+
+/* error code stuff - put together by Merik Karman
+   merik@blackadder.dsh.oz.au */
+
+#if DEBUG_SMB > 0
+
+typedef struct {
+	char *name;
+	int code;
+	char *message;
+} err_code_struct;
+
+/* Dos Error Messages */
+err_code_struct dos_msgs[] = {
+  { "ERRbadfunc",1,"Invalid function."},
+  { "ERRbadfile",2,"File not found."},
+  { "ERRbadpath",3,"Directory invalid."},
+  { "ERRnofids",4,"No file descriptors available"},
+  { "ERRnoaccess",5,"Access denied."},
+  { "ERRbadfid",6,"Invalid file handle."},
+  { "ERRbadmcb",7,"Memory control blocks destroyed."},
+  { "ERRnomem",8,"Insufficient server memory to perform the requested function."},
+  { "ERRbadmem",9,"Invalid memory block address."},
+  { "ERRbadenv",10,"Invalid environment."},
+  { "ERRbadformat",11,"Invalid format."},
+  { "ERRbadaccess",12,"Invalid open mode."},
+  { "ERRbaddata",13,"Invalid data."},
+  { "ERR",14,"reserved."},
+  { "ERRbaddrive",15,"Invalid drive specified."},
+  { "ERRremcd",16,"A Delete Directory request attempted  to  remove  the  server's  current directory."},
+  { "ERRdiffdevice",17,"Not same device."},
+  { "ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."},
+  { "ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing  FIDs  on the file."},
+  { "ERRlock",33,"A Lock request conflicted with an existing lock or specified an  invalid mode,  or an Unlock requested attempted to remove a lock held by another process."},
+  { "ERRfilexists",80,"The file named in a Create Directory, Make  New  File  or  Link  request already exists."},
+  { "ERRbadpipe",230,"Pipe invalid."},
+  { "ERRpipebusy",231,"All instances of the requested pipe are busy."},
+  { "ERRpipeclosing",232,"Pipe close in progress."},
+  { "ERRnotconnected",233,"No process on other end of pipe."},
+  { "ERRmoredata",234,"There is more data to be returned."},
+  { NULL,-1,NULL}};
+
+/* Server Error Messages */
+err_code_struct server_msgs[] = { 
+  { "ERRerror",1,"Non-specific error code."},
+  { "ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."},
+  { "ERRbadtype",3,"reserved."},
+  { "ERRaccess",4,"The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID."},
+  { "ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."},
+  { "ERRinvnetname",6,"Invalid network name in tree connect."},
+  { "ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection."},
+  { "ERRqfull",49,"Print queue full (files) -- returned by open print file."},
+  { "ERRqtoobig",50,"Print queue full -- no space."},
+  { "ERRqeof",51,"EOF on print queue dump."},
+  { "ERRinvpfid",52,"Invalid print file FID."},
+  { "ERRsmbcmd",64,"The server did not recognize the command received."},
+  { "ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."},
+  { "ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid  combination of values."},
+  { "ERRreserved",68,"reserved."},
+  { "ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute."},
+  { "ERRreserved",70,"reserved."},
+  { "ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."},
+  { "ERRpaused",81,"Server is paused."},
+  { "ERRmsgoff",82,"Not receiving messages."},
+  { "ERRnoroom",83,"No room to buffer message."},
+  { "ERRrmuns",87,"Too many remote user names."},
+  { "ERRtimeout",88,"Operation timed out."},
+  { "ERRnoresource",89,"No resources currently available for request."},
+  { "ERRtoomanyuids",90,"Too many UIDs active on this session."},
+  { "ERRbaduid",91,"The UID is not known as a valid ID on this session."},
+  { "ERRusempx",250,"Temp unable to support Raw, use MPX mode."},
+  { "ERRusestd",251,"Temp unable to support Raw, use standard read/write."},
+  { "ERRcontmpx",252,"Continue in MPX mode."},
+  { "ERRreserved",253,"reserved."},
+  { "ERRreserved",254,"reserved."},
+  { "ERRnosupport",0xFFFF,"Function not supported."},
+  { NULL,-1,NULL}};
+
+/* Hard Error Messages */
+err_code_struct hard_msgs[] = { 
+  { "ERRnowrite",19,"Attempt to write on write-protected diskette."},
+  { "ERRbadunit",20,"Unknown unit."},
+  { "ERRnotready",21,"Drive not ready."},
+  { "ERRbadcmd",22,"Unknown command."},
+  { "ERRdata",23,"Data error (CRC)."},
+  { "ERRbadreq",24,"Bad request structure length."},
+  { "ERRseek",25 ,"Seek error."},
+  { "ERRbadmedia",26,"Unknown media type."},
+  { "ERRbadsector",27,"Sector not found."},
+  { "ERRnopaper",28,"Printer out of paper."},
+  { "ERRwrite",29,"Write fault."},
+  { "ERRread",30,"Read fault."},
+  { "ERRgeneral",31,"General failure."},
+  { "ERRbadshare",32,"A open conflicts with an existing open."},
+  { "ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."},
+  { "ERRwrongdisk",34,"The wrong disk was found in a drive."},
+  { "ERRFCBUnavail",35,"No FCBs are available to process request."},
+  { "ERRsharebufexc",36,"A sharing buffer has been exceeded."},
+  { NULL,-1,NULL}
+};
+
+
+struct { 
+	int code;
+	char *class;
+	err_code_struct *err_msgs;
+} err_classes[] = {  
+  { 0,"SUCCESS",NULL},
+  { 0x01,"ERRDOS",dos_msgs},
+  { 0x02,"ERRSRV",server_msgs},
+  { 0x03,"ERRHRD",hard_msgs},
+  { 0x04,"ERRXOS",NULL},
+  { 0xE1,"ERRRMX1",NULL},
+  { 0xE2,"ERRRMX2",NULL},
+  { 0xE3,"ERRRMX3",NULL},
+  { 0xFF,"ERRCMD",NULL},
+  { -1,NULL,NULL}
+};
+
+void
+smb_printerr(int class, int num)
+{
+	int i,j;
+	err_code_struct *err;
+
+	for (i=0; err_classes[i].class; i++) {
+		if (err_classes[i].code != class)
+			continue;
+		if (!err_classes[i].err_msgs) {
+			printk("%s - %d", err_classes[i].class, num);
+			return;
+		}
+
+		err = err_classes[i].err_msgs;
+		for (j=0; err[j].name; j++) {
+			if (num != err[j].code)
+				continue;
+			printk("%s - %s (%s)",
+			       err_classes[i].class, err[j].name,
+                               err[j].message);
+			return;
+		}
+	}
+	
+	printk("Unknown error - (%d,%d)", class, num);
+	return;
+}
+
+#endif /* DEBUG_SMB > 0 */
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this