ftp.nice.ch/pub/next/unix/network/system/sharity-light.0.4.s.tar.gz#/rumba.0.4.s/proc.c

This is proc.c in view mode; [Download] [Up]

/*
 *  proc.c
 *
 *  Copyright (C) 1995 by Paal-Kr. Engstad and Volker Lendecke
 *
 *  28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per
 *
 *	Modified for big endian support by Christian Starkjohann.
 */

#include <linux/config.h>
#include <linux/fs.h>
#include <linux/smbno.h>
#include <linux/smb_fs.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <asm/segment.h>
#include <asm/string.h>

#ifdef __LITTLE_ENDIAN__
#define ARCH i386
#else
#define	ARCH m68k
#endif

#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);
static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);


/*
 * The following is an evil hack to make debugging in "proc.c" configurable
 * from the commandline.
 */
#undef DPRINTK
#undef DDPRINTK
#include "my_defines.h"
#define	DPRINTK(format, args...)	\
			if(debug_mode & DEBUG_PROC) printk(format, ## args)
#define DDPRINTK(format, args...)	\
			if(debug_mode & DEBUG_PROC_NOISY) printk(format , ## args)


/*****************************************************************************/
/*                                                                           */
/*  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;
#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];
}

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_mode & DEBUG_PROC_NOISY){
        if (errcls) {
                printk("smb_errno: ");
                smb_printerr(errcls, error);
                printk("\n");
        }
	}

        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 EDEADLK;
                        case ERRfilexists:  return EEXIST;
                        case 87:            return 0; /* Unknown error!! */
                        /* This next error seems to occur on an mv when
                         * the destination exists */
                        case 183:           return EEXIST;
                        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 EDEADLK;
                        default: return EIO;
                }
        else if (errcls == ERRCMD) 
                return EIO;
        return 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");
        }
}

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_VALID) {
                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);

        entry->opened = 1;
        entry->access &= 3;

        smb_unlock_server(server);

        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,
               __u16 fileid, __u32 mtime)
{
        char *buf = server->packet;

        smb_setup_header_exclusive(server, SMBclose, 3, 0);
        WSET(buf, smb_vwv0, fileid);
        DSET(buf, smb_vwv1, utc2local(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;
}

/* count must be <= 65535. No error number is returned.  A result of 0
   indicates an error, which has to be investigated by a normal read
   call. */
int
smb_proc_read_raw(struct smb_server *server, struct smb_dirent *finfo, 
                  off_t offset, long count, char *data)
{
        char *buf = server->packet;
        int result;

        if ((count <= 0) || (count > 65535)) {
                return -EINVAL;
        }

        smb_setup_header_exclusive(server, SMBreadbraw, 8, 0);

        WSET(buf, smb_vwv0, finfo->fileid);
        DSET(buf, smb_vwv1, offset);
        WSET(buf, smb_vwv3, count);
        WSET(buf, smb_vwv4, 0);
        DSET(buf, smb_vwv5, 0);

        result = smb_request_read_raw(server, data, count);
        smb_unlock_server(server);
        return result;
}

int
smb_proc_write(struct smb_server *server, struct smb_dirent *finfo,
               off_t offset, int count, const 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;
}

/* count must be <= 65535 */
int
smb_proc_write_raw(struct smb_server *server, struct smb_dirent *finfo, 
                   off_t offset, long count, const char *data)
{
        char *buf = server->packet;
        int result;

        if ((count <= 0) || (count > 65535)) {
                return -EINVAL;
        }

        smb_setup_header_exclusive(server, SMBwritebraw, 11, 0);

        WSET(buf, smb_vwv0, finfo->fileid);
        WSET(buf, smb_vwv1, count);
        WSET(buf, smb_vwv2, 0); /* reserved */
        DSET(buf, smb_vwv3, offset);
        DSET(buf, smb_vwv5, 0); /* timeout */
        WSET(buf, smb_vwv7, 1); /* send final result response */
        DSET(buf, smb_vwv8, 0); /* reserved */
        WSET(buf, smb_vwv10, 0); /* no data in this buf */
        WSET(buf, smb_vwv11, 0); /* no data in this buf */

        result = smb_request_ok(server, SMBwritebraw, 1, 0);

        DPRINTK("smb_proc_write_raw: first request returned %d\n", result);
        
        if (result < 0) {
                smb_unlock_server(server);
                return result;
        }
        
        result = smb_request_write_raw(server, data, count);

        DPRINTK("smb_proc_write_raw: raw request returned %d\n", result);
        
        if (result > 0) {
                /* We have to do the checks of smb_request_ok here as well */
                if (smb_valid_packet(server->packet) != 0) {
                        DPRINTK("not a valid packet!\n");
                        result = -EIO;
                } else if (server->rcls != 0) {
                        result = -smb_errno(server->rcls, server->err);
                } else if (smb_verify(server->packet, SMBwritec,1,0) != 0) {
                        DPRINTK("smb_verify failed\n");
                        result = -EIO;
                }
        }

        smb_unlock_server(server);
        return result;
}


/* 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->fileid, entry->mtime);

		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 = "
                                         "%d\n", current_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 = 64; /* this should actually be based on the 
								 maxxmit */
  
		/* NT uses 260, OS/2 uses 2. Both accept 1. */
		int info_level = 1;

		char *p;
		char *lastname;
		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) + 3;
		char *mask;

		mask = smb_kmalloc(dirlen, GFP_KERNEL);
		if (mask == NULL)
		{
				printk("smb_proc_readdir_long: Memory allocation failed\n");
				return -ENOMEM;
		}
		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 */
				lastname = "";
				if (ff_lastname > 0)
				{
						switch(info_level)
						{
						case 260:
								lastname = p + ff_lastname + 94;
								ff_resume_key = 0;
								break;
						case 1:
								lastname = p + ff_lastname + 1;
								ff_resume_key = 0;
								break;
						}
				}
  
				/* Increase size of mask, if it is too small */
				i = strlen(lastname) + 1;
				if (i > dirlen)
				{
						smb_kfree_s(mask,  0);
						dirlen = i;
						mask = smb_kmalloc(dirlen, GFP_KERNEL);
						if (mask == NULL)
						{
								printk("smb_proc_readdir_long: Memory allocation failed\n");
								result = -ENOMEM;
								break;
						}
						strcpy(mask, lastname);
				}
  
				/* 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", current_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 (mask != NULL)
				smb_kfree_s(mask,  0);

		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);
}
                
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 */

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;

                memset(&temp_entry, 0, sizeof(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.fileid, temp_entry.mtime);
                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. */
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. */
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;
        const 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
#ifdef NT1
          { PROTOCOL_NT1,"NT LM 0.12"},
          { PROTOCOL_NT1,"NT LANMAN 1.0"},
#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;
		word any_word;
        byte *p;

		if(server->max_recv <= 0){
			server->max_recv = given_max_xmit > 8000 ? given_max_xmit : 8000;
		}
        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_recv);
        }
        server->packet = smb_kmalloc(server->max_recv, 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 receive positive response "
                       "(err = %x)\n", 
                       server->packet[0]);
                smb_dont_catch_keepalive(server);
		if(debug_mode & DEBUG_PROC_NOISY){
                smb_dump_packet(server->packet);
		}
                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, &any_word);
		i = any_word;
        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);
                DPRINTK("smb_proc_connect: blkmode = %d\n",
                        WVAL(server->packet, smb_vwv5));

                if (server->protocol >= PROTOCOL_NT1) {
                        server->maxxmt = DVAL(server->packet,smb_vwv3+1);
                        server->maxmux = WVAL(server->packet, smb_vwv1+1);
                        server->maxvcs = WVAL(server->packet, smb_vwv2+1);
                        server->blkmode= DVAL(server->packet, smb_vwv9+1);
                        server->sesskey= DVAL(server->packet, smb_vwv7+1);
                } else {
                        server->maxxmt = WVAL(server->packet, smb_vwv2);
                        server->maxmux = WVAL(server->packet, smb_vwv3);
                        server->maxvcs = WVAL(server->packet, smb_vwv4);
                        server->blkmode= WVAL(server->packet, smb_vwv5);
                        server->sesskey= DVAL(server->packet, smb_vwv6);
                }


                if (server->protocol >= PROTOCOL_NT1) {
                        char *workgroup = "WORKGROUP";
                        char *OS_id = "Unix";
                        char *client_id = "ksmbfs";

                        smb_setup_header(server, SMBsesssetupX, 13,
                                         5 + userlen + passlen +
                                         strlen(workgroup) + strlen(OS_id) +
                                         strlen(client_id));
                        
                        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);
                        p += userlen + 1;
                        strcpy(p, workgroup);
                        p += strlen(p) + 1;
                        strcpy(p, OS_id);
                        p += strlen(p) + 1;
                        strcpy(p, client_id);
                } else {
                        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));
        }
        else
        {
                server->maxxmt = 0;
                server->maxmux = 0;
                server->maxvcs = 0;
                server->blkmode = 0;
                server->sesskey = 0;
        }

        /* 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);

# if 0	/* CS: don't resize buffer to max_xmit, leave it at max_recv */
        /* 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;
        }
#endif

        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_recv);
                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 */


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;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.