Files
amiga-smbfs/source_code/proc.c
T
obarthel 31c8fb0d77 Updated to version 1.146
Please keep in mind that this is still a development version and might surprise you (not necessarily in a good way).

Do not let me discourage you to build and test this version, although there will be some risks involved such as data corruption or loss of data.
2018-06-03 11:36:04 +02:00

4375 lines
110 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 "errors.h"
/*****************************************************************************/
#include <smb/smb.h>
#include <smb/smbno.h>
#include <smb/smb_fs.h>
/*****************************************************************************/
#include "smbfs_rev.h"
/*****************************************************************************/
/* smbfs uses the following commands:
*
* <smbno.h> CIFS protocol documentation Number Protocol Status Purpose (-> Replacement)
* SMBclose SMB_COM_CLOSE 0x04 Core Protocol - close file
* SMBcreate SMB_COM_CREATE 0x03 Core Protocol deprecated create file (-> SMB_COM_NT_CREATE_ANDX)
* SMBdskattr SMB_COM_QUERY_INFORMATION_DISK 0x80 Core Protocol deprecated get disk attributes (-> SMB_COM_TRANSACTION2+TRANS2_QUERY_FS_INFORMATION)
* SMBgetatr SMB_COM_QUERY_INFORMATION 0x08 Core Protocol deprecated get file attributes (-> SMB_COM_TRANSACTION2+TRANS2_QUERY_PATH_INFORMATION)
* SMBgetattrE SMB_COM_QUERY_INFORMATION2 0x23 LAN Manager 1.0 deprecated get file attributes expanded (-> SMB_COM_TRANSACTION2+TRANS2_QUERY_PATH_INFORMATION)
* SMBlockingX SMB_COM_LOCKING_ANDX 0x24 LAN Manager 1.0 - lock/unlock byte ranges and X
* SMBlseek SMB_COM_SEEK 0x12 Core Protocol obsolete seek
* SMBmkdir SMB_COM_CREATE_DIRECTORY 0x00 Core Protocol deprecated create directory (-> SMB_COM_TRANSACTION2+TRANS2_CREATE_DIRECTORY)
* SMBmv SMB_COM_RENAME 0x07 Core Protocol - rename file
* SMBnegprot SMB_COM_NEGOTIATE 0x72 Core Protocol - negotiate protocol
* SMBopen SMB_COM_OPEN 0x02 Core Protocol deprecated open file (-> SMB_COM_NT_CREATE_ANDX)
* SMBread SMB_COM_READ 0x0A Core Protocol deprecated read from file (-> SMB_COM_READ_ANDX)
* SMBreadbraw SMB_COM_READ_RAW 0x1a Core Protocol deprecated read a block of data with no smb header (-> SMB_COM_READ_ANDX)
* SMBreadX SMB_COM_READ_ANDX 0x2E LAN Manager 1.0 - read and X
* SMBrmdir SMB_COM_DELETE_DIRECTORY 0x01 Core Protocol - delete directory
* SMBsearch SMB_COM_SEARCH 0x81 Core Protocol deprecated search directory (-> SMB_COM_TRANSACTION2+TRANS2_FIND_FIRST2)
* SMBsesssetupX SMB_COM_SESSION_SETUP_ANDX 0x73 LAN Manager 1.0 - Session Set Up & X (including User Logon)
* SMBsetatr SMB_COM_SET_INFORMATION 0x09 Core Protocol deprecated set file attributes (-> SMB_COM_TRANSACTION2+TRANS2_SET_PATH_INFORMATION)
* SMBsetattrE SMB_COM_SET_INFORMATION2 0x22 LAN Manager 1.0 deprecated set file attributes expanded (-> SMB_COM_TRANSACTION2+TRANS2_SET_PATH_INFORMATION)
* SMBtcon SMB_COM_TREE_CONNECT 0x70 Core Protocol deprecated tree connect (-> SMB_COM_TREE_CONNECT_ANDX)
* SMBtconX SMB_COM_TREE_CONNECT_ANDX 0x75 LAN Manager 1.0 - tree connect and X
* SMBtrans2 SMB_COM_TRANSACTION2 0x32 LAN Manager 1.2 - TRANS2 protocol set
* SMBunlink SMB_COM_DELETE 0x06 Core Protocol - delete file
* SMBwrite SMB_COM_WRITE 0x0B Core Protocol deprecated write to file (-> SMB_COM_WRITE_ANDX)
* SMBwritebraw SMB_COM_WRITE_RAW 0x1d LAN Manager 1.0 deprecated write a block of data with no smb header (-> SMB_COM_WRITE_ANDX)
* SMBwriteX SMB_COM_WRITE_ANDX 0x2F LAN Manager 1.0 - write and X
*/
/*****************************************************************************/
/* Byte offsets into the packet buffer reference the following data
* (with the first four octets used by the NetBIOS session header):
*
* 0: WORD netbios_session[2] = NetBIOS session header
* 4: BYTE smb_idf[4] = contains 0xFF, 'SMB'
* 8: BYTE smb_com = command code
* 9: BYTE smb_rcls = error code class
* 10: BYTE smb_reh = reserved (contains AH if DOS INT-24 ERR)
* 11: WORD smb_err = error code
* 13: BYTE smb_reb = reserved
* 14: WORD smb_res[7] = reserved
* 28: WORD smb_tid = tree id #
* 30: WORD smb_pid = caller's process id #
* 32: WORD smb_uid = user id #
* 34: WORD smb_mid = mutiplex id #
* 36: BYTE smb_wct = count of parameter words
* 37: WORD smb_vwv[] = variable # words of params
* 39: WORD smb_bcc = # bytes of data following
* 41: BYTE smb_data[] = data bytes
*/
#define SMB_VWV(packet) (&(packet)[37])
#define SMB_CMD(packet) ((packet)[8])
#define SMB_WCT(packet) ((packet)[36])
#define SMB_BCC(packet) smb_bcc(packet)
#define SMB_BUF(packet) ((packet) + 37 + SMB_WCT(packet) * sizeof(word) + sizeof(word))
/*****************************************************************************/
#define SMB_DIRINFO_SIZE 43
#define SMB_STATUS_SIZE 21
/*****************************************************************************/
/* Some message size calculations include the size of the NetBIOS session
* header, which may not be necessary. The "message size" in question is not
* the same as the underlying transport layer, which in this case is
* NetBIOS over TCP.
*/
#define NETBIOS_HEADER_SIZE 4
/*****************************************************************************/
/* This is for testing if the message exchange still works if the
* server claims to accept less than 65535 bytes per message.
*/
/*#define OVERRIDE_SERVER_MAX_BUFFER_SIZE 16644*/
/*****************************************************************************/
static void smb_printerr(int class, int num);
static int smb_proc_reconnect (struct smb_server *server, int * error_ptr);
/*****************************************************************************/
/* error code stuff - put together by Merik Karman
merik -at- blackadder -dot- dsh -dot- oz -dot- au */
typedef struct
{
const char * name;
int code;
const 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"},
{"ERRinvalidparam", 87, "One of the requested values is out of range"},
{"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"},
{"OBJECT_NAME_COLLISION", 183, "Object name collision"},
{"ERRinvalidname", 123, "Invalid name"},
{"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, "A problem has occured in the physical I/O"},
{"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;
const 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 }
};
/*****************************************************************************/
static time_t convert_long_date_to_time_t(const char * p);
static void convert_time_t_to_long_date(time_t t, QUAD * long_date);
/*****************************************************************************/
/* Copy a string in ISO-Latin-1 form (8 bits per character) into a
* buffer, converting it into a little-endian 16 bit Unicode version
* of the string. This works because the ISO-Latin-1 sits within
* the ASCII/BMP Latin-1 Unicode range.
*
* This function creates a null-terminated UTF16-LE Unicode string and
* returns how many bytes were written to the buffer, including the
* null-termination.
*/
static int
copy_latin1_to_utf16le(byte * to,int to_size, const byte * from,int len)
{
int num_bytes_written = 0;
int i;
if(to_size >= 2)
{
to_size -= 2;
for(i = 0 ; i < len ; i++)
{
if(num_bytes_written + 2 <= to_size)
{
(*to++) = (*from++);
(*to++) = '\0';
num_bytes_written += 2;
}
}
(*to++) = '\0';
(*to) = '\0';
num_bytes_written += 2;
}
return(num_bytes_written);
}
/*****************************************************************************/
/* Copy a string in little-endian 16 bit Unicode into a buffer, converting
* it into a ISO-Latin-1 (8 bits per character) version of the string, if
* possible. Code points beyond the ASCII/BMP Latin-1 are replaced by the
* control character 0x80.
*
* Note that the length is given as the number of 16 bit Unicode
* characters, not the number of bytes required to store the string.
*
* This function creates a null-terminated ISO-Latin-1 string and
* returns how many bytes were written to the buffer, including the
* null-termination.
*/
static int
copy_utf16le_to_latin1(byte * to,int to_size,const byte * from,int len)
{
int num_bytes_written = 0;
word c;
int i;
if(to_size >= 1)
{
to_size -= 1;
for(i = 0 ; i < len ; i++, from += 2)
{
c = ((word)from[1] << 8) | from[0];
if(c >= 256)
c = 0x80;
if(num_bytes_written + 1 <= to_size)
{
(*to++) = c;
num_bytes_written++;
}
}
(*to) = '\0';
num_bytes_written++;
}
return(num_bytes_written);
}
/*****************************************************************************/
/* Works just like strlen(), but stops as soon as it hits
* the boundaries of the buffer which contains the string.
*/
static size_t strnlen(const char * s,size_t max_size)
{
const char * start = s;
size_t result;
if(max_size > 0)
{
while((*s) != '\0' && --max_size > 0)
s++;
}
result = (size_t)(s - start);
return(result);
}
/*****************************************************************************/
/* Works just like strnlen(), but operates on 16 bit characters. */
static size_t strnlen_utf16(const char * s,size_t max_size)
{
size_t result = 0;
while(max_size >= 2)
{
if((s[0] | s[1]) == 0)
break;
s += 2;
max_size -= 2;
result++;
}
return(result);
}
/*****************************************************************************/
/*****************************************************************************
*
* Encoding/Decoding section
*
*****************************************************************************/
static INLINE byte *
smb_encode_byte(byte * p, byte data)
{
(*p) = data;
return &p[1];
}
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_encode_dword (byte * p, dword data)
{
p[0] = data & 0xffU;
p[1] = (data & 0xff00U) >> 8;
p[2] = (data & 0xff0000U) >> 16;
p[3] = (data & 0xff000000U) >> 24;
return &p[4];
}
static byte *
smb_copy_data (byte * p, const byte * data, int len)
{
memmove (p, data, len);
return &p[len];
}
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, int 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;
memcpy(p, name, len);
p[len] = '\0';
return p + len + 1;
}
static byte *
smb_encode_ascii (byte * p, const byte * name, int len)
{
(*p++) = 4;
memcpy (p, name, len);
p[len] = '\0';
return p + len + 1;
}
static void
smb_encode_vblock (byte * p, const byte * data, int len)
{
ASSERT( 0 <= len && len <= 65535 );
(*p++) = 5;
p = smb_encode_word (p, len);
memcpy (p, data, 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.
*
****************************************************************************/
int
smb_len (const byte * packet)
{
/* This returns the payload length stored in the NetBIOS session header. */
return (((int)(packet[1] & 0x1)) << 16) | (((int)packet[2]) << 8) | (packet[3]);
}
static INLINE word
smb_bcc (const byte * packet)
{
int pos = 37 + 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 int
smb_valid_packet (const byte * packet)
{
int error;
if (memcmp(&packet[4],"\xffSMB",4) != SAME)
error = error_smb_message_signature_missing;
else if (smb_len(packet) < (int)(32 + 1 + SMB_WCT (packet) * sizeof(word) + sizeof(word) + SMB_BCC (packet)) )
error = error_smb_message_too_short;
else
error = 0;
return error;
}
/* 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 (const byte * packet, int command, int wct, int bcc)
{
int error;
if (SMB_CMD (packet) != command)
error = error_smb_message_invalid_command;
else if (SMB_WCT (packet) < wct)
error = error_smb_message_invalid_word_count;
else if (bcc != -1 && SMB_BCC (packet) < bcc)
error = error_smb_message_invalid_byte_count;
else
error = 0;
return(error);
}
void
smb_translate_error_class_and_code(int errcls,int error,char ** class_ptr,char ** code_ptr)
{
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 == errcls)
{
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 == error)
{
err_code = &err[j];
break;
}
}
}
break;
}
}
if(err_class != NULL && err_code != NULL)
{
(*class_ptr) = (char *)err_class->class;
(*code_ptr) = (char *)err_code->message;
}
else
{
(*class_ptr) = "?";
(*code_ptr) = "?";
}
}
int
smb_errno (int errcls, int error)
{
int result,i;
if (errcls == ERRDOS)
{
static const int map[][2] =
{
{ ERRbadfunc, EINVAL },
{ ERRbadfile, ENOENT },
{ ERRbadpath, ENOENT },
{ ERRnofids, EMFILE },
{ ERRnoaccess, EPERM },
{ ERRbadfid, EBADF },
{ ERRbadmcb, EIO },
{ ERRnomem, ENOMEM },
{ ERRbadmem, EFAULT },
{ ERRbadenv, EIO },
{ ERRbadformat, EIO },
{ ERRbadaccess, EACCES },
{ ERRbaddata, E2BIG },
{ ERRbaddrive, ENXIO },
{ ERRremcd, EIO },
{ ERRdiffdevice, EXDEV },
{ ERRbadshare, ETXTBSY },
{ ERRlock, EDEADLK },
{ ERRfilexists, EEXIST },
{ ERRinvalidparam, EINVAL },
{ 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 },
{ ERRdiskfull, ENOSPC },
{ -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;
}
#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 == errcls)
{
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 == error)
{
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",
errcls,error,err_class->class,err_code->message,result,strerror(result)));
}
else
{
LOG(("no proper translation for error code %ld/%ld to %ld (%s)\n",
errcls,error,result,strerror(result)));
}
}
#endif /* DEBUG */
return(result);
}
#if DEBUG
static char
print_char (char c)
{
if ((c < ' ') || (c > '~'))
return '.';
return c;
}
static void
smb_dump_packet (const 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_with_payload (
struct smb_server * server,
int command,
int wct,
int bcc,
void * input_payload,
const void * output_payload,
int payload_size,
int * error_ptr)
{
int result;
int error;
server->rcls = 0;
server->err = 0;
/* Send the message and wait for the response to arrive. */
result = smb_request (server, command, input_payload, output_payload, payload_size, error_ptr);
/* smb_request() failed? */
if (result < 0)
{
LOG (("smb_request failed\n"));
}
/* The message received is inconsistent? */
else if ((error = smb_valid_packet (server->transmit_buffer)) != 0)
{
LOG (("not a valid packet!\n"));
(*error_ptr) = error;
result = -1;
}
/* An error condition was flagged? */
else if (server->rcls != 0)
{
smb_printerr (server->rcls, server->err);
(*error_ptr) = error_check_smb_error;
result = -1;
}
/* The message received does not contain the expected
* number of parameters and/or payload? A bcc value of
* -1 means that payload size should be ignored.
*/
else if ((error = smb_verify (server->transmit_buffer, command, wct, bcc)) != 0)
{
LOG (("smb_verify failed\n"));
(*error_ptr) = error;
result = -1;
}
if(result < 0)
smb_check_server_connection(server, (*error_ptr));
return(result);
}
/* Same thing as smb_request_ok(), but without this payload
* business...
*/
static int
smb_request_ok (struct smb_server *s, int command, int wct, int bcc, int * error_ptr)
{
return(smb_request_ok_with_payload (s, command, wct, bcc, NULL, NULL, 0, error_ptr));
}
/* Try to reopen a file after the server has dropped the connection, or
* was disconnected.
*/
static int
reopen_entry(struct smb_server *server, struct smb_dirent *entry,int * error_ptr)
{
int result;
ASSERT( server != NULL && entry != NULL );
if(!entry->opened)
{
int ignored_error;
LOG (("trying to reopen file '%s'\n", escape_name(entry->complete_path)));
result = smb_proc_open (server, entry->complete_path, entry->len, entry->writable, FALSE, entry, error_ptr != NULL ? error_ptr : &ignored_error);
}
else
{
result = 0;
}
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 FALSE,
* 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 success = FALSE;
int ignored_error;
if (server->state == CONN_VALID)
goto out;
smb_release (server);
/* Stop means stop. */
if(server->dont_retry)
goto out;
if (smb_proc_reconnect (server, &ignored_error) < 0)
{
LOG (("smb_proc_reconnect failed\n"));
server->state = CONN_RETRIED;
goto out;
}
server->state = CONN_VALID;
success = TRUE;
out:
return success;
}
/* smb_setup_header: We completely set up the packet. You only have to
* insert the command-specific fields.
*
* Returns a pointer to the SMB_Data part of the packet, which
* is exactly bcc bytes in size.
*/
static byte *
smb_setup_header (struct smb_server *server, byte command, int wct, int bcc)
{
/* The SMB frame size comes together as follows:
*
* 32 bytes for the SMB header
* 1 byte for the parameter count
* x words (2 bytes each) for the parameters
* 1 word (2 bytes) for the number of bytes to transmit (payload)
* x bytes to transmit (payload)
*/
const int smb_frame_size = 32 + 1 + wct * sizeof (word) + sizeof (word) + bcc;
byte *p = server->transmit_buffer;
byte *buf = server->transmit_buffer;
ASSERT( wct >= 0 );
ASSERT( bcc >= 0 );
ASSERT( smb_frame_size <= (int)server->transmit_buffer_allocation_size );
if(smb_frame_size > (int)server->transmit_buffer_allocation_size)
LOG (("Danger: total packet size (%ld) > transmit buffer allocation (%ld)!\n", smb_frame_size, server->transmit_buffer_allocation_size));
/* This sets up the NetBIOS session header. 'smb_frame_size'
* is the amount of data that follows the header, in this case
* the complete size of the SMB frame, including its payload.
*
* The NetBIOS session header takes up 4 bytes of room.
*/
p = smb_encode_smb_length (p, smb_frame_size);
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, SMB_FLG_CASELESS_PATHNAMES);
WSET (buf, smb_flg2, SMB_FLG2_KNOWS_LONG_NAMES|SMB_FLG2_EAS|(server->unicode_enabled ? SMB_FLG2_UNICODE_STRINGS : 0));
}
(*p++) = wct;
p += wct * sizeof(word);
WSET (p, 0, bcc);
LOG (("total packet size=%ld, max receive size=%ld, packet buffer size=%ld\n",
p + sizeof(word) + bcc - server->transmit_buffer,
server->max_recv,
server->transmit_buffer_allocation_size
));
return p + sizeof(word);
}
/* Returns how much room is still left in the transmission
* buffer, just judging by the number of parameters and
* the payload size.
*/
int
smb_payload_size(const struct smb_server *server, int wct, int bcc)
{
return(server->transmit_buffer_size - (int)(32 + 1 + wct * sizeof(word) + sizeof(word) + bcc));
}
/*****************************************************************************
*
* File operation section.
*
****************************************************************************/
int
smb_proc_open (struct smb_server *server, const char *pathname, int len, int writable, int truncate_file, struct smb_dirent *entry,int * error_ptr)
{
int result;
char *p;
char *buf = server->transmit_buffer;
int retry_read_only;
ENTER();
D(("pathname = '%s'", escape_name(pathname)));
/* Because the original code opened every file/directory in
* read/write mode, we emulate the same behaviour. Why this
* is done is hard to tell. Presumably, some directories need
* to be writable for modifications to take place within.
*
* If read access is requested, we start with write access
* and disable truncation just to be safe. If that attempt
* fails due to access rights issues, we try again with read
* access.
*/
if(writable)
{
retry_read_only = FALSE;
}
else
{
writable = TRUE;
retry_read_only = TRUE;
truncate_file = FALSE;
}
if (server->protocol >= PROTOCOL_NT1)
{
dword desired_access;
dword share_access;
dword create_disposition;
dword create_options;
dword ext_file_attributes;
dword end_of_file_low;
dword end_of_file_high;
char *params;
char *data;
int pathname_size;
int pathname_pad;
SHOWMSG("we'll try SMB_COM_NT_CREATE_ANDX");
if(server->unicode_enabled)
{
pathname_size = 2 * (len + 1);
pathname_pad = 1;
}
else
{
pathname_size = len + 1;
pathname_pad = 0;
}
ASSERT( smb_payload_size(server, 24, pathname_size + pathname_pad) >= 0 );
while(TRUE)
{
if(writable)
{
SHOWMSG("write access required");
/* The following does not work consistently with multiple
* SMB servers. Which is why we stick with generic read/write.
*/
/* desired_access = FILE_READ_DATA|FILE_WRITE_DATA|FILE_DELETE; */
desired_access = GENERIC_READ|GENERIC_WRITE;
}
else
{
SHOWMSG("read access is sufficient");
/* The following does not work consistently with multiple
* SMB servers. Which is why we stick with generic read.
*/
/* desired_access = FILE_READ_DATA; */
desired_access = GENERIC_READ;
}
/* desired_access |= FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES; */
/* Allows others to read, write and delete the file just created.
* This may be useful if smbfs hangs or you need to restart your
* System and you need to clean up after the file you just
* created.
*/
share_access = FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
if(writable && truncate_file)
{
create_disposition = FILE_OVERWRITE_IF;
create_options = FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS;
}
else
{
create_disposition = FILE_OPEN;
create_options = 0;
}
data = smb_setup_header (server, SMBntcreateX, 24, pathname_pad + pathname_size);
params = SMB_VWV (server->transmit_buffer);
params = smb_encode_byte(params, 0xFF); /* AndXCommand: no next command */
params = smb_encode_byte(params, 0); /* AndXReserved */
params = smb_encode_word(params, 0); /* AndXOffset */
params = smb_encode_byte(params, 0); /* Reserved */
params = smb_encode_word(params, pathname_pad + pathname_size); /* NameLength */
params = smb_encode_dword(params, 0); /* Flags */
params = smb_encode_dword(params, 0); /* RootDirectoryFID: 0 -> file name is relative to root directory */
params = smb_encode_dword(params, desired_access); /* DesiredAccess */
params = smb_encode_dword(params, 0); /* AllocationSize (low) */
params = smb_encode_dword(params, 0); /* AllocationSize (high) */
params = smb_encode_dword(params, ATTR_NORMAL); /* ExtFileAttributes */
params = smb_encode_dword(params, share_access); /* ShareAccess */
params = smb_encode_dword(params, create_disposition); /* CreateDisposition */
params = smb_encode_dword(params, create_options); /* CreateOptions */
params = smb_encode_dword(params, SEC_ANONYMOUS); /* ImpersonationLevel */
(void) smb_encode_byte(params, 0); /* SecurityFlags */
/* Now for the data portion of the message */
if(server->unicode_enabled)
{
(*data++) = 0;
copy_latin1_to_utf16le(data, pathname_size, pathname, len);
}
else
{
(void) smb_copy_data(data, pathname, pathname_size); /* Length includes the terminating NUL byte. */
}
LOG(("requesting SMBntcreateX\n"));
result = smb_request_ok(server, SMBntcreateX, 34, 0, error_ptr);
if (result < 0)
{
SHOWMSG("that didn't work; retrying");
/* Try again in read-only mode? */
if ((*error_ptr) == EACCES || ((*error_ptr) == error_check_smb_error && smb_errno(server->rcls,server->err) == EACCES))
{
if(retry_read_only)
{
SHOWMSG("retrying with read-only access");
retry_read_only = FALSE;
writable = FALSE;
continue;
}
else
{
goto out;
}
}
if (smb_retry (server))
continue;
else
goto out;
}
break;
}
params = SMB_VWV (server->transmit_buffer);
params += 5; /* AndXCommand+AndXReserved+AndXOffset+OpLockLevel */
params = smb_decode_word(params, &entry->fileid);
params += sizeof(dword); /* CreateDisposition */
entry->ctime = local2utc (convert_long_date_to_time_t(params));
params += 2 * sizeof(dword); /* CreateTime */
entry->atime = local2utc (convert_long_date_to_time_t(params));
params += 2 * sizeof(dword); /* LastAccessTime */
entry->wtime = local2utc (convert_long_date_to_time_t(params));
params += 2 * sizeof(dword); /* LastWriteTime */
entry->mtime = local2utc (convert_long_date_to_time_t(params));
params += 2 * sizeof(dword); /* LastChangeTime */
params = smb_decode_dword(params, &ext_file_attributes);
entry->attr = ext_file_attributes;
params += 2 * sizeof(dword); /* AllocationSize */
params = smb_decode_dword(params, &end_of_file_low);
(void) smb_decode_dword(params, &end_of_file_high);
entry->size_low = end_of_file_low;
entry->size_high = end_of_file_high;
entry->opened = TRUE;
entry->writable = writable;
goto out;
}
else
{
word access_and_share_modes;
ASSERT( smb_payload_size(server, 2, 2 + len) >= 0 );
SHOWMSG("using the old SMB_COM_OPEN");
while(TRUE)
{
if(writable)
access_and_share_modes = SMB_OPEN_SHARE_DENY_NOTHING|SMB_OPEN_ACCESS_READ_WRITE;
else
access_and_share_modes = SMB_OPEN_SHARE_DENY_NOTHING|SMB_OPEN_ACCESS_READ_ONLY;
p = smb_setup_header (server, SMBopen, 2, 2 + len);
WSET (buf, smb_vwv0, access_and_share_modes);
WSET (buf, smb_vwv1, SMB_FILE_ATTRIBUTE_HIDDEN|SMB_FILE_ATTRIBUTE_SYSTEM|SMB_FILE_ATTRIBUTE_DIRECTORY);
smb_encode_ascii (p, pathname, len);
result = smb_request_ok (server, SMBopen, 7, 0, error_ptr);
if (result < 0)
{
/* Try again in read-only mode? */
if ((*error_ptr) == EACCES || ((*error_ptr) == error_check_smb_error && smb_errno(server->rcls,server->err) == EACCES))
{
if(retry_read_only)
{
SHOWMSG("retrying with read-only access");
retry_read_only = FALSE;
writable = FALSE;
continue;
}
else
{
goto out;
}
}
if (smb_retry (server))
continue;
else
goto out;
}
break;
}
/* We should now have data in vwv[0..6]. */
entry->fileid = WVAL (buf, smb_vwv0);
entry->attr = WVAL (buf, smb_vwv1);
/* This is actually the mtime value, but we use it
* in all other places as well.
*/
entry->ctime = entry->atime = entry->mtime = entry->wtime = local2utc (DVAL (buf, smb_vwv2));
entry->size_low = DVAL (buf, smb_vwv4);
entry->size_high = 0;
entry->opened = TRUE;
entry->writable = writable;
}
out:
RETURN(result);
return(result);
}
/* 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, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
/* 0 and 0xffffffff mean: do not set mtime */
if(mtime != 0 && mtime != 0xffffffff)
mtime = utc2local (mtime);
smb_setup_header (server, SMBclose, 3, 0);
WSET (buf, smb_vwv0, fileid);
DSET (buf, smb_vwv1, mtime);
result = smb_request_ok (server, SMBclose, 0, 0, error_ptr);
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. */
int
smb_proc_read (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
ASSERT( count <= 65535 );
retry:
smb_setup_header (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);
LOG(("requesting %ld bytes\n", count));
result = smb_request_ok_with_payload (server, SMBread, 5, -1, data, NULL, count, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,finfo,NULL) < 0)
{
LOG(("that didn't work.\n"));
goto out;
}
else
{
goto retry;
}
}
else
{
goto out;
}
}
/* The buffer format must be 1; smb_request_ok_with_payload() already checked this. */
LOG(("buffer_format=%ld, should be %ld\n", BVAL(buf, NETBIOS_HEADER_SIZE+45), 1));
result = WVAL (buf, NETBIOS_HEADER_SIZE+46); /* count of bytes to read */
ASSERT( result <= count );
LOG(("read %ld bytes (should be < %ld)\n", result, count));
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, const QUAD * const offset_quad, long count, char *data, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
ASSERT( count <= 65535 );
retry:
smb_setup_header (server, SMBreadbraw, 10, 0);
WSET (buf, smb_vwv0, finfo->fileid);
DSET (buf, smb_vwv1, offset_quad->Low);
WSET (buf, smb_vwv3, count); /* maxcnt */
WSET (buf, smb_vwv4, 0); /* mincnt */
DSET (buf, smb_vwv5, 0); /* timeout */
WSET (buf, smb_vwv7, 0); /* reserved */
DSET (buf, smb_vwv8, offset_quad->High);
result = smb_request_read_raw (server, data, count, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
}
return result;
}
int
smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data, int * error_ptr)
{
int result;
char *buf = server->transmit_buffer;
byte *p;
ASSERT( count <= 65535 );
retry:
p = smb_setup_header (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; /* Buffer format - this field must be 1 */
WSET (p, 0, count); /* Data length - this field must match what the count field in the SMB header says */
result = smb_request_ok_with_payload (server, SMBwrite, 1, 0, NULL, data, count, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,finfo,NULL) < 0)
LOG(("that didn't work.\n"));
else
goto retry;
}
}
else
{
result = WVAL (buf, smb_vwv0);
}
return result;
}
int
smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, const QUAD * const offset_quad, long count, const char *data, int * error_ptr)
{
char *buf = server->transmit_buffer;
int num_bytes_written = 0;
int result;
long max_len;
long len;
byte *p;
ASSERT( count <= 65535 );
LOG (("number of bytes to send = %ld\n", count));
/* 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)+
* 4(offset high)
* = 29 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' - 63 for the payload.
*/
max_len = server->max_buffer_size;
if(max_len > 65535)
max_len = 65535;
max_len -= 63;
LOG(("maximum length for payload = %ld bytes\n", max_len));
/* 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 <= max_len)
{
LOG(("count (%ld) <= max_len (%ld) -- send no data with the message.\n",count,max_len));
len = 0; /* Send a zero length SMB_COM_WRITE_RAW message, followed by the raw data. */
}
else
{
len = count - max_len; /* Send some of the data as part of the SMB_COM_WRITE_RAW message, followed by the remaining raw data. */
LOG(("count (%ld) > max_len (%ld) -- send %ld bytes with the message.\n",count,max_len,len));
}
retry:
p = smb_setup_header (server, SMBwritebraw, 14, len);
WSET (buf, smb_vwv0, finfo->fileid);
WSET (buf, smb_vwv1, count);
WSET (buf, smb_vwv2, 0); /* reserved */
DSET (buf, smb_vwv3, offset_quad->Low);
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));
WSET (buf, smb_vwv12, offset_quad->High);
}
else
{
WSET (buf, smb_vwv10, 0);
}
LOG(("requesting SMBwritebraw\n"));
result = smb_request_ok_with_payload (server, SMBwritebraw, 1, 0, NULL, data, len, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
else
goto out;
}
num_bytes_written += len;
data += len;
count -= len;
LOG (("bytes sent so far = %ld\n", num_bytes_written));
if(count > 0)
{
LOG(("sending %ld bytes of data (raw)\n",count));
ASSERT( count <= 65535 );
result = smb_request_write_raw (server, data, count, error_ptr);
if (result < 0)
{
/* Roll back the counters */
num_bytes_written -= len;
data -= len;
count += len;
if (smb_retry (server))
goto retry;
else
goto out;
}
if(server->write_behind)
{
/* We just assume success; the next file operation to follow
* will set an error status if something went wrong.
*/
result = num_bytes_written + count;
}
else
{
int error;
/* We have to do the checks of smb_request_ok here as well */
if ((error = smb_valid_packet (server->transmit_buffer)) != 0)
{
LOG (("not a valid packet!\n"));
(*error_ptr) = error;
result = -1;
goto out;
}
else if (server->rcls != 0)
{
LOG (("server error %ld/%ld\n", server->rcls, server->err));
smb_printerr (server->rcls, server->err);
(*error_ptr) = error_check_smb_error;
result = -1;
goto out;
}
result = num_bytes_written + count;
}
LOG (("bytes sent so far = %ld\n", result));
}
out:
return result;
}
int
smb_proc_writex (struct smb_server *server, struct smb_dirent *finfo, const QUAD * const offset_quad, long count, const char *data, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
byte *p;
ASSERT( count <= 65535 );
LOG (("number of bytes to send = %ld\n", count));
retry:
p = smb_setup_header (server, SMBwriteX, server->protocol >= PROTOCOL_NT1 ? 14 : 12, 2+1+count);
BSET (buf, smb_vwv0, 0xFF); /* AndXCommand/AndXReserved - no additional SMB commands provided */
WSET (buf, smb_vwv1, 0); /* AndXOffset */
WSET (buf, smb_vwv2, finfo->fileid); /* fid */
DSET (buf, smb_vwv3, offset_quad->Low); /* offset */
DSET (buf, smb_vwv5, 0); /* timeout */
WSET (buf, smb_vwv7, 0); /* write mode */
WSET (buf, smb_vwv8, 0); /* remaining */
WSET (buf, smb_vwv9, 0); /* reserved */
WSET (buf, smb_vwv10, count); /* data length */
if(server->protocol >= PROTOCOL_NT1)
{
WSET (buf, smb_vwv11, 64+2); /* data offset */
DSET (buf, smb_vwv12, offset_quad->High); /* high offset */
}
else
{
WSET (buf, smb_vwv11, 60+2); /* data offset */
}
/* Now for the data portion of the message */
WSET (p, 0, 1+count); p += 2; /* Byte count (1 pad byte + data bytes) */
(*p) = 0; /* Padding byte that must be ignored */
LOG(("requesting SMBwriteX: offset=%s, count=%ld\n", convert_quad_to_string(offset_quad), count));
result = smb_request_ok_with_payload (server, SMBwriteX, 6, 0, NULL, data, count, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,finfo,NULL) < 0)
LOG(("that didn't work.\n"));
else
goto retry;
}
}
else
{
word available;
p = SMB_VWV (server->transmit_buffer);
smb_decode_word (p + 4, &available);
LOG(("number of bytes written = %ld\n", available));
result = available;
}
return result;
}
int
smb_proc_readx (struct smb_server *server, struct smb_dirent *finfo, const QUAD * const offset_quad, long count, char *data, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
byte *p;
ASSERT( count <= 65535 );
LOG (("number of bytes to read = %ld\n", count));
retry:
p = smb_setup_header (server, SMBreadX, server->protocol >= PROTOCOL_NT1 ? 12 : 10, 2);
BSET (buf, smb_vwv0, 0xFF); /* AndXCommand/AndXReserved - no additional SMB commands provided */
WSET (buf, smb_vwv1, 0); /* AndXOffset */
WSET (buf, smb_vwv2, finfo->fileid); /* fid */
DSET (buf, smb_vwv3, offset_quad->Low); /* offset */
WSET (buf, smb_vwv5, count); /* MaxCountOfBytesToReturn */
WSET (buf, smb_vwv6, 0); /* MinCountOfBytesToReturn */
DSET (buf, smb_vwv7, 0); /* timeout */
WSET (buf, smb_vwv9, 0); /* remaining */
if(server->protocol >= PROTOCOL_NT1)
DSET (buf, smb_vwv10, offset_quad->High); /* OffsetHigh */
/* Now for the data portion of the message */
WSET (p, 0, 0); /* Byte count */
LOG(("requesting SMBreadX: offset=%s, count=%ld\n", convert_quad_to_string(offset_quad), count));
result = smb_request_ok_with_payload (server, SMBreadX, 7, 0, data, NULL, count, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,finfo,NULL) < 0)
LOG(("that didn't work.\n"));
else
goto retry;
}
}
else
{
word data_length;
p = SMB_VWV (server->transmit_buffer);
smb_decode_word (p + 10, &data_length);
LOG(("number of bytes read = %ld\n", data_length));
result = data_length;
}
return result;
}
/* 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, const struct smb_lkrng *locks, int num_entries, int mode, long timeout, int * error_ptr)
{
int result;
int num_locks, num_unlocks;
char *buf = server->transmit_buffer;
char *data;
const struct smb_lkrng *p;
int i;
num_locks = num_unlocks = 0;
if (mode & 2)
num_unlocks = num_entries;
else
num_locks = num_entries;
ASSERT( smb_payload_size(server, 8, num_entries * 10) >= 0 );
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);
}
result = smb_request_ok (server, SMBlockingX, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,finfo,NULL) < 0)
LOG(("that didn't work.\n"));
else
goto retry;
}
}
return result;
}
/* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */
int
smb_proc_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr)
{
int result;
char *p;
char *buf = server->transmit_buffer;
int local_time;
retry:
ASSERT( smb_payload_size(server, 3, len + 2) >= 0 );
p = smb_setup_header (server, SMBcreate, 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);
result = smb_request_ok (server, SMBcreate, 1, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
else
goto out;
}
entry->opened = TRUE;
entry->fileid = WVAL (buf, smb_vwv0);
/* Don't change the modification time. */
smb_proc_close (server, entry->fileid, 0, error_ptr);
out:
return result;
}
int
smb_proc_mv (struct smb_server *server, const char *old_path, const int old_path_len, const char *new_path, const int new_path_len, int * error_ptr)
{
char *p;
char *buf = server->transmit_buffer;
int size;
int result;
if(server->unicode_enabled)
size = 2 + 1 + 2 * (old_path_len+1) + 1 + 2 * (new_path_len+1);
else
size = 2 + 1 + (old_path_len+1) + 1 + (new_path_len+1);
ASSERT( smb_payload_size(server, 1, size) >= 0 );
retry:
p = smb_setup_header (server, SMBmv, 1, size);
WSET (buf, smb_vwv0, SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN);
if(server->unicode_enabled)
{
(*p++) = 4; /* A null-terminated string follows. */
p += copy_latin1_to_utf16le(p,2 * (old_path_len+1),old_path,old_path_len);
(*p++) = 4; /* A null-terminated string follows. */
(void) copy_latin1_to_utf16le(p,2 * (new_path_len+1),new_path,new_path_len);
}
else
{
p = smb_encode_ascii (p, old_path, old_path_len);
(void) smb_encode_ascii (p, new_path, new_path_len);
}
result = smb_request_ok (server, SMBmv, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
}
return result;
}
int
smb_proc_mkdir (struct smb_server *server, const char *path, const int len, int * error_ptr)
{
int path_size;
int result;
char *p;
if(server->unicode_enabled)
path_size = 2 * (len + 1);
else
path_size = len + 1;
retry:
/* olsen (2018-05-20): The TRANSACT2_MKDIR command does not seem to
* work correctly with Samba 3.0.25, although the
* implementation appears to be sound (it follows
* both the SNIA and Microsoft CIFS documentation,
* as well as the 1996 Microsoft SMB file sharing
* protocol documentation). The extended attributes
* are documented as being optional, and we omit
* them here.
*/
if (FALSE && server->protocol >= PROTOCOL_LANMAN2)
{
unsigned char *outbuf = server->transmit_buffer;
ASSERT( smb_payload_size(server, 15, 3 + 4 + path_size) >= 0 );
smb_setup_header (server, SMBtrans2, 15, 3 + 4 + path_size);
WSET (outbuf, smb_tpscnt, 4 + path_size); /* TotalParameterCount */
WSET (outbuf, smb_tdscnt, 0); /* TotalDataCount */
WSET (outbuf, smb_mprcnt, 2); /* MaxParameterCount */
WSET (outbuf, smb_mdrcnt, server->max_recv); /* MaxDataCount */
WSET (outbuf, smb_msrcnt, 0); /* MaxSetupCount+Reserved1 */
WSET (outbuf, smb_flags, 0); /* Flags */
DSET (outbuf, smb_timeout, 0); /* Timeout */
/* Reserved2 */
WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); /* ParameterCount */
WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - NETBIOS_HEADER_SIZE); /* ParameterOffset */
WSET (outbuf, smb_dscnt, 0); /* DataCount */
WSET (outbuf, smb_dsoff, 0); /* DataOffset */
WSET (outbuf, smb_suwcnt, 1); /* SetupCount+Reserved3 */
WSET (outbuf, smb_setup0, TRANSACT2_MKDIR); /* Setup[0] */
p = SMB_BUF (outbuf);
/* Align to a 4-byte-boundary. */
(*p++) = '\0';
(*p++) = '\0';
(*p++) = '\0';
DSET (p, 0, 0); /* Reserved */
p += 4;
if(server->unicode_enabled)
{
copy_latin1_to_utf16le(p,path_size,path,len);
}
else
{
memcpy(p,path,len);
p[len] = '\0';
}
result = smb_trans2_request (server, SMBtrans2, NULL, NULL, NULL, NULL, error_ptr);
if (result < 0)
{
if((*error_ptr) != error_check_smb_error)
{
if (smb_retry (server))
goto retry;
}
goto out;
}
if(server->rcls != 0)
{
(*error_ptr) = error_check_smb_error;
result = -1;
goto out;
}
}
else
{
ASSERT( smb_payload_size(server, 0, 1 + path_size) >= 0 );
p = smb_setup_header (server, SMBmkdir, 0, 1 + path_size);
if(server->unicode_enabled)
{
(*p++) = 4; /* A null-terminated string follows. */
copy_latin1_to_utf16le(p,path_size,path,len);
}
else
{
smb_encode_ascii (p, path, len);
}
result = smb_request_ok (server, SMBmkdir, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
}
}
out:
return result;
}
int
smb_proc_rmdir (struct smb_server *server, const char *path, const int len, int * error_ptr)
{
int path_size;
int result;
char *p;
if(server->unicode_enabled)
path_size = 2 * (len + 1);
else
path_size = len + 1;
ASSERT( smb_payload_size(server, 0, 1 + path_size) >= 0 );
retry:
p = smb_setup_header (server, SMBrmdir, 0, 1 + path_size);
if(server->unicode_enabled)
{
(*p++) = 4; /* A null-terminated string follows. */
copy_latin1_to_utf16le(p,path_size,path,len);
}
else
{
smb_encode_ascii (p, path, len);
}
result = smb_request_ok (server, SMBrmdir, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
}
return result;
}
int
smb_proc_unlink (struct smb_server *server, const char *path, const int len, int * error_ptr)
{
int path_size;
char *p;
char *buf = server->transmit_buffer;
int result;
if(server->unicode_enabled)
path_size = 2 * (len + 1);
else
path_size = len + 1;
ASSERT( smb_payload_size(server, 1, 1 + path_size) >= 0 );
retry:
p = smb_setup_header (server, SMBunlink, 1, 1 + path_size);
/* Allow for system and hidden files to be deleted, too. After
* all, we show these in the directory lists.
*/
WSET (buf, smb_vwv0, SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN);
if(server->unicode_enabled)
{
(*p++) = 4; /* A null-terminated string follows. */
copy_latin1_to_utf16le(p,path_size,path,len);
}
else
{
smb_encode_ascii (p, path, len);
}
result = smb_request_ok (server, SMBunlink, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
}
return result;
}
int
smb_proc_trunc (struct smb_server *server, word fid, dword length, int * error_ptr)
{
char *p;
char *buf = server->transmit_buffer;
int result;
ASSERT( smb_payload_size(server, 5, 3) >= 0 );
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);
result = smb_request_ok (server, SMBwrite, 1, 0, error_ptr);
if(result < 0)
{
if (smb_retry (server))
goto retry;
}
else
{
result = DVAL(buf, smb_vwv0);
}
return result;
}
static int
smb_decode_dirent (const 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_low = DVAL (p, 5);
entry->size_high = 0;
name_size = 13;
if(name_size > entry->complete_path_size-1)
return(-1);
memcpy (entry->complete_path, p + 9, name_size);
entry->complete_path[name_size] = '\0';
LOG (("path = '%s'\n", escape_name(entry->complete_path)));
#if DEBUG
{
struct tm tm;
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));
}
#endif /* DEBUG */
return(0);
}
/* 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, const char *path, int fpos, int cache_size, struct smb_dirent *entry, int * error_ptr)
{
char *p;
char *buf;
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_recv - 100) / SMB_DIRINFO_SIZE;
int path_len = strlen (path);
char * mask;
mask = malloc(path_len + 4 + 1);
if (mask == NULL)
{
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
memcpy(mask, path, path_len);
memcpy(&mask[path_len], "\\*.*", 5); /* Length includes terminating NUL. */
LOG (("SMB call readdir %ld @ %ld\n", cache_size, fpos));
LOG ((" mask = '%s'\n", escape_name(mask)));
buf = server->transmit_buffer;
retry:
first = TRUE;
total_count = 0;
current_entry = entry;
while (TRUE)
{
if (first)
{
ASSERT( smb_payload_size(server, 2, 5 + strlen (mask)) >= 0 );
p = smb_setup_header (server, SMBsearch, 2, 5 + strlen (mask));
WSET (buf, smb_vwv0, entries_asked);
WSET (buf, smb_vwv1, SMB_FILE_ATTRIBUTE_DIRECTORY);
p = smb_encode_ascii (p, mask, strlen (mask));
(*p++) = 5;
(void) smb_encode_word (p, 0);
}
else
{
ASSERT( smb_payload_size(server, 2, 5 + SMB_STATUS_SIZE) >= 0 );
p = smb_setup_header (server, SMBsearch, 2, 5 + SMB_STATUS_SIZE);
WSET (buf, smb_vwv0, entries_asked);
WSET (buf, smb_vwv1, SMB_FILE_ATTRIBUTE_DIRECTORY);
p = smb_encode_ascii (p, "", 0);
(void) smb_encode_vblock (p, status, SMB_STATUS_SIZE);
}
if (smb_request_ok (server, SMBsearch, 1, -1, error_ptr) < 0)
{
if (server->rcls == ERRDOS && server->err == ERRnofiles)
{
result = total_count - fpos;
goto out;
}
else
{
if (smb_retry (server))
goto retry;
result = -1;
goto out;
}
}
p = SMB_VWV (server->transmit_buffer);
p = smb_decode_word (p, &count); /* vwv[0] = count-returned */
p = smb_decode_word (p, &bcc);
first = FALSE;
if (count <= 0)
{
result = total_count - fpos;
goto out;
}
if (bcc != count * SMB_DIRINFO_SIZE + 3)
{
LOG (("byte count (%ld) does not match expected size (%ld)\n", bcc, count * SMB_DIRINFO_SIZE + 3));
(*error_ptr) = error_invalid_directory_size;
result = -1;
goto out;
}
p += 3; /* Skipping VBLOCK header (5, length lo, length hi). */
/* Read the last entry into the status field. */
memcpy (status, SMB_BUF (server->transmit_buffer) + 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 (("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 out;
}
else
{
if(smb_decode_dirent (p, current_entry) == 0)
{
current_entry += 1;
}
else
{
p += SMB_DIRINFO_SIZE;
LOG (("skipped entry because name is too long; total_count = %ld, i = %ld, fpos = %ld\n", total_count, i, fpos));
}
}
total_count += 1;
}
}
out:
if(mask != NULL)
free(mask);
return result;
}
/*****************************************************************************/
/* Translate 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
convert_long_date_to_time_t(const 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);
}
/*****************************************************************************/
/* Translate a 'time_t' value into an SMB file time, which is
* the equivalent in "100ns units since jan 1st 1601".
*/
static void
convert_time_t_to_long_date(time_t t, QUAD * long_date)
{
QUAD adjust;
long_date->Low = t;
long_date->High = 0;
/* 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;
add_64_plus_64_to_64(&adjust,long_date,long_date);
/* Multiply by 10,000,000 to convert the time from
* seconds to 100ns units.
*/
multiply_64_by_32_to_64(long_date,10000000,long_date);
}
/*****************************************************************************/
static void
smb_get_dirent_name(char *p,int level,char ** name_ptr,int * len_ptr)
{
switch (level)
{
case SMB_INFO_STANDARD:
(*len_ptr) = p[26];
(*name_ptr) = &p[27];
break;
case SMB_FILE_BOTH_DIRECTORY_INFO:
(*len_ptr) = DVAL (p, 60);
(*name_ptr) = &p[94];
break;
default:
(*name_ptr) = NULL;
(*len_ptr) = 0;
break;
}
}
/* interpret a long filename structure */
static int
smb_decode_long_dirent (const struct smb_server *server, const char *p, struct smb_dirent *finfo, int level, int * entry_length_ptr)
{
int success = TRUE;
ENTER();
ASSERT( entry_length_ptr != NULL );
switch (level)
{
case SMB_INFO_STANDARD:
SHOWMSG("SMB_INFO_STANDARD");
(*entry_length_ptr) = 28 + BVAL (p, 26);
if (finfo != NULL)
{
const char * name;
int name_size;
int name_len;
name = &p[27];
name_size = BVAL (p, 26);
SHOWVALUE(name_size);
/* Figure out how long the string really is. It's
* supposed to be NUL-terminated.
*/
if(server->unicode_enabled)
name_len = strnlen_utf16(name, name_size);
else
name_len = strnlen(name, name_size);
SHOWVALUE(name_len);
/* Skip directory entries whose names we cannot store. */
if(name_len >= (int)finfo->complete_path_size)
{
D(("name length >= %ld (skipping it)", finfo->complete_path_size));
success = FALSE;
break;
}
if(name_len == 0)
{
D(("name length == 0 (skipping it)"));
success = FALSE;
break;
}
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;
finfo->size_low = DVAL (p, 16);
finfo->size_high = DVAL (p, 16);
finfo->attr = WVAL (p, 24);
if(server->unicode_enabled)
{
copy_utf16le_to_latin1(finfo->complete_path, finfo->complete_path_size, name, name_len);
}
else
{
memcpy(finfo->complete_path, name, name_len);
finfo->complete_path[name_len] = '\0';
}
finfo->len = name_len;
D(("name = '%s', length=%ld",escape_name(finfo->complete_path),name_len));
}
break;
case SMB_FILE_BOTH_DIRECTORY_INFO:
SHOWMSG("SMB_FILE_BOTH_DIRECTORY_INFO");
(*entry_length_ptr) = WVAL (p, 0);
if (finfo != NULL)
{
dword size_low,size_high;
int name_size;
int name_len;
p += 4; /* next entry offset */
p += 4; /* fileindex */
finfo->ctime = convert_long_date_to_time_t(p);
p += 8;
finfo->atime = convert_long_date_to_time_t(p);
p += 8;
finfo->wtime = convert_long_date_to_time_t(p);
p += 8;
finfo->mtime = convert_long_date_to_time_t(p);
p += 8;
/* If the modification time is not set, try to
* substitute the write time for it.
*/
if(finfo->mtime == 0)
finfo->mtime = finfo->wtime;
size_low = DVAL (p, 0);
size_high = DVAL (p, 4);
p += 8;
finfo->size_low = size_low;
finfo->size_high = size_high;
p += 8; /* alloc size */
finfo->attr = DVAL (p, 0);
p += 4;
name_size = DVAL (p, 0);
p += 4;
/* Skip directory entries whose names we cannot store. */
if(name_size == 0)
{
SHOWMSG("name is empty");
success = FALSE;
break;
}
SHOWVALUE(name_size);
p += 4; /* EA size */
p += 1; /* short name length */
p += 1; /* reserved */
p += 12*2; /* short name (12 WCHAR characters) */
/* Figure out how long the string really is. It's
* supposed to be NUL-terminated.
*/
if(server->unicode_enabled)
name_len = strnlen_utf16(p, name_size);
else
name_len = strnlen(p, name_size);
SHOWVALUE(name_len);
/* Skip directory entries whose names we cannot store. */
if(name_len >= (int)finfo->complete_path_size)
{
D(("name length >= %ld (skipping it)", finfo->complete_path_size));
success = FALSE;
break;
}
if(name_len == 0)
{
D(("name length == 0 (skipping it)"));
success = FALSE;
break;
}
if(server->unicode_enabled)
{
copy_utf16le_to_latin1(finfo->complete_path, finfo->complete_path_size, p, name_len);
}
else
{
memcpy(finfo->complete_path, p, name_len);
finfo->complete_path[name_len] = '\0';
}
finfo->len = name_len;
D(("name = '%s', length=%ld",escape_name(finfo->complete_path),name_len));
}
break;
/* This should never happen. */
default:
(*entry_length_ptr) = 0;
success = FALSE;
break;
}
D(("entry_length = %ld",(*entry_length_ptr)));
RETURN(success);
return(success);
}
static int
smb_proc_readdir_long (struct smb_server *server, const char *path, int fpos, int cache_size, struct smb_dirent *entry, int * error_ptr)
{
int max_matches = 512; /* this should actually be based on the max_recv value */
int info_level = server->protocol < PROTOCOL_NT1 ? SMB_INFO_STANDARD : SMB_FILE_BOTH_DIRECTORY_INFO;
char *p;
int i;
int is_first;
int total_count = 0;
struct smb_dirent *current_entry;
char *resp_data = NULL;
char *resp_param = NULL;
int resp_data_len = 0;
int resp_param_len = 0;
int attribute = SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_HIDDEN | SMB_FILE_ATTRIBUTE_DIRECTORY;
int result = 0;
int ff_searchcount;
int ff_end_of_search = 0;
int ff_dir_handle = 0;
int ff_resume_key = 0;
int loop_count = 0;
unsigned char *outbuf = server->transmit_buffer;
int path_len = strlen(path);
int mask_buffer_size = path_len + 2 + 1;
char *mask;
int mask_len;
int mask_size;
int entry_length;
ENTER();
/* ZZZ experimental 'max_matches' adjustment */
/*
if(info_level == SMB_FILE_BOTH_DIRECTORY_INFO)
max_matches = server->max_recv / 360;
else
max_matches = server->max_recv / 40;
*/
SHOWVALUE(server->max_recv);
SHOWVALUE(max_matches);
mask = malloc (mask_buffer_size);
if (mask == NULL)
{
LOG (("Memory allocation failed\n"));
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
memcpy(mask,path,path_len);
memcpy(&mask[path_len],"\\*",3); /* This includes the terminating NUL. */
mask_len = path_len + 2;
LOG (("SMB call lreaddir %ld @ %ld\n", cache_size, fpos));
LOG ((" mask = '%s'\n", escape_name(mask)));
resp_param = NULL;
resp_data = NULL;
retry:
is_first = TRUE;
total_count = 0;
current_entry = entry;
while (ff_end_of_search == 0)
{
loop_count++;
if (loop_count > 200)
{
LOG (("Looping in FIND_NEXT???\n"));
(*error_ptr) = error_looping_in_find_next;
result = -1;
break;
}
memset (outbuf, 0, sizeof(word) * smb_setup1);
if(server->unicode_enabled)
mask_size = 2 * (mask_len + 1);
else
mask_size = mask_len + 1;
ASSERT( smb_payload_size(server, 15, 3 + 12 + mask_size) >= 0 );
smb_setup_header (server, SMBtrans2, 15, 3 + 12 + mask_size);
WSET (outbuf, smb_tpscnt, 12 + mask_size); /* TotalParameterCount */
WSET (outbuf, smb_tdscnt, 0); /* TotalDataCount */
WSET (outbuf, smb_mprcnt, 10); /* MaxParameterCount */
WSET (outbuf, smb_mdrcnt, server->max_recv); /* MaxDataCount */
WSET (outbuf, smb_msrcnt, 0); /* MaxSetupCount+Reserved1 */
WSET (outbuf, smb_flags, 0); /* Flags */
DSET (outbuf, smb_timeout, 0); /* Timeout */
/* Reserved2 */
WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); /* ParameterCount */
WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - NETBIOS_HEADER_SIZE); /* ParameterOffset */
WSET (outbuf, smb_dscnt, 0); /* DataCount */
WSET (outbuf, smb_dsoff, 0); /* DataOffset */
WSET (outbuf, smb_suwcnt, 1); /* SetupCount+Reserved3 */
WSET (outbuf, smb_setup0, is_first ? TRANSACT2_FINDFIRST : TRANSACT2_FINDNEXT); /* Setup[0] */
p = SMB_BUF (outbuf);
(*p++) = '\0'; /* put in a null smb_name */
/* Align the parameter data to a 4-byte boundary. */
(*p++) = '\0';
(*p++) = '\0';
if (is_first)
{
LOG (("first match\n"));
WSET (p, 0, attribute); /* attribute */
WSET (p, 2, max_matches); /* max count */
WSET (p, 4, SMB_FIND_CLOSE_AT_EOS|SMB_FIND_RETURN_RESUME_KEYS|SMB_FIND_CONTINUE_FROM_LAST);
WSET (p, 6, info_level);
DSET (p, 8, 0); /* return every entry there is */
}
else
{
LOG (("next match; ff_dir_handle=0x%lx ff_resume_key=%ld mask='%s'\n", ff_dir_handle, ff_resume_key, escape_name(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, SMB_FIND_CLOSE_AT_EOS|SMB_FIND_RETURN_RESUME_KEYS|SMB_FIND_CONTINUE_FROM_LAST);
}
p += 12;
if(server->unicode_enabled)
{
copy_latin1_to_utf16le(p,mask_size,mask,mask_len);
}
else
{
memcpy (p, mask, mask_len);
p[mask_len] = '\0';
}
result = smb_trans2_request (server, SMBtrans2, &resp_data_len, &resp_param_len, &resp_data, &resp_param, error_ptr);
LOG (("smb_trans2_request returns %ld\n", result));
/* If an error was flagged, check is_first if it's a protocol
* error which could handle below. Otherwise, try again.
*/
if (result < 0 && (*error_ptr) != error_check_smb_error)
{
if (smb_retry (server))
goto retry;
LOG (("got error from trans2_request\n"));
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 there's a protocol error, stop and abort. */
if (server->rcls != 0)
{
LOG (("server->rcls = %ld err = %ld\n",server->rcls, server->err));
smb_printerr (server->rcls, server->err);
(*error_ptr) = error_check_smb_error;
result = -1;
break;
}
/* Bail out if this is empty. */
if (resp_param == NULL)
break;
/* parse out some important return info */
p = resp_param;
if (is_first)
{
ff_dir_handle = WVAL (p, 0);
ff_searchcount = WVAL (p, 2);
ff_end_of_search = WVAL (p, 4);
}
else
{
ff_searchcount = WVAL (p, 0);
ff_end_of_search = WVAL (p, 2);
}
LOG (("received %ld entries (end_of_search=%ld)\n",ff_searchcount, ff_end_of_search));
if (ff_searchcount == 0)
break;
/* 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++, p += entry_length)
{
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(len + 1 > mask_buffer_size)
{
/* Grow the buffer in steps of 16 bytes,
* so that we won't have to reallocate it
* over and over again if it keeps growing
* in smaller portions only.
*/
const int grow_size_by = 16;
D(("increasing mask; old value = %ld new value = %ld",mask_buffer_size,len + grow_size_by));
mask_buffer_size = len + grow_size_by;
SHOWVALUE(mask_buffer_size);
if(mask != NULL)
free (mask);
mask = malloc (mask_buffer_size);
if (mask == NULL)
{
LOG (("Memory allocation failed\n"));
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
}
memcpy (mask, last_name, len);
mask[len] = '\0';
}
mask_len = len;
}
if (total_count < fpos)
{
smb_decode_long_dirent (server, p, NULL, info_level, &entry_length);
if(entry_length == 0)
break;
LOG (("skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos));
}
else if (total_count >= fpos + cache_size)
{
smb_decode_long_dirent (server, p, NULL, info_level, &entry_length);
if(entry_length == 0)
break;
LOG (("skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos));
continue;
}
else
{
/* Skip this entry if we cannot decode the name. This could happen
* if the name will no fit into the buffer.
*/
if(!smb_decode_long_dirent (server, p, current_entry, info_level, &entry_length))
{
if(entry_length == 0)
break;
LOG (("skipped entry; total_count = %ld, i = %ld, fpos = %ld\n",total_count, i, fpos));
continue;
}
current_entry += 1;
}
total_count += 1;
if(entry_length == 0)
break;
}
SHOWVALUE(ff_resume_key);
if (resp_data != NULL)
{
free (resp_data);
resp_data = NULL;
}
if (resp_param != NULL)
{
free (resp_param);
resp_param = NULL;
}
is_first = FALSE;
if (ff_searchcount > 0)
loop_count = 0;
}
out:
/* finished: not needed any more */
if (mask != NULL)
free (mask);
if (resp_data != NULL)
free (resp_data);
if (resp_param != NULL)
free (resp_param);
if(result == 0)
result = total_count - fpos;
RETURN(result);
return(result);
}
int
smb_proc_readdir (struct smb_server *server, const char *path, int fpos, int cache_size, struct smb_dirent *entry, int * error_ptr)
{
int result;
if (server->protocol >= PROTOCOL_LANMAN2)
result = smb_proc_readdir_long (server, path, fpos, cache_size, entry, error_ptr);
else
result = smb_proc_readdir_short (server, path, fpos, cache_size, entry, error_ptr);
return result;
}
int
smb_proc_getattr_core (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr)
{
int result;
char *p;
char *buf = server->transmit_buffer;
LOG (("path='%s'\n", escape_name(path)));
ASSERT( smb_payload_size(server, 0, 2 + len) >= 0 );
retry:
p = smb_setup_header (server, SMBgetatr, 0, 2 + len);
smb_encode_ascii (p, path, len);
result = smb_request_ok (server, SMBgetatr, 10, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
else
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_low = DVAL (buf, smb_vwv3);
entry->size_high = 0;
#if DEBUG
{
struct tm tm;
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));
}
#endif /* DEBUG */
out:
return result;
}
int
smb_query_path_information(struct smb_server *server, const char *path, int len, int fid, struct smb_dirent *entry, int * error_ptr)
{
unsigned char *outbuf = server->transmit_buffer;
dword ext_file_attributes;
dword end_of_file_low;
dword end_of_file_high;
int parameter_count;
char * p;
int result;
char *resp_data = NULL;
int resp_data_len = 0;
int path_size;
retry:
if(len > 0)
{
if(server->unicode_enabled)
path_size = 2 * (len+1);
else
path_size = len+1;
parameter_count = 2 + 4 + path_size;
}
else
{
parameter_count = 2 + 2;
path_size = 0;
}
ASSERT( smb_payload_size(server, 15, 3 + parameter_count) >= 0 );
smb_setup_header (server, SMBtrans2, 15, 3 + parameter_count);
WSET (outbuf, smb_tpscnt, parameter_count); /* TotalParameterCount */
WSET (outbuf, smb_tdscnt, 0); /* TotalDataCount */
WSET (outbuf, smb_mprcnt, 0); /* MaxParameterCount */
WSET (outbuf, smb_mdrcnt, server->max_recv); /* MaxDataCount */
WSET (outbuf, smb_msrcnt, 0); /* MaxSetupCount+Reserved1 */
WSET (outbuf, smb_flags, 0); /* Flags */
DSET (outbuf, smb_timeout, 0); /* Timeout */
/* Reserved2 */
WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); /* ParameterCount */
WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - NETBIOS_HEADER_SIZE); /* ParameterOffset */
WSET (outbuf, smb_dscnt, 0); /* DataCount */
WSET (outbuf, smb_dsoff, 0); /* DataOffset */
WSET (outbuf, smb_suwcnt, 1); /* SetupCount+Reserved3 */
WSET (outbuf, smb_setup0, len > 0 ? TRANSACT2_QPATHINFO : TRANSACT2_QFILEINFO); /* Setup[0] */
p = SMB_BUF (outbuf);
/* Align to a 4-byte-boundary. */
(*p++) = '\0';
(*p++) = '\0';
(*p++) = '\0';
if(len > 0)
{
WSET (p, 0, SMB_QUERY_FILE_ALL_INFO);
DSET (p, 2, 0);
p += 2 + 4;
if(server->unicode_enabled)
{
copy_latin1_to_utf16le(p,path_size,path,len);
}
else
{
memcpy(p,path,len);
p[len] = '\0';
}
}
else
{
WSET (p, 0, fid);
WSET (p, 2, SMB_QUERY_FILE_ALL_INFO);
}
result = smb_trans2_request (server, SMBtrans2, &resp_data_len, NULL, &resp_data, NULL, error_ptr);
if (result < 0)
{
if((*error_ptr) != error_check_smb_error)
{
if (smb_retry (server))
goto retry;
}
goto out;
}
if(server->rcls != 0)
{
(*error_ptr) = error_check_smb_error;
result = -1;
goto out;
}
ASSERT( resp_data != NULL );
p = resp_data;
p += 2 * sizeof(dword); /* CreateTime */
p += 2 * sizeof(dword); /* LastAccessTime */
p += 2 * sizeof(dword); /* LastWriteTime */
entry->ctime = entry->atime = entry->mtime = entry->wtime = local2utc (convert_long_date_to_time_t(p));
p += 2 * sizeof(dword); /* LastChangeTime */
p = smb_decode_dword(p, &ext_file_attributes);
entry->attr = ext_file_attributes;
p += sizeof(dword); /* Reserved */
p += 2 * sizeof(dword); /* AllocationSize */
p = smb_decode_dword(p, &end_of_file_low);
(void) smb_decode_dword(p, &end_of_file_high);
entry->size_low = end_of_file_low;
entry->size_high = end_of_file_high;
#if DEBUG
{
struct tm tm;
QUAD entry_size_quad;
entry_size_quad.Low = entry->size_low;
entry_size_quad.High = entry->size_high;
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));
LOG(("size = %s (0x%08lx:0x%08lx)\n",convert_quad_to_string(&entry_size_quad),entry->size_high,entry->size_low));
LOG(("attr = 0x%08lx\n",entry->attr));
}
#endif /* DEBUG */
out:
if(resp_data != NULL)
free(resp_data);
return result;
}
/* smb_proc_getattrE: entry->fid must be valid */
int
smb_proc_getattrE (struct smb_server *server, struct smb_dirent *entry, int * error_ptr)
{
char *buf = server->transmit_buffer;
int result;
retry:
smb_setup_header (server, SMBgetattrE, 1, 0);
WSET (buf, smb_vwv0, entry->fileid);
result = smb_request_ok (server, SMBgetattrE, 11, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
else
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_low = DVAL (buf, smb_vwv6);
entry->size_high = 0;
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;
}
int
smb_set_file_information(struct smb_server *server, struct smb_dirent *entry, const QUAD * const size_quad, int * error_ptr)
{
unsigned char *outbuf = server->transmit_buffer;
int data_size;
char * p;
int result;
QUAD change_time_quad;
retry:
data_size = (size_quad == NULL) ? 40 : 8;
ASSERT( smb_payload_size(server, 15, 3 + 6 + 2 + data_size) >= 0 );
smb_setup_header (server, SMBtrans2, 15, 3 + 6 + 2 + data_size);
WSET (outbuf, smb_tpscnt, 6); /* TotalParameterCount */
WSET (outbuf, smb_tdscnt, data_size); /* TotalDataCount */
WSET (outbuf, smb_mprcnt, 0); /* MaxParameterCount */
WSET (outbuf, smb_mdrcnt, server->max_recv); /* MaxDataCount */
WSET (outbuf, smb_msrcnt, 0); /* MaxSetupCount+Reserved1 */
WSET (outbuf, smb_flags, 0); /* Flags */
DSET (outbuf, smb_timeout, 0); /* Timeout */
/* Reserved2 */
WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); /* ParameterCount */
WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - NETBIOS_HEADER_SIZE); /* ParameterOffset */
WSET (outbuf, smb_dscnt, WVAL (outbuf, smb_tdscnt)); /* DataCount */
WSET (outbuf, smb_dsoff, WVAL (outbuf, smb_psoff) + 6+2); /* DataOffset */
WSET (outbuf, smb_suwcnt, 1); /* SetupCount+Reserved3 */
WSET (outbuf, smb_setup0, TRANSACT2_SETFILEINFO); /* Setup[0] */
p = SMB_BUF (outbuf);
/* Align to a 4-byte-boundary. */
(*p++) = '\0';
(*p++) = '\0';
(*p++) = '\0';
WSET (p, 0, entry->fileid);
WSET (p, 2, size_quad == NULL ? SMB_SET_FILE_BASIC_INFO : SMB_SET_FILE_END_OF_FILE_INFO);
WSET (p, 4, 0);
p += 6;
/* Align to a 4-byte-boundary. */
(*p++) = '\0';
(*p++) = '\0';
if(size_quad == NULL)
{
/* Creation time (no change) */
DSET(p, 0, 0);
DSET(p, 4, 0);
/* Last access time (no change) */
DSET(p, 8, 0);
DSET(p, 12, 0);
convert_time_t_to_long_date(entry->mtime,&change_time_quad);
/* Last write time */
DSET(p, 16, change_time_quad.Low);
DSET(p, 20, change_time_quad.High);
/* Change time */
DSET(p, 24, change_time_quad.Low);
DSET(p, 28, change_time_quad.High);
/* Extended file attributes */
DSET(p, 32, entry->attr);
/* Reserved */
DSET(p, 36, 0);
}
else
{
/* End of file */
DSET(p, 0, size_quad->Low);
DSET(p, 4, size_quad->High);
}
result = smb_trans2_request (server, SMBtrans2, NULL, NULL, NULL, NULL, error_ptr);
if (result < 0)
{
if((*error_ptr) != error_check_smb_error)
{
if (smb_retry (server))
{
if(reopen_entry(server,entry,NULL) < 0)
{
LOG(("that didn't work.\n"));
goto out;
}
else
{
goto retry;
}
}
}
goto out;
}
if(server->rcls != 0)
{
(*error_ptr) = error_check_smb_error;
result = -1;
}
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, const struct smb_dirent *new_finfo, int * error_ptr)
{
char *p;
char *buf = server->transmit_buffer;
int result;
int local_time;
ASSERT( smb_payload_size(server, 8, 4 + len) >= 0 );
retry:
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);
result = smb_request_ok (server, SMBsetatr, 0, 0, error_ptr);
if (result < 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, int * error_ptr)
{
char *buf = server->transmit_buffer;
word date, time_value;
int result;
retry:
smb_setup_header (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 (server, SMBsetattrE, 0, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
{
if(reopen_entry(server,new_entry,NULL) < 0)
LOG(("that didn't work.\n"));
else
goto retry;
}
}
return result;
}
int
smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr, int * error_ptr)
{
char *resp_data = NULL;
int resp_data_len = 0;
int result;
char *p;
retry:
if (server->protocol >= PROTOCOL_NT1)
{
unsigned char *outbuf = server->transmit_buffer;
dword total_allocation_units_low;
dword total_free_allocation_units_low;
dword sectors_per_allocation_unit;
dword bytes_per_sector;
ASSERT( smb_payload_size(server, 15, 3 + 2) >= 0 );
smb_setup_header (server, SMBtrans2, 15, 3 + 2);
WSET (outbuf, smb_tpscnt, 2); /* TotalParameterCount */
WSET (outbuf, smb_tdscnt, 0); /* TotalDataCount */
WSET (outbuf, smb_mprcnt, 0); /* MaxParameterCount */
WSET (outbuf, smb_mdrcnt, server->max_recv); /* MaxDataCount */
WSET (outbuf, smb_msrcnt, 0); /* MaxSetupCount+Reserved1 */
WSET (outbuf, smb_flags, 0); /* Flags */
DSET (outbuf, smb_timeout, 0); /* Timeout */
/* Reserved2 */
WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); /* ParameterCount */
WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - NETBIOS_HEADER_SIZE); /* ParameterOffset */
WSET (outbuf, smb_dscnt, 0); /* DataCount */
WSET (outbuf, smb_dsoff, 0); /* DataOffset */
WSET (outbuf, smb_suwcnt, 1); /* SetupCount+Reserved3 */
WSET (outbuf, smb_setup0, TRANSACT2_QFSINFO); /* Setup[0] */
p = SMB_BUF (outbuf);
/* Align to a 4-byte-boundary. */
(*p++) = '\0';
(*p++) = '\0';
(*p++) = '\0';
WSET (p, 0, SMB_QUERY_FS_SIZE_INFO);
result = smb_trans2_request (server, SMBtrans2, &resp_data_len, NULL, &resp_data, NULL, error_ptr);
if (result < 0)
{
if((*error_ptr) != error_check_smb_error)
{
if (smb_retry (server))
goto retry;
}
goto out;
}
if(server->rcls != 0)
{
(*error_ptr) = error_check_smb_error;
result = -1;
goto out;
}
ASSERT( resp_data != NULL );
p = resp_data;
p = smb_decode_dword (p, &total_allocation_units_low);
p += sizeof(dword);
SHOWVALUE(total_allocation_units_low);
p = smb_decode_dword (p, &total_free_allocation_units_low);
p += sizeof(dword);
SHOWVALUE(total_free_allocation_units_low);
p = smb_decode_dword (p, &sectors_per_allocation_unit);
(void) smb_decode_dword (p, &bytes_per_sector);
SHOWVALUE(bytes_per_sector);
SHOWVALUE(sectors_per_allocation_unit);
attr->total = total_allocation_units_low;
attr->free = total_free_allocation_units_low;
attr->blocksize = bytes_per_sector;
attr->allocblocks = sectors_per_allocation_unit;
}
else
{
word total_units;
word sectors_per_unit;
word bytes_per_sector;
word free_units;
ASSERT( smb_payload_size(server, 0, 0) >= 0 );
smb_setup_header (server, SMBdskattr, 0, 0);
result = smb_request_ok (server, SMBdskattr, 5, 0, error_ptr);
if (result < 0)
{
if (smb_retry (server))
goto retry;
goto out;
}
p = SMB_VWV (server->transmit_buffer);
p = smb_decode_word (p, &total_units);
p = smb_decode_word (p, &sectors_per_unit);
p = smb_decode_word (p, &bytes_per_sector);
(void) smb_decode_word (p, &free_units);
attr->total = total_units;
attr->allocblocks = sectors_per_unit;
attr->blocksize = bytes_per_sector;
attr->free = free_units;
}
out:
if (resp_data != NULL)
free (resp_data);
return result;
}
/*****************************************************************************
*
* Mount/umount operations.
*
****************************************************************************/
struct smb_prots
{
enum smb_protocol prot;
const char * name;
};
static int
smb_proc_reconnect (struct smb_server *server, int * error_ptr)
{
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"},
};
/* Add a bit of fudge to account for the NetBIOS session
* header and whatever else might show up. How much space is
* required breaks down as follows:
*
* 4 bytes for the NetBIOS session header
* 32 bytes for the SMB frame header
* 1 byte for the parameter count
* x words (2 bytes each) for the parameters
* 1 word (2 bytes) for the number of bytes to transmit (payload)
* y bytes to transmit (payload)
*
* The parameter count (x) and the number of bytes to
* transmit may vary.
*
* What follows the 4 byte NetBIOS session header must not be
* larger than that an unsigned 17 bit integer can hold. It should
* not be much larger than 65535 bytes, though, since the transmission
* buffer only has some extra 1024 bytes of room at the end.
*/
const int safety_margin = 1024;
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;
int result;
word dialect_index;
byte *p;
unsigned char password[24];
int password_len;
unsigned char nt_password[24];
int nt_password_len;
char * share_name = NULL;
byte *packet;
dword max_buffer_size;
int packet_size;
result = smb_connect (server, error_ptr);
if (result < 0)
{
LOG (("could not smb_connect\n"));
goto out;
}
/* Unless we know better, we don't go for Unicode just yet. */
server->unicode_enabled = FALSE;
/* Here we assume that the connection is valid */
server->state = CONN_VALID;
server->max_buffer_size = default_max_buffer_size;
SHOWVALUE(server->max_buffer_size);
/* Minimum receive buffer size is 8000 bytes, but cannot
* be larger than 65535 bytes.
*/
if (0 < server->mount_data.given_max_xmit && server->mount_data.given_max_xmit < 8000)
given_max_xmit = 8000;
else if (0 < server->mount_data.given_max_xmit && server->mount_data.given_max_xmit < 65536)
given_max_xmit = server->mount_data.given_max_xmit;
else
given_max_xmit = 65535;
if (server->max_recv <= 0)
server->max_recv = given_max_xmit;
SHOWVALUE(server->max_recv);
/* We need to allocate memory for the buffer which is used both
* for reception and transmission of messages. This means that it has
* to be large enough to account for both uses. The maximum
* transmit buffer size reported by the server may be smaller than
* the maximum message size which smbfs is prepared to accept, or
* it could be the other way round.
*/
packet_size = server->max_recv;
if (packet_size < (int)server->max_buffer_size)
packet_size = (int)server->max_buffer_size;
SHOWVALUE(packet_size);
if (server->transmit_buffer != NULL)
free (server->transmit_buffer);
server->transmit_buffer_size = packet_size;
/* Add a bit of fudge to account for the NetBIOS session
* header and whatever else might show up...
*/
server->transmit_buffer_allocation_size = server->transmit_buffer_size + safety_margin;
server->transmit_buffer = malloc (server->transmit_buffer_allocation_size);
if (server->transmit_buffer == NULL)
{
LOG (("No memory! Bailing out.\n"));
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
packet = server->transmit_buffer;
/* Prepend a NetBIOS header? */
if(!server->raw_smb)
{
/* Start with an RFC1002 session request packet. */
p = packet + NETBIOS_HEADER_SIZE;
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 */
result = smb_request (server, 0, NULL, NULL, 0, error_ptr);
if (result < 0)
{
LOG (("Failed to send SESSION REQUEST.\n"));
goto out;
}
if (packet[0] != 0x82)
{
LOG (("Did not receive positive response (err = %lx)\n",packet[0]));
#if DEBUG
{
smb_dump_packet (packet);
}
#endif /* DEBUG */
(*error_ptr) = error_session_request_failed;
result = -1;
goto out;
}
LOG (("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 += 1 + strlen (prots[i].name) + 1;
ASSERT( smb_payload_size(server, 0, plength) >= 0 );
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 (("Request SMBnegprot...\n"));
result = smb_request_ok (server, SMBnegprot, 1, -1, error_ptr);
if (result < 0)
{
LOG (("Failure requesting SMBnegprot\n"));
goto out;
}
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 (("Unsupported dialect\n"));
(*error_ptr) = error_unsupported_dialect;
result = -1;
goto out;
}
server->protocol = prots[dialect_index].prot;
LOG (("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 (("password = %s\n",server->mount_data.password*));
*/
LOG (("usernam = %s\n",server->mount_data.username));
LOG (("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);
SHOWVALUE (max_buffer_size);
#if defined(OVERRIDE_SERVER_MAX_BUFFER_SIZE)
{
max_buffer_size = OVERRIDE_SERVER_MAX_BUFFER_SIZE;
}
#endif /* OVERRIDE_SERVER_MAX_BUFFER_SIZE */
p = smb_decode_dword(p, &server->max_raw_size);
SHOWVALUE (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);
SHOWVALUE (max_buffer_size);
#if defined(OVERRIDE_SERVER_MAX_BUFFER_SIZE)
{
max_buffer_size = OVERRIDE_SERVER_MAX_BUFFER_SIZE;
}
#endif /* OVERRIDE_SERVER_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 (("workgroup = %s\n", server->mount_data.workgroup_name));
/* NT LAN Manager or newer. */
if (server->protocol >= PROTOCOL_NT1)
{
const char *native_os = "AmigaOS";
const char *native_lanman = VERS;
dword capabilities;
SHOWMSG("server->protocol >= PROTOCOL_NT1");
ASSERT( smb_payload_size(server, 13, user_len + password_len + nt_password_len + strlen (server->mount_data.workgroup_name)+1 + strlen (native_os)+1 + strlen (native_lanman)+1) >= 0 );
smb_setup_header (server, SMBsesssetupX, 13, user_len + password_len + nt_password_len + strlen (server->mount_data.workgroup_name)+1 + strlen (native_os)+1 + strlen (native_lanman)+1);
capabilities = CAP_LARGE_READX|CAP_LARGE_WRITEX|CAP_NT_FIND|CAP_LARGE_FILES;
if(server->use_unicode && (server->capabilities & CAP_UNICODE) != 0)
{
SHOWMSG("Unicode support enabled");
server->unicode_enabled = TRUE;
capabilities |= CAP_UNICODE;
}
WSET (packet, smb_vwv0, 0xff); /* AndXCommand+AndXReserved */
WSET (packet, smb_vwv1, 0); /* AndXOffset */
WSET (packet, smb_vwv2, given_max_xmit); /* MaxBufferSize */
WSET (packet, smb_vwv3, 2); /* MaxMpxCount */
WSET (packet, smb_vwv4, 0); /* VcNumber */
DSET (packet, smb_vwv5, server_sesskey); /* SessionKey */
WSET (packet, smb_vwv7, password_len); /* OEMPasswordLen */
WSET (packet, smb_vwv8, nt_password_len); /* UnicodePasswordLen */
DSET (packet, smb_vwv9, 0); /* Reserved */
DSET (packet, smb_vwv11, capabilities); /* Capabilities */
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, native_os);
p += strlen (p) + 1;
strcpy (p, native_lanman);
}
/* LAN Manager 2.0 or older */
else
{
ASSERT( smb_payload_size(server, 10, user_len + password_len) >= 0 );
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);
}
result = smb_request_ok (server, SMBsesssetupX, 3, 0, error_ptr);
if (result < 0)
{
LOG (("SMBsessetupX failed\n"));
goto out;
}
smb_decode_word (packet + 32, &server->server_uid);
}
else
{
max_buffer_size = server->max_buffer_size;
#if defined(OVERRIDE_SERVER_MAX_BUFFER_SIZE)
{
max_buffer_size = OVERRIDE_SERVER_MAX_BUFFER_SIZE;
}
#endif /* OVERRIDE_SERVER_MAX_BUFFER_SIZE */
server->capabilities = 0;
password_len = strlen(server->mount_data.password)+1;
nt_password_len = 0;
}
if(nt_password_len > 0)
{
int server_name_len;
int service_len;
int share_name_len;
int share_name_size;
int padding_needed;
server_name_len = strlen(server->mount_data.server_name);
service_len = strlen(server->mount_data.service);
share_name_len = 2 + server_name_len + 1 + service_len;
share_name = malloc(share_name_len+1);
if(share_name == NULL)
{
LOG (("No memory! Bailing out.\n"));
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
memcpy(share_name,"//",2);
memcpy(&share_name[2],server->mount_data.server_name,server_name_len);
share_name[2 + server_name_len] = '/';
memcpy(&share_name[2 + server_name_len + 1],server->mount_data.service,service_len+1); /* Length includes terminating NUL byte. */
/* Flip the slashes, so they become backslashes. */
for(i = 0 ; i < share_name_len ; i++)
{
if(share_name[i] == '/')
share_name[i] = '\\';
}
StringToUpper(share_name);
D(("share_name = '%s'", escape_name(share_name)));
if(server->unicode_enabled)
share_name_size = 2 * (share_name_len + 1);
else
share_name_size = share_name_len + 1;
if(server->unicode_enabled && (password_len % 2) == 0)
padding_needed = 1;
else
padding_needed = 0;
ASSERT( smb_payload_size(server, 4, password_len + padding_needed + share_name_size + strlen(dev)+1) >= 0 );
smb_setup_header (server, SMBtconX, 4, password_len + padding_needed + share_name_size + strlen(dev)+1);
WSET (packet, smb_vwv0, 0xFF);
WSET (packet, smb_vwv3, password_len);
p = SMB_BUF (packet);
memcpy(p,password,password_len);
p += password_len;
if(padding_needed)
(*p++) = 0;
if(server->unicode_enabled)
copy_latin1_to_utf16le(p,share_name_size,share_name,share_name_len);
else
memcpy(p,share_name,share_name_size);
p += share_name_size;
strcpy(p,dev);
result = smb_request_ok (server, SMBtconX, 3, 0, error_ptr);
if (result < 0)
{
SHOWVALUE(SMB_WCT(packet));
LOG (("SMBtconX not verified.\n"));
goto out;
}
SHOWVALUE(SMB_WCT(packet));
server->tid = WVAL(packet,smb_tid);
}
else
{
word decoded_max_xmit;
ASSERT( smb_payload_size(server, 0, 6 + strlen (server->mount_data.service) + strlen (server->mount_data.password) + strlen (dev)) >= 0 );
/* 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));
result = smb_request_ok (server, SMBtcon, 2, 0, error_ptr);
if (result < 0)
{
LOG (("SMBtcon not verified.\n"));
goto out;
}
LOG (("OK! Managed to set up SMBtcon!\n"));
p = SMB_VWV (packet);
p = smb_decode_word (p, &decoded_max_xmit);
max_buffer_size = decoded_max_xmit;
#if defined(OVERRIDE_SERVER_MAX_BUFFER_SIZE)
{
max_buffer_size = OVERRIDE_SERVER_MAX_BUFFER_SIZE;
}
#endif /* OVERRIDE_SERVER_MAX_BUFFER_SIZE */
(void) smb_decode_word (p, &server->tid);
}
LOG (("max_buffer_size = %ld, tid = %ld\n", max_buffer_size, server->tid));
/* Let's get paranoid. Make sure that we can actually receive
* as much data as the buffer size allows, in a single consecutive
* buffer which follows the SMB message header.
*/
if(packet_size < (int)max_buffer_size)
{
SHOWVALUE(packet_size);
/* We need to allocate a larger packet buffer. */
packet_size = max_buffer_size;
D(("packet size updated to %ld bytes\n", packet_size));
free (server->transmit_buffer);
server->transmit_buffer_size = packet_size;
/* Add a bit of fudge to account for the NetBIOS session
* header and whatever else might show up...
*/
server->transmit_buffer_allocation_size = server->transmit_buffer_size + safety_margin;
server->transmit_buffer = malloc (server->transmit_buffer_allocation_size);
if (server->transmit_buffer == NULL)
{
LOG (("No memory! Bailing out.\n"));
(*error_ptr) = ENOMEM;
result = -1;
goto out;
}
}
/* Finally, limit the amount of data to send to the server,
* if requested.
*/
if(8000 <= server->mount_data.given_max_xmit && server->mount_data.given_max_xmit < (int)max_buffer_size)
{
max_buffer_size = server->mount_data.given_max_xmit;
D(("maximum buffer size limited to %ld bytes\n", max_buffer_size));
}
server->max_buffer_size = max_buffer_size;
LOG (("Normal exit\n"));
result = 0;
out:
if(share_name != NULL)
free(share_name);
if(result < 0)
server->state = CONN_INVALID;
return result;
}
/* smb_proc_reconnect: server->transmit_buffer is allocated with
server->max_buffer_size bytes if and only if we return >= 0 */
int
smb_proc_connect (struct smb_server *server, int * error_ptr)
{
int result;
result = smb_proc_reconnect (server, error_ptr);
if ((result < 0) && (server->transmit_buffer != NULL))
{
free (server->transmit_buffer);
server->transmit_buffer = 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));
}