Files
amiga-smbfs/source_code/proc.c
T
Olaf Barthel dcef889911 Hide password debug output
This is no information which should be shared when writing debug files.
2016-05-01 10:40:31 +02:00

2961 lines
73 KiB
C

/*
* :ts=4
*
* 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.
* Modified for use with AmigaOS by Olaf Barthel <obarthel -at- gmx -dot- net>
*/
#include "smbfs.h"
#include "quad_math.h"
/*****************************************************************************/
#include <smb/smb.h>
#include <smb/smbno.h>
#include <smb/smb_fs.h>
/*****************************************************************************/
#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
/*****************************************************************************/
static INLINE byte * smb_encode_word(byte *p, word data);
static INLINE byte * smb_decode_word(const byte *p, word *data);
static INLINE byte * smb_decode_dword(const byte *p, dword *data);
static INLINE word smb_bcc(const byte *packet);
static INLINE int smb_verify(const byte *packet, int command, int wct, int bcc);
static byte *smb_encode_dialect(byte *p, const byte *name, int len);
static byte *smb_encode_ascii(byte *p, const byte *name, int len);
static void smb_encode_vblock(byte *p, const byte *data, word len, int unused_fs);
static byte *smb_decode_data(const byte *p, byte *data, word *data_len, int fs);
static byte *smb_name_mangle(byte *p, const byte *name);
static int date_dos2unix(unsigned short time_value, unsigned short date);
static void date_unix2dos(int unix_date, unsigned short *time_value, unsigned short *date);
static INLINE int smb_valid_packet(const byte *packet);
static int smb_errno(int errcls, int error);
static int smb_request_ok(struct smb_server *s, int command, int wct, int bcc);
static int smb_retry(struct smb_server *server);
static int smb_request_ok_unlock(struct smb_server *s, int command, int wct, int bcc);
static byte *smb_setup_header(struct smb_server *server, byte command, word wct, word bcc);
static byte *smb_setup_header_exclusive(struct smb_server *server, byte command, word wct, word bcc);
static int smb_proc_do_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry, word command);
static char *smb_decode_dirent(char *p, struct smb_dirent *entry);
static int smb_proc_readdir_short(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry);
static char *smb_decode_long_dirent(char *p, struct smb_dirent *finfo, int level);
static int smb_proc_readdir_long(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry);
static int smb_proc_reconnect(struct smb_server *server);
static void smb_printerr(int class, int num);
/*****************************************************************************/
/* error code stuff - put together by Merik Karman
merik -at- blackadder -dot- dsh -dot- oz -dot- au */
typedef struct
{
char *name;
int code;
char *message;
} err_code_struct;
/* Dos Error Messages */
static const 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"},
{"ERRnosuchshare", 67, "Share name not found"},
{"ERRfilexists", 80, "The file named in a Create Directory, Make New File or Link request already exists"},
{"ERRpaused", 81, "The server is temporarily paused"},
{"ERRtimeout", 88, "The requested operation on a named pipe or an I/O device has timed out"},
{"ERRnoresource", 89, "No resources currently available for this SMB request"},
{"ERRtoomanyuids", 90, "Too many UIDs active for this SMB connection"},
{"ERRbaduid", 91, "The UID supplied is not known to the session, or the user identified by the UID does not have sufficient privileges"},
{"ERROR_DIRECTORY_NOT_EMPTY", 145, "The directory is not empty"},
{"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"},
{"ERROR_EAS_DIDNT_FIT", 275, "Either there are no extended attributes, or the available extended attributes did not fit into the response"},
{"ERROR_EAS_NOT_SUPPORTED", 282, "The server file system does not support Extended Attributes"},
{NULL, -1, NULL}
};
/* Server Error Messages */
static const 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"},
{"ERRbadPW", 254, "Invalid password"},
{"ERRaccountExpired", 2239, "User account on the target machine is disabled or has expired"},
{"ERRbadClient", 2240, "The client does not have permission to access this server"},
{"ERRbadLogonTime", 2241, "Access to the server is not permitted at this time"},
{"ERRpasswordExpired", 2242, "The user's password has expired"},
{"ERRnosupport", 0xFFFF, "Function not supported"},
{NULL, -1, NULL}
};
/* Hard Error Messages */
static const 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"},
{"ERRdiskfull", 39, "The file system is full"},
{NULL, -1, NULL}
};
typedef struct
{
int code;
char *class;
const err_code_struct *err_msgs;
} err_class_struct;
static const err_class_struct 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 }
};
/*****************************************************************************/
/*****************************************************************************
*
* Encoding/Decoding section
*
*****************************************************************************/
static INLINE byte *
smb_encode_word (byte * p, word data)
{
p[0] = data & 0x00ffU;
p[1] = (data & 0xff00U) >> 8;
return &p[2];
}
static INLINE byte *
smb_decode_word (const byte * p, word * data)
{
(*data) = ((word)p[0]) | (((word)p[1]) << 8);
return (byte *)&p[2];
}
static INLINE byte *
smb_decode_dword (const byte * p, dword * data)
{
(*data) = ((dword)p[0]) | (((dword)p[1]) << 8) | (((dword)p[2]) << 16) | (((dword)p[3]) << 24);
return (byte *)&p[4];
}
byte *
smb_encode_smb_length (byte * p, dword len)
{
/* 0x00 = NetBIOS session message */
p[0] = 0;
/* 0 = reserved */
p[1] = 0;
/* Payload length in network byte order. */
p[2] = (len & 0xFF00) >> 8;
p[3] = (len & 0xFF);
/* Length is actually a 17 bit integer. */
p[1] |= (len >> 16) & 1;
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 void
smb_encode_vblock (byte * p, const byte * data, word len, int unused_fs)
{
(*p++) = 5;
p = smb_encode_word (p, len);
memcpy (p, data, len);
}
static byte *
smb_decode_data (const byte * p, byte * data, word * data_len, int unused_fs)
{
word len;
if (!((*p) == 1 || (*p) == 5))
LOG (("Warning! Data block not starting with 1 or 5\n"));
len = WVAL (p, 1);
p += 3;
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) != '\0')
{
(*p++) = ((*name) >> 4) + 'A';
(*p++) = ((*name) & 0x0F) + 'A';
name++;
}
while (pad-- > 0)
{
(*p++) = 'C';
(*p++) = 'A';
}
(*p++) = '\0';
return p;
}
/* According to the core protocol documentation times are
expressed as seconds past January 1st, 1970, local
time zone. */
static INLINE int
utc2local (int time_value)
{
int result;
if(time_value > 0)
result = time_value - GetTimeZoneDelta();
else
result = time_value;
return result;
}
static INLINE int
local2utc (int time_value)
{
int result;
if(time_value > 0)
result = time_value + GetTimeZoneDelta();
else
result = time_value;
return result;
}
/* Convert a MS-DOS time/date pair to a UNIX date (seconds since January 1st 1970). */
static int
date_dos2unix (unsigned short time_value, unsigned short date)
{
time_t seconds;
struct tm tm;
memset(&tm,0,sizeof(tm));
tm.tm_sec = 2 * (time_value & 0x1F);
tm.tm_min = (time_value >> 5) & 0x3F;
tm.tm_hour = (time_value >> 11) & 0x1F;
tm.tm_mday = date & 0x1F;
tm.tm_mon = ((date >> 5) & 0xF) - 1;
tm.tm_year = ((date >> 9) & 0x7F) + 80;
seconds = MakeTime(&tm);
return(seconds);
}
/* Convert linear UNIX date to a MS-DOS time/date pair. */
static void
date_unix2dos (int unix_date, unsigned short *time_value, unsigned short *date)
{
struct tm tm;
GMTime(unix_date,&tm);
(*time_value) = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec / 2);
(*date) = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
}
/****************************************************************************
*
* Support section.
*
****************************************************************************/
dword
smb_len (const byte * packet)
{
/* This returns the payload length stored in the NetBIOS session header. */
return (dword)( (((dword)(packet[1] & 0x1)) << 16) | (((dword)packet[2]) << 8) | (packet[3]) );
}
static INLINE word
smb_bcc (const byte * packet)
{
int pos = SMB_HEADER_LEN + SMB_WCT (packet) * sizeof (word);
return (word)(packet[pos] | ((word)packet[pos + 1]) << 8);
}
/* smb_valid_packet: We check if packet fulfills the basic
requirements of a smb packet */
static INLINE int
smb_valid_packet (const byte * packet)
{
int result;
LOG (("len: %ld, wct: %ld, bcc: %ld -- SMB_HEADER_LEN[%ld] + SMB_WCT (packet) * 2[%ld] + SMB_BCC (packet)[%ld] + 2 = %ld\n",
smb_len (packet),
SMB_WCT (packet),
SMB_BCC (packet),
SMB_HEADER_LEN,
SMB_WCT (packet) * 2,
SMB_BCC (packet),
SMB_HEADER_LEN + SMB_WCT (packet) * 2 + SMB_BCC (packet) + 2));
/* olsen: During protocol negotiation Samba 3.2.5 may return SMB responses which contain
more data than is called for. I'm not sure what the right approach to handle
this would be. But for now, I've modified this test to check only if there
is less data in a response than required. */
result = (packet[4] == 0xff
&& packet[5] == 'S'
&& packet[6] == 'M'
&& packet[7] == 'B'
&& (smb_len (packet) + 4 >= (dword)
(SMB_HEADER_LEN + SMB_WCT (packet) * 2 + SMB_BCC (packet) + 2))) ? 0 : (-EIO);
return result;
}
/* 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 INLINE int
smb_verify (const 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)
{
int result,i;
if (errcls)
smb_printerr (errcls, error);
if (errcls == ERRDOS)
{
static const int map[][2] =
{
{ ERRbadfunc,EINVAL },
{ ERRbadfile,ENOENT },
{ ERRbadpath,ENOENT },
{ ERRnofids,EMFILE },
{ ERRnoaccess,EACCES },
{ ERRbadfid,EBADF },
{ ERRbadmcb,EIO },
{ ERRnomem,ENOMEM },
{ ERRbadmem,EFAULT },
{ ERRbadenv,EIO },
{ ERRbadformat,EIO },
{ ERRbadaccess,EACCES },
{ ERRbaddata,E2BIG },
{ ERRbaddrive,ENXIO },
{ ERRremcd,EIO },
{ ERRdiffdevice,EXDEV },
{ ERRnofiles,0 },
{ ERRbadshare,ETXTBSY },
{ ERRlock,EDEADLK },
{ ERRfilexists,EEXIST },
{ 87,0 },/* Unknown error! */
{ 145,ENOTEMPTY},/* Directory is not empty; this is what Samba reports (2016-04-23) */
{ 183,EEXIST },/* This next error seems to occur on an mv when the destination exists ("object name collision") */
{ -1,-1 }
};
result = EIO;
for(i = 0 ; map[i][0] != -1 ; i++)
{
if(map[i][0] == error)
{
result = map[i][1];
break;
}
}
}
else if (errcls == ERRSRV)
{
static const int map[][2] =
{
{ ERRerror,ENFILE },
{ ERRbadpw,EINVAL },
{ ERRbadtype,EIO },
{ ERRaccess,EACCES },
{ -1, -1 }
};
result = EIO;
for(i = 0 ; map[i][0] != -1 ; i++)
{
if(map[i][0] == error)
{
result = map[i][1];
break;
}
}
}
else if (errcls == ERRHRD)
{
static const int map[][2] =
{
{ ERRnowrite,EROFS },
{ ERRbadunit,ENODEV },
{ ERRnotready,EBUSY },
{ ERRbadcmd,EIO },
{ ERRdata,EIO },
{ ERRbadreq,ERANGE },
{ ERRbadshare,ETXTBSY },
{ ERRlock,EDEADLK },
{ -1, -1 }
};
result = EIO;
for(i = 0 ; map[i][0] != -1 ; i++)
{
if(map[i][0] == error)
{
result = map[i][1];
break;
}
}
}
else if (errcls == ERRCMD)
{
result = EIO;
}
else
{
result = 0;
}
return(result);
}
#if DEBUG
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] | ((int)packet[12]) << 8);
LOG (("smb_len = %ld valid = %ld\n", len = smb_len (packet), smb_valid_packet (packet)));
LOG (("smb_cmd = %ld smb_wct = %ld smb_bcc = %ld\n", packet[8], SMB_WCT (packet), SMB_BCC (packet)));
LOG (("smb_rcls = %ld smb_err = %ld\n", errcls, error));
if (errcls)
smb_printerr (errcls, error);
if (len > 100)
len = 100;
PRINTHEADER();
for (i = 0; i < len; i += 10)
{
PRINTF (("%03ld:", i));
for (j = i; j < i + 10; j++)
{
if (j < len)
PRINTF (("%02lx ", packet[j]));
else
PRINTF ((" "));
}
PRINTF ((": "));
for (j = i; j < i + 10; j++)
{
if (j < len)
PRINTF (("%lc", print_char (packet[j])));
}
PRINTF (("\n"));
}
}
#endif /* DEBUG */
/* 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;
int error;
s->rcls = 0;
s->err = 0;
result = smb_request (s);
if (result < 0)
{
LOG (("smb_request failed\n"));
}
else if ((error = smb_valid_packet (s->packet)) != 0)
{
LOG (("not a valid packet!\n"));
result = error;
}
else if (s->rcls != 0)
{
result = -smb_errno (s->rcls, s->err);
#if DEBUG
{
const err_class_struct * err_class = NULL;
const err_code_struct * err_code = NULL;
int i;
for (i = 0; err_classes[i].class; i++)
{
if (err_classes[i].code == s->rcls)
{
err_class = &err_classes[i];
if(err_class->err_msgs != NULL)
{
const err_code_struct * err = err_class->err_msgs;
int j;
for (j = 0; err[j].name; j++)
{
if(err[j].code == s->err)
{
err_code = &err[j];
break;
}
}
}
break;
}
}
if(err_class != NULL && err_code != NULL)
{
LOG(("translated error code %ld/%ld (%s/%s) to %ld (%s)\n",
s->rcls,s->err,err_class->class,err_code->message,(-result),strerror(-result)));
}
else
{
LOG(("no proper translation for error code %ld/%ld to %ld (%s)\n",
s->rcls,s->err,(-result),strerror(-result)));
}
}
#endif /* DEBUG */
}
else if ((error = smb_verify (s->packet, command, wct, bcc)) != 0)
{
LOG (("smb_verify failed\n"));
result = error;
}
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)
{
int result = 0;
if (server->state == CONN_VALID)
goto out;
if (smb_release (server) < 0)
{
LOG (("smb_retry: smb_release failed\n"));
server->state = CONN_RETRIED;
goto out;
}
if (smb_proc_reconnect (server) < 0)
{
LOG (("smb_proc_reconnect failed\n"));
server->state = CONN_RETRIED;
goto out;
}
server->state = CONN_VALID;
result = 1;
out:
return result;
}
static int
smb_request_ok_unlock (struct smb_server *s, int command, int wct, int bcc)
{
int result;
result = smb_request_ok (s, command, wct, bcc);
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;
/* This sets up the NetBIOS session header. 'xmit_length'
* is the amount of data that follows the header.
*/
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);
/* Fixed header length (32 bytes). */
memset (&p[5], 0, 32 - 5);
p += 32;
WSET (buf, smb_tid, server->tid);
WSET (buf, smb_pid, 0); /* server->pid */
WSET (buf, smb_uid, server->server_uid);
WSET (buf, smb_mid, 0); /* server->mid */
if (server->protocol > PROTOCOL_CORE)
{
BSET (buf, smb_flg, 0x8); /* path names are caseless */
WSET (buf, smb_flg2, 0x3); /* extended attributes supported, long names supported */
}
(*p++) = 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)
{
byte * result;
result = smb_setup_header (server, command, wct, bcc);
return result;
}
/*****************************************************************************
*
* 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;
LOG (("path=%s\n", pathname));
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)
goto out;
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;
goto out;
}
}
/* 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 = entry->wtime = local2utc (DVAL (buf, smb_vwv2));
entry->size = DVAL (buf, smb_vwv4);
entry->opened = 1;
#if DEBUG
{
struct tm tm;
GMTime(entry->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
out:
return error;
}
/* smb_proc_close: in finfo->mtime we can send a modification time to
the server */
int
smb_proc_close (struct smb_server *server, word fileid, dword mtime)
{
char *buf = server->packet;
int local_time;
int result;
if(mtime != 0 && mtime != 0xffffffff)
{
/* 0 and 0xffffffff mean: do not set mtime */
local_time = utc2local (mtime);
}
else
{
local_time = mtime;
}
smb_setup_header_exclusive (server, SMBclose, 3, 0);
WSET (buf, smb_vwv0, fileid);
DSET (buf, smb_vwv1, local_time);
result = smb_request_ok_unlock (server, SMBclose, 0, 0);
return result;
}
/* 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 result;
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)
{
result = error;
goto out;
}
returned_count = WVAL (buf, smb_vwv0);
smb_decode_data (SMB_BUF (server->packet), data, &data_len, fs);
if (returned_count != data_len)
{
LOG (("Warning, returned_count != data_len\n"));
LOG (("ret_c=%ld, data_len=%ld\n", returned_count, data_len));
}
else
{
LOG (("ret_c=%ld, data_len=%ld\n", returned_count, data_len));
}
result = data_len;
out:
return result;
}
/* 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;
smb_setup_header_exclusive (server, SMBreadbraw, 8, 0);
WSET (buf, smb_vwv0, finfo->fileid);
DSET (buf, smb_vwv1, offset);
WSET (buf, smb_vwv3, count); /* maxcnt */
WSET (buf, smb_vwv4, 0); /* mincnt */
DSET (buf, smb_vwv5, 0); /* timeout */
WSET (buf, smb_vwv7, 0); /* reserved */
result = smb_request_read_raw (server, data, count);
return result;
}
int
smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data)
{
int res;
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 (p + 2, data, count);
if ((res = smb_request_ok (server, SMBwrite, 1, 0)) >= 0)
res = WVAL (buf, smb_vwv0);
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;
long len;
byte *p;
/* Calculate maximum number of bytes that could be transferred with
* a single SMB_COM_WRITE_RAW packet...
*
* 'max_buffer_size' is the maximum size of a complete SMB message
* including the message header, the parameter and data blocks.
*
* The message header accounts for
* 4(protocol)+1(command)+4(status)+1(flags)+2(flags2)+2(pidhigh)+
* 8(securityfeatures)+2(reserved)+2(tid)+2(pidlow)+2(uid)+2(mid)
* = 32 bytes
*
* The parameters of a SMB_COM_WRITE_RAW command account for
* 1(wordcount)+2(fid)+2(countofbytes)+2(reserved1)+4(offset)+
* 4(timeout)+2(writemode)+4(reserved2)+2(datalength)+2(dataoffset)
* = 25 bytes
*
* The data part of a SMB_COM_WRITE_RAW command account for
* 2(bytecount)+0(pad) = 2 bytes, not including
* the actual payload
*
* This leaves 'max_buffer_size' - 59 for the payload.
*/
if(server->max_raw_size < server->max_buffer_size)
len = server->max_raw_size - 59 - 4;
else
len = server->max_buffer_size - 59 - 4;
/* Number of bytes to write is smaller than the maximum
* number of bytes which may be sent in a single SMB
* message, including parameter and data fields?
*/
if (count <= len)
len = 0; /* Send a zero length SMB_COM_WRITE_RAW message, followed by the raw data. */
else
len = count - len; /* Send some of the data as part of the SMB_COM_WRITE_RAW message, followed by the remaining raw data. */
p = smb_setup_header_exclusive (server, SMBwritebraw, server->protocol > PROTOCOL_COREPLUS ? 12 : 11, len);
WSET (buf, smb_vwv0, finfo->fileid);
DSET (buf, smb_vwv1, count);
DSET (buf, smb_vwv3, offset);
DSET (buf, smb_vwv5, 0); /* timeout */
if(server->write_behind)
WSET (buf, smb_vwv7, 0); /* do not send a final result response. */
else
WSET (buf, smb_vwv7, 1); /* send final result response */
DSET (buf, smb_vwv8, 0); /* reserved */
if (server->protocol > PROTOCOL_COREPLUS)
{
WSET (buf, smb_vwv10, len);
WSET (buf, smb_vwv11, p - smb_base(buf));
}
else
{
WSET (buf, smb_vwv10, 0);
}
if (len > 0)
memcpy (p, data, len);
result = smb_request_ok (server, SMBwritebraw, 1, 0);
LOG (("first request returned %ld\n", result));
if (result < 0)
goto out;
/* ZZZ if smb_request_write_raw() fails then the server may
* not be able to send a response, hence the write operation will
* be stuck.
*/
result = smb_request_write_raw (server, data + len, count - len);
LOG(("raw request returned %ld\n", result));
if (result >= 0)
{
int error;
/* We have to do the checks of smb_request_ok here as well */
if ((error = smb_valid_packet (server->packet)) != 0)
{
LOG (("not a valid packet!\n"));
result = error;
}
else if (server->rcls != 0)
{
LOG (("server error %ld/%ld\n", server->rcls, server->err));
result = -smb_errno (server->rcls, server->err);
}
else if (server->write_behind)
{
/* We just assume success; the next file operation to follow
* will set an error status if something went wrong.
*/
result = (count - len);
}
else
{
int sock_fd = server->mount_data.fd;
/* SMBwritebraw may be followed by the server sending
* several interim update responses of SMBwritebraw,
* which are eventually followed by a final response
* of SMBwritec (complete).
*/
while(SMB_CMD(server->packet) == SMBwritebraw && SMB_BCC(server->packet) == 0 && SMB_WCT(server->packet) == 1)
{
LOG (("interim update\n"));
error = smb_receive (server, sock_fd);
if(error < 0)
{
LOG (("smb_receive() returned %ld\n", error));
result = error;
break;
}
error = smb_valid_packet (server->packet);
if(error != 0)
{
LOG (("not a valid packet\n"));
result = error;
break;
}
if (server->rcls != 0)
{
LOG (("server error %ld/%ld\n", server->rcls, server->err));
result = -smb_errno (server->rcls, server->err);
break;
}
}
/* If everything went fine so far, this should be the
* final packet which concludes the transfer.
*/
if(result >= 0)
{
error = smb_verify (server->packet, SMBwritec, 1, 0);
if (error != 0)
{
LOG (("smb_verify failed\n"));
result = error;
}
}
}
LOG (("bytes written = %ld (expected %ld)\n", result, count - len));
/* If everything went fine, the whole block has been transfered. */
if (result == (count - len))
result = count;
}
out:
return result;
}
int
smb_proc_lseek (struct smb_server *server, struct smb_dirent *finfo, off_t offset, int mode, off_t * new_position_ptr)
{
char *buf = server->packet;
int error;
retry:
smb_setup_header (server, SMBlseek, 4,0);
WSET (buf, smb_vwv0, finfo->fileid);
WSET (buf, smb_vwv1, mode);
DSET (buf, smb_vwv2, offset);
if ((error = smb_request_ok (server, SMBlseek, 1, 0)) < 0)
{
if (smb_retry (server))
goto retry;
}
else
{
(*new_position_ptr) = DVAL(buf, smb_vwv0);
}
return error;
}
/* smb_proc_lockingX: We don't chain any further packets to the initial one */
int
smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct smb_lkrng *locks, int num_entries, int mode, long timeout)
{
int result;
int num_locks, num_unlocks;
char *buf = server->packet;
char *data;
struct smb_lkrng *p;
int i;
num_locks = num_unlocks = 0;
if (mode & 2)
num_unlocks = num_entries;
else
num_locks = num_entries;
retry:
data = smb_setup_header(server, SMBlockingX, 8, num_entries * 10);
BSET (buf, smb_vwv0, 0xFF);
WSET (buf, smb_vwv1, 0);
WSET (buf, smb_vwv2, finfo->fileid);
WSET (buf, smb_vwv3, mode & 1); /* must be WSET() or new oplock level will be random */
DSET (buf, smb_vwv4, timeout);
WSET (buf, smb_vwv6, num_unlocks);
WSET (buf, smb_vwv7, num_locks);
for (i = 0, p = locks; i < num_entries; i++, p++)
{
WSET (data, SMB_LPID_OFFSET(i), 0); /* server->pid */
DSET (data, SMB_LKOFF_OFFSET(i), p->offset);
DSET (data, SMB_LKLEN_OFFSET(i), p->len);
}
if ((result = smb_request_ok (server, SMBlockingX, 0, 0)) < 0)
{
if (smb_retry (server))
goto retry;
}
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;
int local_time;
retry:
p = smb_setup_header (server, command, 3, len + 2);
WSET (buf, smb_vwv0, entry->attr);
local_time = utc2local (entry->ctime);
DSET (buf, smb_vwv1, local_time);
smb_encode_ascii (p, path, len);
if ((error = smb_request_ok (server, command, 1, 0)) < 0)
{
if (smb_retry (server))
goto retry;
goto out;
}
entry->opened = 1;
entry->fileid = WVAL (buf, smb_vwv0);
smb_proc_close (server, entry->fileid, entry->mtime);
out:
return error;
}
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_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;
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;
}
return result;
}
int
smb_proc_mkdir (struct smb_server *server, const char *path, const int len)
{
char *p;
int result;
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;
}
return result;
}
int
smb_proc_rmdir (struct smb_server *server, const char *path, const int len)
{
char *p;
int result;
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;
}
return result;
}
int
smb_proc_unlink (struct smb_server *server, const char *path, const int len)
{
char *p;
char *buf = server->packet;
int result;
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;
}
return result;
}
int
smb_proc_trunc (struct smb_server *server, word fid, dword length)
{
char *p;
char *buf = server->packet;
int result;
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);
result = smb_request_ok (server, SMBwrite, 1, 0);
if (result >= 0)
result = DVAL(buf, smb_vwv0);
return result;
}
static char *
smb_decode_dirent (char *p, struct smb_dirent *entry)
{
size_t name_size;
p += SMB_STATUS_SIZE; /* reserved (search_status) */
entry->attr = BVAL (p, 0);
entry->mtime = entry->atime = entry->ctime = entry->wtime = date_dos2unix (WVAL (p, 1), WVAL (p, 3));
entry->size = DVAL (p, 5);
name_size = 13;
if(name_size > entry->complete_path_size-1)
name_size = entry->complete_path_size-1;
memcpy (entry->complete_path, p + 9, name_size);
entry->complete_path[name_size] = '\0';
LOG (("smb_decode_dirent: path = %s\n", entry->complete_path));
#if DEBUG
{
struct tm tm;
GMTime(entry->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
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, char *path, int fpos, int cache_size, struct smb_dirent *entry)
{
char *p;
char *buf;
int error;
int result = 0;
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_buffer_size - 100) / SMB_DIRINFO_SIZE;
int dirlen = strlen (path);
char * mask;
mask = malloc(dirlen + 4 + 1);
if (mask == NULL)
{
result = (-ENOMEM);
goto out;
}
strcpy (mask, path);
strcat (mask, "\\*.*");
LOG (("SMB call readdir %ld @ %ld\n", cache_size, fpos));
LOG ((" mask = %s\n", mask));
buf = server->packet;
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;
(void) 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);
(void) 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;
LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\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 += 1;
}
total_count += 1;
}
}
unlock_return:
if(mask != NULL)
free(mask);
out:
return result;
}
/*****************************************************************************/
/* Interpret an 8 byte "filetime" structure to a 'time_t'.
* It's originally in "100ns units since jan 1st 1601".
*
* Unlike the Samba implementation of that date conversion
* algorithm this one tries to perform the entire
* calculation using integer operations only.
*/
static time_t
interpret_long_date(char * p)
{
QUAD adjust;
QUAD long_date;
ULONG underflow;
time_t result;
/* Extract the 64 bit time value. */
long_date.Low = DVAL(p,0);
long_date.High = DVAL(p,4);
/* Divide by 10,000,000 to convert the time from 100ns
units into seconds. */
divide_64_by_32(&long_date,10000000,&long_date);
/* Adjust by 369 years (11,644,473,600 seconds) to convert
from the epoch beginning on January 1st 1601 to the one
beginning on January 1st 1970 (the Unix epoch). */
adjust.Low = 0xb6109100;
adjust.High = 0x00000002;
underflow = subtract_64_from_64_to_64(&long_date,&adjust,&long_date);
/* If the result did not produce an underflow or overflow,
return the number of seconds encoded in the least
significant word of the result. */
if(underflow == 0 && long_date.High == 0)
result = long_date.Low;
else
result = 0;
return(result);
}
/*****************************************************************************/
static void
smb_get_dirent_name(char *p,int level,char ** name_ptr,int * len_ptr)
{
switch (level)
{
case 1: /* OS/2 understands this */
(*name_ptr) = p + 27;
(*len_ptr) = strlen(p + 27);
break;
case 2: /* this is what OS/2 uses */
(*name_ptr) = p + 31;
(*len_ptr) = strlen(p + 31);
break;
case 3: /* untested */
(*name_ptr) = p + 33;
(*len_ptr) = strlen(p + 33);
break;
case 4: /* untested */
(*name_ptr) = p + 37;
(*len_ptr) = strlen(p + 37);
break;
case 260: /* NT uses this, but also accepts 2 */
(*name_ptr) = p + 94;
(*len_ptr) = min (DVAL (p+60, 0), SMB_MAXNAMELEN);
break;
default:
(*name_ptr) = NULL;
(*len_ptr) = 0;
break;
}
}
/* 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;
switch (level)
{
case 1: /* OS/2 understands this */
#if DEBUG
{
char buffer[255];
memcpy(buffer,p + 27,sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = '\0';
LOG(("type=%ld, name='%s'\n",level,buffer));
}
#endif /* DEBUG */
if (finfo != NULL)
{
strlcpy (finfo->complete_path, p + 27, finfo->complete_path_size);
finfo->len = strlen (finfo->complete_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));
finfo->wtime = finfo->mtime;
#if DEBUG
{
struct tm tm;
GMTime(finfo->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
}
result = p + 28 + BVAL (p, 26);
break;
case 2: /* this is what OS/2 uses */
#if DEBUG
{
char buffer[255];
memcpy(buffer,p + 31,sizeof(buffer)-1);
buffer[sizeof(buffer)-1] = '\0';
LOG(("type=%ld, name='%s'\n",level,buffer));
}
#endif /* DEBUG */
if (finfo != NULL)
{
strlcpy (finfo->complete_path, p + 31, finfo->complete_path_size);
finfo->len = strlen (finfo->complete_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));
finfo->wtime = finfo->mtime;
#if DEBUG
{
struct tm tm;
GMTime(finfo->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
}
result = p + 32 + BVAL (p, 30);
break;
case 260: /* NT uses this, but also accepts 2 */
#if DEBUG
{
char buffer[255];
int len;
len = min (DVAL (p+60, 0), sizeof(buffer)-1);
memcpy(buffer,p+94,len);
buffer[len] = '\0';
LOG(("type=%ld, name='%s'\n",level,buffer));
}
#endif /* DEBUG */
result = p + WVAL (p, 0);
if (finfo != NULL)
{
int namelen;
time_t swap;
p += 4; /* next entry offset */
p += 4; /* fileindex */
finfo->ctime = interpret_long_date(p);
p += 8;
finfo->atime = interpret_long_date(p);
p += 8;
finfo->wtime = interpret_long_date(p);
p += 8;
finfo->mtime = interpret_long_date(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? */
if(namelen > (int)finfo->complete_path_size-1)
namelen = finfo->complete_path_size-1;
/* If the modification time is not set, try to
substitute the write time for it. */
if(finfo->mtime == 0)
finfo->mtime = finfo->wtime;
/* Swap last modification time and last write time. */
swap = finfo->mtime;
finfo->mtime = finfo->wtime;
finfo->wtime = swap;
memcpy (finfo->complete_path, p, namelen);
finfo->complete_path[namelen] = '\0';
finfo->len = namelen;
#if DEBUG
{
struct tm tm;
GMTime(finfo->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(finfo->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
}
break;
default:
if (finfo != NULL)
{
/* 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->wtime = finfo->atime = 0;
}
LOG (("Unknown long filename format %ld\n", level));
result = p + WVAL (p, 0);
break;
}
return result;
}
static int
smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry)
{
int max_matches = 512; /* this should actually be based on the max_buffer_size value */
/* NT uses 260, OS/2 uses 2. Both accept 1. */
int info_level = server->protocol < PROTOCOL_NT1 ? 1 : 260;
char *p;
int i;
int first;
int total_count = 0;
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 error = 0;
int ff_searchcount;
int ff_eos = 0;
int ff_dir_handle = 0;
int ff_resume_key = 0;
int loop_count = 0;
unsigned char *outbuf = server->packet;
int dirlen = strlen (path) + 2 + 1;
char *mask;
int masklen;
ENTER();
/* ZZZ experimental 'max_matches' adjustment */
/*
if(info_level == 260)
max_matches = server->max_buffer_size / 360;
else
max_matches = server->max_buffer_size / 40;
*/
SHOWVALUE(server->max_buffer_size);
SHOWVALUE(max_matches);
mask = malloc (dirlen);
if (mask == NULL)
{
LOG (("Memory allocation failed\n"));
error = (-ENOMEM);
SHOWVALUE(error);
goto out;
}
strcpy (mask, path);
strcat (mask, "\\*");
masklen = strlen (mask);
LOG (("SMB call lreaddir %ld @ %ld\n", cache_size, fpos));
LOG ((" mask = %s\n", mask));
resp_param = NULL;
resp_data = NULL;
retry:
first = 1;
total_count = 0;
current_entry = entry;
while (ff_eos == 0)
{
loop_count++;
if (loop_count > 200)
{
LOG (("smb_proc_readdir_long: Looping in FIND_NEXT???\n"));
error = -EIO;
SHOWVALUE(error);
break;
}
memset (outbuf, 0, 39);
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, server->max_buffer_size);
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 */
/* ZZZ the following may be unnecessary, because they
* likely represent random data used for alignment
* padding purposes.
*/
(*p++) = 'D';
(*p++) = ' '; /* this was added because OS/2 does it */
if (first != 0)
{
LOG (("first match\n"));
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);
}
else
{
LOG (("next match; ff_dir_handle=0x%lx ff_resume_key=%ld mask='%s'\n", ff_dir_handle, ff_resume_key, 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);
WSET (p, 10, 8 + 4 + 2); /* resume required + close on end + continue */
}
p += 12;
if(masklen > 0)
memcpy (p, mask, masklen);
p += masklen;
(*p++) = 0;
(*p) = 0;
result = smb_trans2_request (server, &resp_data_len, &resp_param_len, &resp_data, &resp_param);
LOG (("smb_proc_readdir_long: smb_trans2_request returns %ld\n", result));
if (result < 0)
{
if (smb_retry (server))
goto retry;
LOG (("smb_proc_readdir_long: got error from trans2_request\n"));
error = result;
SHOWVALUE(error);
break;
}
/* Apparently, there is a bug in Windows 95 and friends which
causes the directory read attempt to fail if you're asking
for too much data too fast... */
if(server->rcls == ERRSRV && server->err == ERRerror)
{
SHOWMSG("ouch; delaying and retrying");
Delay(TICKS_PER_SECOND / 5);
continue;
}
if (server->rcls != 0)
{
LOG (("server->rcls = %ld err = %ld\n",server->rcls, server->err));
error = smb_errno (server->rcls, server->err);
SHOWVALUE(error);
break;
}
/* ZZZ bail out if this is empty. */
if (resp_param == NULL)
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);
}
else
{
ff_searchcount = WVAL (p, 0);
ff_eos = WVAL (p, 2);
}
LOG (("received %ld entries (eos=%ld)\n",ff_searchcount, ff_eos));
if (ff_searchcount == 0)
break;
/* ZZZ bail out if this is empty. */
if (resp_data == NULL)
break;
/* point to the data bytes */
p = resp_data;
/* Now we are ready to parse smb directory entries. */
for (i = 0; i < ff_searchcount; i++)
{
if(i == ff_searchcount - 1)
{
char * last_name;
int len;
ff_resume_key = DVAL(p, 0);
smb_get_dirent_name(p,info_level,&last_name,&len);
if(len > 0)
{
#if DEBUG
{
char buffer[SMB_MAXNAMELEN+1];
memcpy(buffer,last_name,len);
buffer[len] = '\0';
LOG(("last name = '%s'\n",buffer));
}
#endif /* DEBUG */
if(len + 1 > dirlen)
{
D(("increasing mask; old value = %ld new value = %ld",dirlen,len+1));
if(mask != NULL)
free (mask);
dirlen = len + 1;
SHOWVALUE(dirlen);
mask = malloc (dirlen);
if (mask == NULL)
{
LOG (("smb_proc_readdir_long: Memory allocation failed\n"));
error = -ENOMEM;
SHOWVALUE(error);
goto fail;
}
}
memcpy (mask, last_name, len);
mask[len] = '\0';
masklen = len;
}
else
{
masklen = 0;
}
}
if (total_count < fpos)
{
p = smb_decode_long_dirent (p, NULL, info_level);
LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos));
}
else if (total_count >= fpos + cache_size)
{
p = smb_decode_long_dirent (p, NULL, info_level);
LOG (("smb_proc_readdir: skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos));
continue;
}
else
{
p = smb_decode_long_dirent (p, current_entry, info_level);
current_entry += 1;
}
total_count += 1;
}
SHOWVALUE(ff_resume_key);
if (resp_data != NULL)
{
free (resp_data);
resp_data = NULL;
}
if (resp_param != NULL)
{
free (resp_param);
resp_param = NULL;
}
first = 0;
if (ff_searchcount > 0)
loop_count = 0;
}
fail:
/* finished: not needed any more */
if (mask != NULL)
free (mask);
if (resp_data != NULL)
free (resp_data);
if (resp_param != NULL)
free (resp_param);
out:
if(error < 0)
{
RETURN(error);
return(error);
}
else
{
RETURN (total_count - fpos);
return (total_count - fpos);
}
}
int
smb_proc_readdir (struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry)
{
int result;
if (server->protocol >= PROTOCOL_LANMAN2)
result = smb_proc_readdir_long (server, path, fpos, cache_size, entry);
else
result = smb_proc_readdir_short (server, path, fpos, cache_size, entry);
return result;
}
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;
LOG (("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;
goto out;
}
entry->attr = WVAL (buf, smb_vwv0);
/* The server only tells us 1 time */
entry->ctime = entry->atime = entry->mtime = entry->wtime = local2utc (DVAL (buf, smb_vwv1));
entry->size = DVAL (buf, smb_vwv3);
#if DEBUG
{
struct tm tm;
GMTime(entry->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
out:
return result;
}
/* 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)
goto out;
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->wtime = entry->mtime;
entry->size = DVAL (buf, smb_vwv6);
entry->attr = WVAL (buf, smb_vwv10);
#if DEBUG
{
struct tm tm;
GMTime(entry->ctime,&tm);
LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->atime,&tm);
LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->mtime,&tm);
LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
GMTime(entry->wtime,&tm);
LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec));
}
#endif /* DEBUG */
out:
return result;
}
/* 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;
int local_time;
retry:
LOG (("smb_proc_setattr_core\n"));
p = smb_setup_header (server, SMBsetatr, 8, 4 + len);
WSET (buf, smb_vwv0, new_finfo->attr);
local_time = utc2local (new_finfo->mtime);
DSET (buf, smb_vwv1, local_time);
p = smb_encode_ascii (p, path, len);
(void) smb_encode_ascii (p, "", 0);
if ((result = smb_request_ok (server, SMBsetatr, 0, 0)) < 0)
{
if (smb_retry (server))
goto retry;
}
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_value;
int result;
LOG (("smb_proc_setattrE\n"));
smb_setup_header_exclusive (server, SMBsetattrE, 7, 0);
WSET (buf, smb_vwv0, fid);
date_unix2dos (new_entry->ctime, &time_value, &date);
WSET (buf, smb_vwv1, date);
WSET (buf, smb_vwv2, time_value);
date_unix2dos (new_entry->atime, &time_value, &date);
WSET (buf, smb_vwv3, date);
WSET (buf, smb_vwv4, time_value);
date_unix2dos (new_entry->mtime, &time_value, &date);
WSET (buf, smb_vwv5, date);
WSET (buf, smb_vwv6, time_value);
result = smb_request_ok_unlock (server, SMBsetattrE, 0, 0);
return result;
}
int
smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr)
{
int error;
char *p;
retry:
smb_setup_header (server, SMBdskattr, 0, 0);
if ((error = smb_request_ok (server, SMBdskattr, 5, 0)) < 0)
{
if (smb_retry (server))
goto retry;
goto out;
}
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);
(void) smb_decode_word (p, &attr->free);
out:
return error;
}
/*****************************************************************************
*
* 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. */
static int
smb_proc_reconnect (struct smb_server *server)
{
static const struct smb_prots prots[] =
{
{PROTOCOL_CORE, "PC NETWORK PROGRAM 1.0"}, /* Core Protocol */
{PROTOCOL_COREPLUS, "MICROSOFT NETWORKS 1.03"}, /* CorePlus */
{PROTOCOL_LANMAN1, "MICROSOFT NETWORKS 3.0"}, /* DOS LAN Manager 1.0 */
{PROTOCOL_LANMAN1, "LANMAN1.0"}, /* LAN Manager 1.0 */
{PROTOCOL_LANMAN2, "LM1.2X002"}, /* LAN Manager 2.0 */
{PROTOCOL_NT1, "NT LM 0.12"}, /* NT LAN Manager */
{PROTOCOL_NT1, "NT LANMAN 1.0"},
};
const int num_prots = sizeof(prots) / sizeof(prots[0]);
const char dev[] = "A:";
int i, plength;
const int default_max_buffer_size = 1024; /* Space needed for first request. */
int given_max_xmit = server->mount_data.given_max_xmit;
int result;
word dialect_index;
byte *p;
unsigned char password[24];
int password_len;
unsigned char nt_password[24];
int nt_password_len;
unsigned char full_share[SMB_MAXNAMELEN+1];
int full_share_len;
byte *packet;
dword max_buffer_size;
/* Reception buffer size (buffer is allocated below) is always as large as the
* maximum transmission buffer size, and could be larger if the transmission
* buffer size is smaller than 8000 bytes.
*/
if (server->max_recv <= 0)
server->max_recv = given_max_xmit < 8000 ? 8000 : given_max_xmit;
if ((result = smb_connect (server)) < 0) /* this is really a plain connect() call */
{
LOG (("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)
free (server->packet);
server->packet = malloc (server->max_recv);
if (server->packet == NULL)
{
LOG (("smb_proc_connect: No memory! Bailing out.\n"));
result = -ENOMEM;
goto fail;
}
packet = server->packet;
server->max_buffer_size = default_max_buffer_size;
LOG (("server max buffer size set to %ld\n", default_max_buffer_size));
/* Prepend a NetBIOS header? */
if(!server->raw_smb)
{
/* Start with an RFC1002 session request packet. */
p = packet + 4;
p = smb_name_mangle (p, server->mount_data.server_name);
p = smb_name_mangle (p, server->mount_data.client_name);
smb_encode_smb_length (packet, (byte *) p - (byte *) (packet));
packet[0] = 0x81; /* SESSION REQUEST */
if ((result = smb_request (server)) < 0)
{
LOG (("smb_proc_connect: Failed to send SESSION REQUEST.\n"));
goto fail;
}
if (packet[0] != 0x82)
{
LOG (("smb_proc_connect: Did not receive positive response (err = %lx)\n",packet[0]));
#if DEBUG
{
smb_dump_packet (packet);
}
#endif /* DEBUG */
result = -EIO;
goto fail;
}
LOG (("smb_proc_connect: Passed SESSION REQUEST.\n"));
}
/* Now we are ready to send a SMB Negotiate Protocol packet. */
plength = 0;
for (i = 0; i < num_prots ; i++)
plength += strlen (prots[i].name) + 2;
smb_setup_header (server, SMBnegprot, 0, plength);
p = SMB_BUF (packet);
for (i = 0; i < num_prots ; i++)
p = smb_encode_dialect (p, prots[i].name, strlen (prots[i].name));
LOG (("smb_proc_connect: Request SMBnegprot...\n"));
if ((result = smb_request_ok (server, SMBnegprot, 1, -1)) < 0)
{
LOG (("smb_proc_connect: Failure requesting SMBnegprot\n"));
goto fail;
}
LOG (("Verified!\n"));
p = SMB_VWV (packet);
p = smb_decode_word (p, &dialect_index);
/* If the server does not support any of the listed
* dialects, ist must return a dialect index of 0xFFFF.
*/
if(dialect_index > num_prots || dialect_index == 0xFFFFU)
{
LOG (("smb_proc_connect: Unsupported dialect\n"));
result = -EACCES;
goto fail;
}
server->protocol = prots[dialect_index].prot;
LOG (("smb_proc_connect: Server wants %s protocol.\n",prots[dialect_index].name));
if (server->protocol > PROTOCOL_LANMAN1)
{
int user_len = strlen (server->mount_data.username)+1;
dword server_sesskey;
LOG (("smb_proc_connect: password = %s\n",server->mount_data.password));
LOG (("smb_proc_connect: usernam = %s\n",server->mount_data.username));
LOG (("smb_proc_connect: blkmode = %ld\n",WVAL (packet, smb_vwv5)));
/* NT LAN Manager or newer. */
if (server->protocol >= PROTOCOL_NT1)
{
server->security_mode = (*p++);
/* Skip "max mpx count" (1 word) and "max number vcs" (1 word). */
p += 2 * sizeof(word);
p = smb_decode_dword(p, &max_buffer_size);
LOG (("max_buffer_size = %ld\n", max_buffer_size));
p = smb_decode_dword(p, &server->max_raw_size);
p = smb_decode_dword(p, &server_sesskey);
p = smb_decode_dword(p, &server->capabilities);
/* Skip "system time" (1 qword) and "server time zone" (1 word). */
p += 2 * sizeof(dword) + sizeof(word);
server->crypt_key_length = (*p++);
memcpy(server->crypt_key,SMB_BUF(packet),server->crypt_key_length);
}
/* LAN Manager 2.0 or older */
else
{
word blkmode;
server->security_mode = BVAL(packet, smb_vwv1);
max_buffer_size = WVAL (packet, smb_vwv2);
LOG (("max_buffer_size = %ld\n", max_buffer_size));
/* Maximum raw read/write size is fixed to 65535 bytes. */
server->max_raw_size = 65535;
blkmode = WVAL (packet, smb_vwv5);
server_sesskey = DVAL (packet, smb_vwv6);
/* Crypt key size is fixed to 8 bytes. */
server->crypt_key_length = 8;
memcpy(server->crypt_key,SMB_BUF(packet),server->crypt_key_length);
/* We translate this into capabilities. According to the
LAN Manager 1.x/2.0 documentation both bits 0+1 being set
means the same thing as CAP_RAW_MODE being set. */
if((blkmode & 3) == 3)
server->capabilities = CAP_RAW_MODE;
}
SHOWVALUE(server->security_mode);
if(server->security_mode & NEGOTIATE_ENCRYPT_PASSWORDS)
{
SHOWMSG("encrypted passwords required");
memset(password,0,sizeof(password));
strlcpy(password,server->mount_data.password,sizeof(password));
smb_encrypt(password,server->crypt_key,password);
password_len = 24;
/*
PRINTHEADER();
PRINTF(("password: "));
for(i = 0 ; i < 24 ; i++)
PRINTF(("%02lx ",password[i]));
PRINTF(("\n"));
*/
memset(nt_password,0,sizeof(nt_password));
strlcpy(nt_password,server->mount_data.password,sizeof(nt_password));
smb_nt_encrypt(nt_password,server->crypt_key,nt_password);
nt_password_len = 24;
/*
PRINTHEADER();
PRINTF(("nt_password: "));
for(i = 0 ; i < 24 ; i++)
PRINTF(("%02lx ",nt_password[i]));
PRINTF(("\n"));
*/
/*
PRINTHEADER();
PRINTF(("crypt_key: "));
for(i = 0 ; i < server->crypt_key_length ; i++)
PRINTF(("%02lx ",server->crypt_key[i]));
PRINTF(("\n"));
*/
}
else
{
SHOWMSG("plain text passwords sufficient");
password_len = strlen(server->mount_data.password)+1;
nt_password_len = 0;
}
/* If in share level security then don't send a password now */
if((server->security_mode & NEGOTIATE_USER_SECURITY) == 0)
{
SHOWMSG("share level security; zapping passwords");
strcpy(password,"");
password_len = 0;
strcpy(nt_password,"");
nt_password_len = 0;
}
SHOWVALUE(password_len);
SHOWVALUE(nt_password_len);
LOG (("smb_proc_connect: workgroup = %s\n", server->mount_data.workgroup_name));
/* NT LAN Manager or newer. */
if (server->protocol >= PROTOCOL_NT1)
{
char *OS_id = "AmigaOS";
char *client_id = "smbfs";
SHOWMSG("server->protocol >= PROTOCOL_NT1");
smb_setup_header (server, SMBsesssetupX, 13, user_len + password_len + nt_password_len + strlen (server->mount_data.workgroup_name)+1 + strlen (OS_id)+1 + strlen (client_id)+1);
WSET (packet, smb_vwv0, 0xff);
WSET (packet, smb_vwv2, given_max_xmit);
WSET (packet, smb_vwv3, 2);
WSET (packet, smb_vwv4, 0); /* server->pid */
DSET (packet, smb_vwv5, server_sesskey);
WSET (packet, smb_vwv7, password_len);
WSET (packet, smb_vwv8, nt_password_len);
DSET (packet, smb_vwv9, 0); /* reserved */
DSET (packet, smb_vwv11, server->capabilities & 0x00800000); /* capabilities: Unix support. */
p = SMB_BUF (packet);
if(nt_password_len != 0)
{
SHOWMSG("adding encrypted passwords");
memcpy (p, password, password_len);
p += password_len;
memcpy (p, nt_password, nt_password_len);
p += nt_password_len;
}
else
{
SHOWMSG("adding plain text password");
memcpy (p, server->mount_data.password, password_len);
p += password_len;
}
memcpy (p, server->mount_data.username, user_len);
p += user_len;
strcpy (p, server->mount_data.workgroup_name);
p += strlen (p) + 1;
strcpy (p, OS_id);
p += strlen (p) + 1;
strcpy (p, client_id);
}
/* LAN Manager 2.0 or older */
else
{
smb_setup_header (server, SMBsesssetupX, 10, user_len + password_len);
WSET (packet, smb_vwv0, 0xff); /* No further ANDX command */
WSET (packet, smb_vwv1, 0); /* ANDX offset = 0 */
WSET (packet, smb_vwv2, given_max_xmit); /* maximum buffer size */
WSET (packet, smb_vwv3, 2); /* maximum mpx count; should be copied from server */
WSET (packet, smb_vwv4, 0); /* server->pid */
DSET (packet, smb_vwv5, server_sesskey);
WSET (packet, smb_vwv7, password_len); /* case sensitive password length */
WSET (packet, smb_vwv8, 0); /* offset to encrypted password */
p = SMB_BUF (packet);
memcpy (p, server->mount_data.password, password_len);
p += password_len;
memcpy (p, server->mount_data.username, user_len);
}
if ((result = smb_request_ok (server, SMBsesssetupX, 3, 0)) < 0)
{
LOG (("smb_proc_connect: SMBsessetupX failed\n"));
goto fail;
}
smb_decode_word (packet + 32, &server->server_uid);
}
else
{
max_buffer_size = 0;
server->capabilities = 0;
password_len = strlen(server->mount_data.password)+1;
nt_password_len = 0;
}
if(nt_password_len > 0)
{
strlcpy(full_share,"//",sizeof(full_share));
strlcat(full_share,server->mount_data.server_name,sizeof(full_share));
strlcat(full_share,"/",sizeof(full_share));
strlcat(full_share,server->mount_data.service,sizeof(full_share));
full_share_len = strlen(full_share);
for(i = 0 ; i < full_share_len ; i++)
{
if(full_share[i] == '/')
full_share[i] = '\\';
}
StringToUpper(full_share);
SHOWSTRING(full_share);
smb_setup_header (server, SMBtconX, 4, password_len + full_share_len+1 + strlen(dev)+1);
WSET (packet, smb_vwv0, 0xFF);
WSET (packet, smb_vwv3, password_len);
p = SMB_BUF (packet);
if(nt_password_len > 0)
memcpy(p,password,password_len);
else
memcpy (p, server->mount_data.password, password_len);
p += password_len;
memcpy(p,full_share,full_share_len+1);
p += full_share_len+1;
strcpy(p,dev);
if ((result = smb_request_ok (server, SMBtconX, 3, 0)) < 0)
{
SHOWVALUE(SMB_WCT(packet));
LOG (("smb_proc_connect: SMBtconX not verified.\n"));
goto fail;
}
SHOWVALUE(SMB_WCT(packet));
/* Changed, max_buffer_size hasn't been updated if a tconX message was send instead of tcon. */
if (max_buffer_size != 0)
{
server->max_buffer_size = max_buffer_size;
LOG (("server max buffer size set to %ld\n", max_buffer_size));
}
server->tid = WVAL(packet,smb_tid);
}
else
{
word decoded_max_xmit;
/* Fine! We have a connection, send a tcon message. */
smb_setup_header (server, SMBtcon, 0, 6 + strlen (server->mount_data.service) + strlen (server->mount_data.password) + strlen (dev));
p = SMB_BUF (packet);
p = smb_encode_ascii (p, server->mount_data.service, strlen (server->mount_data.service));
p = smb_encode_ascii (p, server->mount_data.password, strlen (server->mount_data.password));
(void) smb_encode_ascii (p, dev, strlen (dev));
if ((result = smb_request_ok (server, SMBtcon, 2, 0)) < 0)
{
LOG (("smb_proc_connect: SMBtcon not verified.\n"));
goto fail;
}
LOG (("OK! Managed to set up SMBtcon!\n"));
p = SMB_VWV (packet);
p = smb_decode_word (p, &decoded_max_xmit);
server->max_buffer_size = decoded_max_xmit;
LOG (("server max buffer size set to %ld\n", decoded_max_xmit));
SHOWVALUE(server->max_buffer_size);
/* Added by Brian Willette - We were ignoring the server's initial
maxbuf value */
if (max_buffer_size != 0 && server->max_buffer_size > max_buffer_size)
{
server->max_buffer_size = max_buffer_size;
LOG (("server max buffer size set to %ld\n", max_buffer_size));
}
(void) smb_decode_word (p, &server->tid);
}
SHOWVALUE(server->max_buffer_size);
/* Ok, everything is fine. max_buffer_size does not include
the SMB session header of 4 bytes. */
if (server->max_buffer_size < 65535 - 4)
{
server->max_buffer_size += 4;
LOG (("server max buffer size set to %ld\n", server->max_buffer_size));
}
/* Changed, max_buffer_size hasn't been updated if a tconX message was send instead of tcon. */
if (server->max_buffer_size > given_max_xmit)
{
server->max_buffer_size = given_max_xmit;
LOG (("server max buffer size set to %ld\n", server->max_buffer_size));
}
LOG (("max_buffer_size = %ld, tid = %ld\n", server->max_buffer_size, server->tid));
LOG (("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_buffer_size bytes if and only if we return >= 0 */
int
smb_proc_connect (struct smb_server *server)
{
int result;
result = smb_proc_reconnect (server);
if ((result < 0) && (server->packet != NULL))
{
free (server->packet);
server->packet = NULL;
}
return result;
}
static void
smb_printerr (int class, int num)
{
const err_code_struct *err;
int i, j;
for (i = 0; err_classes[i].class; i++)
{
if (err_classes[i].code != class)
continue;
if (!err_classes[i].err_msgs)
{
ReportError("%s - %ld.", err_classes[i].class, num);
LOG (("%s - %ld\n", err_classes[i].class, num));
return;
}
err = (err_code_struct *)err_classes[i].err_msgs;
for (j = 0; err[j].name; j++)
{
if (num != err[j].code)
continue;
ReportError ("%s - %s (%s).", err_classes[i].class, err[j].name, err[j].message);
LOG (("%s - %s (%s)\n",err_classes[i].class, err[j].name,err[j].message));
return;
}
}
ReportError ("Unknown error - (%ld, %ld).", class, num);
LOG (("Unknown error - (%ld, %ld)\n", class, num));
}