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