mirror of
https://github.com/obarthel/amiga-smbfs.git
synced 2025-12-08 14:58:35 +00:00
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.
4381 lines
110 KiB
C
4381 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 int
|
|
utc2local (int time_value)
|
|
{
|
|
int result;
|
|
|
|
if(time_value > 0)
|
|
result = time_value - get_time_zone_delta();
|
|
else
|
|
result = time_value;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
local2utc (int time_value)
|
|
{
|
|
int result;
|
|
|
|
if(time_value > 0)
|
|
result = time_value + get_time_zone_delta();
|
|
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 = tm_to_seconds(&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;
|
|
|
|
seconds_to_tm(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 = convert_long_date_to_time_t(params);
|
|
params += 2 * sizeof(dword); /* CreateTime */
|
|
|
|
entry->atime = convert_long_date_to_time_t(params);
|
|
params += 2 * sizeof(dword); /* LastAccessTime */
|
|
|
|
entry->wtime = convert_long_date_to_time_t(params);
|
|
params += 2 * sizeof(dword); /* LastWriteTime */
|
|
|
|
entry->mtime = 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 just 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 != (dword)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)
|
|
{
|
|
dword local_time = utc2local (entry->ctime);
|
|
int result;
|
|
char *p;
|
|
char *buf = server->transmit_buffer;
|
|
|
|
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);
|
|
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;
|
|
|
|
seconds_to_tm(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 just the mtime */
|
|
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;
|
|
|
|
seconds_to_tm(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;
|
|
|
|
entry->ctime = convert_long_date_to_time_t(p);
|
|
p += 2 * sizeof(dword); /* CreateTime */
|
|
|
|
entry->atime = convert_long_date_to_time_t(p);
|
|
p += 2 * sizeof(dword); /* LastAccessTime */
|
|
|
|
p += 2 * sizeof(dword); /* LastWriteTime */
|
|
|
|
entry->mtime = 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;
|
|
|
|
seconds_to_tm(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;
|
|
|
|
seconds_to_tm(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));
|
|
|
|
seconds_to_tm(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));
|
|
|
|
seconds_to_tm(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));
|
|
|
|
seconds_to_tm(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)
|
|
{
|
|
dword local_time = utc2local (new_finfo->mtime);
|
|
char *p;
|
|
char *buf = server->transmit_buffer;
|
|
int result;
|
|
|
|
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);
|
|
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, §ors_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, §ors_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 connect to server\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] = '\\';
|
|
}
|
|
|
|
string_toupper(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)
|
|
{
|
|
report_error("%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;
|
|
|
|
report_error ("%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;
|
|
}
|
|
}
|
|
|
|
report_error ("Unknown error - (%ld, %ld).", class, num);
|
|
|
|
LOG (("Unknown error - (%ld, %ld)\n", class, num));
|
|
}
|