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