diff options
Diffstat (limited to 'fs/smbfs/proc.c')
-rw-r--r-- | fs/smbfs/proc.c | 1969 |
1 files changed, 1969 insertions, 0 deletions
diff --git a/fs/smbfs/proc.c b/fs/smbfs/proc.c new file mode 100644 index 000000000..ee998a89f --- /dev/null +++ b/fs/smbfs/proc.c @@ -0,0 +1,1969 @@ +/* + * proc.c + * + * Copyright (C) 1995, 1996 by Paal-Kr. Engstad and Volker Lendecke + * + * 28/06/96 - Fixed long file name support (smb_proc_readdir_long) by Yuri Per + */ + +#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/uaccess.h> +#include <asm/string.h> + +#define SMB_VWV(packet) ((packet) + SMB_HEADER_LEN) +#define SMB_CMD(packet) (BVAL(packet,8)) +#define SMB_WCT(packet) (BVAL(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 + +static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc); + +static inline int +min(int a, int b) +{ + return a < b ? a : b; +} + +static void +str_upper(char *name) +{ + while (*name) + { + if (*name >= 'a' && *name <= 'z') + *name -= ('a' - 'A'); + name++; + } +} + +static void +str_lower(char *name) +{ + while (*name) + { + if (*name >= 'A' && *name <= 'Z') + *name += ('a' - 'A'); + name++; + } +} + +/*****************************************************************************/ +/* */ +/* Encoding/Decoding section */ +/* */ +/*****************************************************************************/ + +static inline byte * +smb_decode_word(byte * p, word * data) +{ + *data = WVAL(p, 0); + return p + 2; +} + +byte * +smb_encode_smb_length(byte * p, dword len) +{ + BSET(p, 0, 0); + BSET(p, 1, 0); + BSET(p, 2, (len & 0xFF00) >> 8); + BSET(p, 3, (len & 0xFF)); + if (len > 0xFFFF) + { + BSET(p, 1, 1); + } + return p + 4; +} + +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_this_name(byte * p, const char *name, const int len) +{ + *p++ = '\\'; + strncpy(p, name, len); + return p + len; +} + +/* I put smb_encode_parents into a separate function so that the + recursion only takes 16 bytes on the stack per path component on a + 386. */ + +static byte * +smb_encode_parents(byte * p, struct smb_inode_info *ino) +{ + byte *q; + + if (ino->dir == NULL) + { + return p; + } + q = smb_encode_parents(p, ino->dir); + if (q - p + 1 + ino->finfo.len > SMB_MAXPATHLEN) + { + return p; + } + return smb_encode_this_name(q, ino->finfo.name, ino->finfo.len); +} + +static byte * +smb_encode_path(struct smb_server *server, + byte * p, struct smb_inode_info *dir, + const char *name, const int len) +{ + byte *start = p; + p = smb_encode_parents(p, dir); + p = smb_encode_this_name(p, name, len); + *p++ = 0; + if (server->protocol <= PROTOCOL_COREPLUS) + { + str_upper(start); + } + return p; +} + +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) + copy_to_user(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, byte * date, byte * time) +{ + int day, year, nl_day, month; + + unix_date = utc2local(unix_date); + WSET(time, 0, + (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; + } + WSET(date, 0, + nl_day - day_n[month - 1] + 1 + (month << 5) + (year << 9)); +} + + + +/*****************************************************************************/ +/* */ +/* Support section. */ +/* */ +/*****************************************************************************/ + +dword +smb_len(byte * p) +{ + return ((BVAL(p, 1) & 0x1) << 16L) | (BVAL(p, 2) << 8L) | (BVAL(p, 3)); +} + +static word +smb_bcc(byte * packet) +{ + int pos = SMB_HEADER_LEN + SMB_WCT(packet) * sizeof(word); + return WVAL(packet, pos); +} + +/* smb_valid_packet: We check if packet fulfills the basic + requirements of a smb packet */ + +static int +smb_valid_packet(byte * packet) +{ + DDPRINTK("len: %d, 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 (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 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 */ + +__u8 * +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 - 4); + + 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); +} + +static void +smb_setup_bcc(struct smb_server *server, byte * p) +{ + __u8 *packet = server->packet; + __u8 *pbcc = packet + SMB_HEADER_LEN + 2 * SMB_WCT(packet); + __u16 bcc = p - (pbcc + 2); + + WSET(pbcc, 0, bcc); + smb_encode_smb_length(packet, + SMB_HEADER_LEN + 2 * SMB_WCT(packet) - 2 + bcc); +} + + +/*****************************************************************************/ +/* */ +/* File operation section. */ +/* */ +/*****************************************************************************/ + +int +smb_proc_open(struct smb_server *server, + struct smb_inode_info *dir, const char *name, int len, + struct smb_dirent *entry) +{ + int error; + char *p; + char *buf; + const word o_attr = aSYSTEM | aHIDDEN | aDIR; + + DPRINTK("smb_proc_open: name=%s\n", name); + + smb_lock_server(server); + buf = server->packet; + + if (entry->opened != 0) + { + /* Somebody else opened the file while we slept */ + smb_unlock_server(server); + return 0; + } + retry: + p = smb_setup_header(server, SMBopen, 2, 0); + WSET(buf, smb_vwv0, 0x42); /* read/write */ + WSET(buf, smb_vwv1, o_attr); + *p++ = 4; + p = smb_encode_path(server, p, dir, name, len); + smb_setup_bcc(server, p); + + if ((error = smb_request_ok(server, SMBopen, 7, 0)) != 0) + { + + if (smb_retry(server)) + { + goto retry; + } + if ((error != -EACCES) && (error != -ETXTBSY) + && (error != -EROFS)) + { + smb_unlock_server(server); + return error; + } + p = smb_setup_header(server, SMBopen, 2, 0); + WSET(buf, smb_vwv0, 0x40); /* read only */ + WSET(buf, smb_vwv1, o_attr); + *p++ = 4; + p = smb_encode_path(server, p, dir, name, len); + smb_setup_bcc(server, p); + + 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->f_ctime = entry->f_atime = + entry->f_mtime = local2utc(DVAL(buf, smb_vwv2)); + entry->f_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; +} + +int +smb_proc_close(struct smb_server *server, + __u16 fileid, __u32 mtime) +{ + char *buf; + + smb_setup_header_exclusive(server, SMBclose, 3, 0); + buf = server->packet; + 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 + copy_to_user. */ + +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; + int error; + + smb_setup_header_exclusive(server, SMBread, 5, 0); + buf = server->packet; + + 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, const char *data) +{ + int res = 0; + char *buf; + byte *p; + + p = smb_setup_header_exclusive(server, SMBwrite, 5, count + 3); + buf = server->packet; + 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); + copy_from_user(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; +} + +int +smb_proc_create(struct inode *dir, const char *name, int len, + word attr, time_t ctime) +{ + int error; + char *p; + struct smb_server *server = SMB_SERVER(dir); + char *buf; + __u16 fileid; + + smb_lock_server(server); + buf = server->packet; + retry: + p = smb_setup_header(server, SMBcreate, 3, 0); + WSET(buf, smb_vwv0, attr); + DSET(buf, smb_vwv1, utc2local(ctime)); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), name, len); + smb_setup_bcc(server, p); + + if ((error = smb_request_ok(server, SMBcreate, 1, 0)) < 0) + { + if (smb_retry(server)) + { + goto retry; + } + smb_unlock_server(server); + return error; + } + fileid = WVAL(buf, smb_vwv0); + smb_unlock_server(server); + + smb_proc_close(server, fileid, CURRENT_TIME); + + return 0; +} + +int +smb_proc_mv(struct inode *odir, const char *oname, const int olen, + struct inode *ndir, const char *nname, const int nlen) +{ + char *p; + struct smb_server *server = SMB_SERVER(odir); + char *buf; + int result; + + smb_lock_server(server); + buf = server->packet; + + retry: + p = smb_setup_header(server, SMBmv, 1, 0); + WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(odir), oname, olen); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(ndir), nname, nlen); + smb_setup_bcc(server, p); + + 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 inode *dir, const char *name, const int len) +{ + char *p; + int result; + struct smb_server *server = SMB_SERVER(dir); + + smb_lock_server(server); + + retry: + p = smb_setup_header(server, SMBmkdir, 0, 0); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), name, len); + smb_setup_bcc(server, p); + + 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 inode *dir, const char *name, const int len) +{ + char *p; + int result; + struct smb_server *server = SMB_SERVER(dir); + + smb_lock_server(server); + + + retry: + p = smb_setup_header(server, SMBrmdir, 0, 0); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), name, len); + smb_setup_bcc(server, p); + + 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 inode *dir, const char *name, const int len) +{ + char *p; + struct smb_server *server = SMB_SERVER(dir); + char *buf; + int result; + + smb_lock_server(server); + buf = server->packet; + + retry: + p = smb_setup_header(server, SMBunlink, 1, 0); + WSET(buf, smb_vwv0, aSYSTEM | aHIDDEN); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), name, len); + smb_setup_bcc(server, p); + + 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; + int result; + + smb_lock_server(server); + buf = server->packet; + + retry: + p = smb_setup_header(server, SMBwrite, 5, 0); + WSET(buf, smb_vwv0, fid); + WSET(buf, smb_vwv1, 0); + DSET(buf, smb_vwv2, length); + WSET(buf, smb_vwv4, 0); + p = smb_encode_ascii(p, "", 0); + smb_setup_bcc(server, p); + + if ((result = smb_request_ok(server, SMBwrite, 1, 0)) < 0) + { + if (smb_retry(server)) + { + goto retry; + } + } + smb_unlock_server(server); + return result; +} + +static void +smb_init_dirent(struct smb_server *server, struct smb_dirent *entry) +{ + memset(entry, 0, sizeof(struct smb_dirent)); + + entry->f_nlink = 1; + entry->f_uid = server->m.uid; + entry->f_gid = server->m.gid; + entry->f_blksize = 512; +} + +static void +smb_finish_dirent(struct smb_server *server, struct smb_dirent *entry) +{ + if ((entry->attr & aDIR) != 0) + { + entry->f_mode = server->m.dir_mode; + entry->f_size = 512; + } else + { + entry->f_mode = server->m.file_mode; + } + + if ((entry->f_blksize != 0) && (entry->f_size != 0)) + { + entry->f_blocks = + (entry->f_size - 1) / entry->f_blksize + 1; + } else + { + entry->f_blocks = 0; + } + return; +} + +void +smb_init_root_dirent(struct smb_server *server, struct smb_dirent *entry) +{ + smb_init_dirent(server, entry); + entry->attr = aDIR; + entry->f_ino = 1; + smb_finish_dirent(server, entry); +} + + +static char * +smb_decode_dirent(struct smb_server *server, char *p, struct smb_dirent *entry) +{ + smb_init_dirent(server, entry); + + p += SMB_STATUS_SIZE; /* reserved (search_status) */ + entry->attr = BVAL(p, 0); + entry->f_mtime = entry->f_atime = entry->f_ctime = + date_dos2unix(WVAL(p, 1), WVAL(p, 3)); + entry->f_size = DVAL(p, 5); + entry->len = strlen(p + 9); + if (entry->len > 12) + { + entry->len = 12; + } + memcpy(entry->name, p + 9, entry->len); + entry->name[entry->len] = '\0'; + while (entry->len > 2) + { + /* Pathworks fills names with spaces */ + entry->len -= 1; + if (entry->name[entry->len] == ' ') + { + entry->name[entry->len] = '\0'; + } + } + switch (server->case_handling) + { + case CASE_UPPER: + str_upper(entry->name); + break; + case CASE_LOWER: + str_lower(entry->name); + break; + default: + break; + } + DPRINTK("smb_decode_dirent: name = %s\n", entry->name); + smb_finish_dirent(server, entry); + 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; + + DPRINTK("SMB call readdir %d @ %d\n", cache_size, fpos); + + smb_lock_server(server); + buf = server->packet; + + retry: + first = 1; + total_count = 0; + current_entry = entry; + + while (1) + { + if (first == 1) + { + p = smb_setup_header(server, SMBsearch, 2, 0); + WSET(buf, smb_vwv0, entries_asked); + WSET(buf, smb_vwv1, aDIR); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), "*.*", 3); + *p++ = 5; + WSET(p, 0, 0); + p += 2; + } else + { + p = smb_setup_header(server, SMBsearch, 2, 0); + WSET(buf, smb_vwv0, entries_asked); + WSET(buf, smb_vwv1, aDIR); + p = smb_encode_ascii(p, "", 0); + *p++ = 5; + WSET(p, 0, SMB_STATUS_SIZE); + p += 2; + memcpy(p, status, SMB_STATUS_SIZE); + p += SMB_STATUS_SIZE; + } + + smb_setup_bcc(server, p); + + 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); + 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(server, 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(struct smb_server *server, char *p, + struct smb_dirent *entry, int level) +{ + char *result; + + smb_init_dirent(server, entry); + + switch (level) + { + /* We might add more levels later... */ + case 1: + entry->len = BVAL(p, 26); + strncpy(entry->name, p + 27, entry->len); + entry->name[entry->len] = '\0'; + entry->f_size = DVAL(p, 16); + entry->attr = BVAL(p, 24); + + entry->f_ctime = date_dos2unix(WVAL(p, 6), WVAL(p, 4)); + entry->f_atime = date_dos2unix(WVAL(p, 10), WVAL(p, 8)); + entry->f_mtime = date_dos2unix(WVAL(p, 14), WVAL(p, 12)); + result = p + 28 + BVAL(p, 26); + break; + + default: + DPRINTK("Unknown long filename format %d\n", level); + result = p + WVAL(p, 0); + } + + switch (server->case_handling) + { + case CASE_UPPER: + str_upper(entry->name); + break; + case CASE_LOWER: + str_lower(entry->name); + break; + default: + break; + } + + smb_finish_dirent(server, entry); + return result; +} + +int +smb_proc_readdir_long(struct smb_server *server, struct inode *dir, int fpos, + int cache_size, struct smb_dirent *cache) +{ + /* NT uses 260, OS/2 uses 2. Both accept 1. */ + const int info_level = 1; + const int max_matches = 512; + + char *p; + char *lastname; + int lastname_len; + int i; + int first, entries, entries_seen; + + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + __u16 command; + + 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; + + char param[SMB_MAXPATHLEN + 2 + 12]; + int mask_len; + unsigned char *mask = &(param[12]); + + mask_len = smb_encode_path(server, mask, + SMB_INOP(dir), "*", 1) - mask; + + mask[mask_len] = 0; + mask[mask_len + 1] = 0; + + DPRINTK("smb_readdir_long cache=%d, fpos=%d, mask=%s\n", + cache_size, fpos, mask); + + smb_lock_server(server); + + retry: + + first = 1; + entries = 0; + entries_seen = 2; + + while (ff_eos == 0) + { + loop_count += 1; + if (loop_count > 200) + { + printk("smb_proc_readdir_long: " + "Looping in FIND_NEXT??\n"); + break; + } + if (first != 0) + { + command = TRANSACT2_FINDFIRST; + WSET(param, 0, aSYSTEM | aHIDDEN | aDIR); + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, 8 + 4 + 2); /* resume required + + close on end + + continue */ + WSET(param, 6, info_level); + DSET(param, 8, 0); + } else + { + command = TRANSACT2_FINDNEXT; + DPRINTK("hand=0x%X resume=%d ff_lastname=%d mask=%s\n", + ff_dir_handle, ff_resume_key, ff_lastname, mask); + WSET(param, 0, ff_dir_handle); + WSET(param, 2, max_matches); /* max count */ + WSET(param, 4, info_level); + DSET(param, 6, ff_resume_key); /* ff_resume_key */ + WSET(param, 10, 8 + 4 + 2); /* resume required + + close on end + + continue */ +#ifdef CONFIG_SMB_WIN95 + /* Windows 95 is not able to deliver answers + to FIND_NEXT fast enough, so sleep 0.2 seconds */ + current->timeout = jiffies + HZ / 5; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; +#endif + } + + result = smb_trans2_request(server, command, + 0, NULL, 12 + mask_len + 2, param, + &resp_data_len, &resp_data, + &resp_param_len, &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 */ + if (first != 0) + { + ff_dir_handle = WVAL(resp_param, 0); + ff_searchcount = WVAL(resp_param, 2); + ff_eos = WVAL(resp_param, 4); + ff_lastname = WVAL(resp_param, 8); + } else + { + ff_searchcount = WVAL(resp_param, 0); + ff_eos = WVAL(resp_param, 2); + ff_lastname = WVAL(resp_param, 6); + } + + if (ff_searchcount == 0) + { + break; + } + /* point to the data bytes */ + p = resp_data; + + /* we might need the lastname for continuations */ + lastname = ""; + lastname_len = 0; + if (ff_lastname > 0) + { + switch (info_level) + { + case 260: + lastname = p + ff_lastname; + lastname_len = resp_data_len - ff_lastname; + ff_resume_key = 0; + break; + case 1: + lastname = p + ff_lastname + 1; + lastname_len = BVAL(p, ff_lastname); + ff_resume_key = 0; + break; + } + } + lastname_len = min(lastname_len, 256); + strncpy(mask, lastname, lastname_len); + mask[lastname_len] = '\0'; + + /* Now we are ready to parse smb directory entries. */ + + for (i = 0; i < ff_searchcount; i++) + { + struct smb_dirent *entry = &(cache[entries]); + + p = smb_decode_long_dirent(server, p, + entry, info_level); + + DDPRINTK("smb_readdir_long: got %s\n", entry->name); + + if ((entry->name[0] == '.') + && ((entry->name[1] == '\0') + || ((entry->name[1] == '.') + && (entry->name[2] == '\0')))) + { + /* ignore . and .. from the server */ + continue; + } + if (entries_seen >= fpos) + { + entry->f_pos = entries_seen; + entries += 1; + } + if (entries >= cache_size) + { + goto finished; + } + entries_seen += 1; + } + + DPRINTK("received %d entries (eos=%d resume=%d)\n", + ff_searchcount, ff_eos, ff_resume_key); + + first = 0; + } + + finished: + smb_unlock_server(server); + return entries; +} + +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 inode *dir, const char *name, int len, + struct smb_dirent *entry) +{ + int result; + char *p; + struct smb_server *server = SMB_SERVER(dir); + char *buf; + + smb_lock_server(server); + buf = server->packet; + + DDPRINTK("smb_proc_getattr: %s\n", name); + + retry: + p = smb_setup_header(server, SMBgetatr, 0, 0); + *p++ = 4; + p = smb_encode_path(server, p, SMB_INOP(dir), name, len); + smb_setup_bcc(server, p); + + 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->f_ctime = entry->f_atime = + entry->f_mtime = local2utc(DVAL(buf, smb_vwv1)); + + entry->f_size = DVAL(buf, smb_vwv3); + smb_unlock_server(server); + return 0; +} + +static int +smb_proc_getattr_trans2(struct inode *dir, const char *name, int len, + struct smb_dirent *entry) +{ + struct smb_server *server = SMB_SERVER(dir); + char param[SMB_MAXPATHLEN + 20]; + char *p; + int result; + + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + DSET(param, 2, 0); + p = smb_encode_path(server, param + 6, SMB_INOP(dir), name, len); + + smb_lock_server(server); + retry: + result = smb_trans2_request(server, TRANSACT2_QPATHINFO, + 0, NULL, p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + + if (server->rcls != 0) + { + smb_unlock_server(server); + return -smb_errno(server->rcls, server->err); + } + if (result < 0) + { + if (smb_retry(server)) + { + goto retry; + } + smb_unlock_server(server); + return result; + } + if (resp_data_len < 22) + { + smb_unlock_server(server); + return -ENOENT; + } + entry->f_ctime = date_dos2unix(WVAL(resp_data, 2), + WVAL(resp_data, 0)); + entry->f_atime = date_dos2unix(WVAL(resp_data, 6), + WVAL(resp_data, 4)); + entry->f_mtime = date_dos2unix(WVAL(resp_data, 10), + WVAL(resp_data, 8)); + entry->f_size = DVAL(resp_data, 12); + entry->attr = WVAL(resp_data, 20); + smb_unlock_server(server); + + return 0; +} + +int +smb_proc_getattr(struct inode *dir, const char *name, int len, + struct smb_dirent *entry) +{ + struct smb_server *server = SMB_SERVER(dir); + int result = 0; + + smb_init_dirent(server, entry); + + if (server->protocol >= PROTOCOL_LANMAN2) + { + result = smb_proc_getattr_trans2(dir, name, len, entry); + } + if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0)) + { + result = smb_proc_getattr_core(dir, name, len, entry); + } + smb_finish_dirent(server, entry); + + entry->len = len; + memcpy(entry->name, name, len); + /* entry->name is null terminated from smb_init_dirent */ + + return result; +} + + +/* In core protocol, there is only 1 time to be set, we use + entry->f_mtime, to make touch work. */ +static int +smb_proc_setattr_core(struct smb_server *server, + struct inode *i, struct smb_dirent *new_finfo) +{ + char *p; + char *buf; + int result; + + smb_lock_server(server); + buf = server->packet; + + retry: + p = smb_setup_header(server, SMBsetatr, 8, 0); + WSET(buf, smb_vwv0, new_finfo->attr); + DSET(buf, smb_vwv1, utc2local(new_finfo->f_mtime)); + *p++ = 4; + p = smb_encode_path(server, p, + SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name, + SMB_INOP(i)->finfo.len); + p = smb_encode_ascii(p, "", 0); + + smb_setup_bcc(server, p); + if ((result = smb_request_ok(server, SMBsetatr, 0, 0)) < 0) + { + if (smb_retry(server)) + { + goto retry; + } + } + smb_unlock_server(server); + return result; +} + +static int +smb_proc_setattr_trans2(struct smb_server *server, + struct inode *i, struct smb_dirent *new_finfo) +{ + char param[SMB_MAXPATHLEN + 20]; + char data[26]; + char *p; + int result; + + unsigned char *resp_data = NULL; + unsigned char *resp_param = NULL; + int resp_data_len = 0; + int resp_param_len = 0; + + WSET(param, 0, 1); /* Info level SMB_INFO_STANDARD */ + DSET(param, 2, 0); + p = smb_encode_path(server, param + 6, + SMB_INOP(i)->dir, SMB_INOP(i)->finfo.name, + SMB_INOP(i)->finfo.len); + + date_unix2dos(new_finfo->f_ctime, &(data[0]), &(data[2])); + date_unix2dos(new_finfo->f_atime, &(data[4]), &(data[6])); + date_unix2dos(new_finfo->f_mtime, &(data[8]), &(data[10])); + DSET(data, 12, new_finfo->f_size); + DSET(data, 16, new_finfo->f_blksize); + WSET(data, 20, new_finfo->attr); + WSET(data, 22, 0); + + smb_lock_server(server); + retry: + result = smb_trans2_request(server, TRANSACT2_SETPATHINFO, + 26, data, p - param, param, + &resp_data_len, &resp_data, + &resp_param_len, &resp_param); + + if (server->rcls != 0) + { + smb_unlock_server(server); + return -smb_errno(server->rcls, server->err); + } + if (result < 0) + { + if (smb_retry(server)) + { + goto retry; + } + } + smb_unlock_server(server); + return 0; +} + +int +smb_proc_setattr(struct smb_server *server, struct inode *inode, + struct smb_dirent *new_finfo) +{ + int result; + + if (server->protocol >= PROTOCOL_LANMAN2) + { + result = smb_proc_setattr_trans2(server, inode, new_finfo); + } + if ((server->protocol < PROTOCOL_LANMAN2) || (result < 0)) + { + result = smb_proc_setattr_core(server, inode, new_finfo); + } + return result; +} + +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; + 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_vfree(server->packet); + server->packet_size = 0; + } + server->packet = smb_vmalloc(max_xmit); + + if (server->packet == NULL) + { + printk("smb_proc_connect: No memory! Bailing out.\n"); + result = -ENOMEM; + goto fail; + } + server->packet_size = 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) + { + DPRINTK("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); + 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++ = 2; + strcpy(p, prots[i].name); + p += strlen(prots[i].name) + 1; + } + + if ((result = smb_request_ok(server, SMBnegprot, 1, -1)) < 0) + { + DPRINTK("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); + DPRINTK("smb_proc_connect: blkmode = %d\n", + WVAL(server->packet, smb_vwv5)); + + if (server->protocol >= PROTOCOL_NT1) + { + server->max_xmit = 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->max_xmit = 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->max_xmit < given_max_xmit) + { + /* We do not distinguish between the client + requests and the server response. */ + given_max_xmit = server->max_xmit; + } + if (server->protocol >= PROTOCOL_NT1) + { + char *workgroup = server->m.domain; + 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->max_xmit = 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); + + if (server->protocol <= PROTOCOL_COREPLUS) + { + word max_xmit; + + p = smb_decode_word(p, &max_xmit); + server->max_xmit = max_xmit; + + if (server->max_xmit > given_max_xmit) + { + server->max_xmit = given_max_xmit; + } + } else + { + p += 2; + } + + 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_vfree(server->packet); + + server->packet = smb_vmalloc(server->max_xmit); + if (server->packet == NULL) + { + printk("smb_proc_connect: No memory left in end of " + "connection phase :-(\n"); + smb_dont_catch_keepalive(server); + goto fail; + } + server->packet_size = server->max_xmit; + + 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_vfree(server->packet); + 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); +} |