diff --git a/documentation/history.doc b/documentation/history.doc index 0dfebfb..6df0314 100644 --- a/documentation/history.doc +++ b/documentation/history.doc @@ -1437,3 +1437,134 @@ smbfs 1.130 (13.5.2018) finally produce the kind of insights which so far have been denied to us during long data copying sessions which either stalled or stopped for no apparent reason. + + +smbfs 1.131 (14.5.2018) + +- Hitting Ctrl+C (or using the Break shell command) no longer + allows smbfs to shut down and then retry to open the connection + to the server. + +- smb_proc_read(), smb_proc_read_raw(), smb_proc_write(), + smb_proc_write_raw(), smb_proc_create(), smb_proc_trunc(), + smb_proc_getattrE(), smb_proc_setattrE() and smb_proc_dskattr() + now try to reconnect to the server if the connection + was broken by the previous network access. + +- The reconnection attempt no longer overwrites the previously + recorded error code if it fails to succeed. + + +smbfs 1.132 (19.5.2018) + +- If raw SMB mode is not used, the NetBIOS session setup now works + again. + +- Reading directories which contain long names supports only the two + documented entry formats now. All the other formats which might or + might not have been supported by OS/2, etc. are gone now. This + simplified the whole overly complex directory scanner code. + +- Directory entries whose names are too long are now skipped while + reading the directory, rather than getting truncated. + +- Replaced the deprecated commands SMB_COM_CREATE, SMB_COM_OPEN, + SMB_COM_QUERY_INFORMATION_DISK, SMB_COM_QUERY_INFORMATION, + SMB_COM_CREATE_DIRECTORY and SMB_COM_SET_INFORMATION with their + CIFS counterparts. The old commands are still in place, on the + off chance that you just might want to connect smbfs to an OS/2 + or Windows 3 server. + +- If possible, SMB_COM_WRITE_ANDX and SMB_COM_READ_ANDX will now + be used in place of the older SMB_COM_WRITE/SMB_COM_WRITE_RAW + and SMB_COM_READ/SMB_COM_READ_RAW commands. This makes both + read/write operations much simpler than they used to be. + +- Just to repeat the point here: smbfs no longer uses any SMB + commands which the CIFS documentation describes as either + "deprecated" or "obsolete" (unless the server which smbfs + connected to does not support the LAN Manager 2.0 or + NT LAN Manager protocols). + +- Removed the use of the SMB_COM_SEEK command, which wasn't doing + anything useful in the first place: none of the read/write commands + actually required it (this is the one command which the CIFS + documentation labeled as "obsolete"). + +- ACTION_SET_FILE_SIZE now actually works: you can make files both + shorter or longer, as needed. Previously, you could not make + files any shorter, only longer. + +- Added the NATIVEOS option, which allows you to override the + default of "AmigaOS", in case this should be necessary. + +- smbfs now reports itself ("Native LAN Manager") as "smbfs" with + the version number tacked on ("smbfs 1.132"). + +- Changed the translation of the POSIX error codes EPERM (operation + not permitted) and EACCESS (permission denied) from + "object not found" to "read protected". This is less misleading, + but not much clearer either. Pity that we don't have an AmigaDOS + error code which matches the blanket "you can't do that" error. + Anyway, if it says "read protected" it now indicates a permission + problem, rather than suggesting some file or directory does not + exist. + +- Changed the translation of the POSIX error codes EADDRNOTAVAIL, + ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED, ECONNRESET, + EHOSTDOWN and EHOSTUNREACH from "object not found" to + "directory not found". This is still a poor match for the + purpose of the error flagged, but at least you'll get an idea + that something isn't accessible on the network, rather than + file or directory might be missing. + +- Open(...,MODE_NEWFILE), which translates into ACTION_FINDOUTPUT, + can finally truncate an existing file, or will create it if it + does not exist. This uses the SMB_COM_NT_CREATE_ANDX command, if + available (too bad for OS/2, etc.). + +- Simplified the Seek() handling, because the formerly underlying + SMB_COM_SEEK was basically only slightly worse than useless. + +- Added another (so far) unaccounted error code (123) to the list of + errors for which we have labels. + +- Verified that the directory scanner built on top of the + SMB_COM_TRANSACTION2+TRANS2_FIND_FIRST2/TRANS2_FIND_NEXT2 + commands actually does work. The SMB_COM_TRANSACTION2 message + it set up was two bytes longer than it should have been, + though. + +- All file operations (reading, writing, setting the file size, + obtaining the size of a file, reading directory entries) now + support file sizes larger than 4 Gigabytes. In theory anyway, + because the AmigaDOS API is currently restricted to just + 2 Gigabytes. Should the need arise, the 64 bit integer values + could be made to do the job they were intended for. Note that + file sizes > 4 Gigabytes are currently "clipped", which means + that only the least significant 32 bits are used. There is + is currently no weird workaround in place to avoid the side-effects + of this policy. + +- Updated the 64 bit integer math operations for the SetFileDate() + function, which now has to deal with the SMB_COM_TRANSACTION2 + TRANS2_SET_FILE_INFORMATION command. TRANS2_SET_FILE_INFORMATION + uses 64 bit date/time. + +- Opening a file or directory, which is something akin to the + AmigaDOS Lock() operation, now defaults to read access unless + write access is required. The previous implementation always + opened for read/write access, and if that didn't work, tried + again with read access only. The read/write access is rarely + needed, but is mandatory for file/directory delete operations. + +- Manipulating file/directory attributes (which consists of + flipping the "protection bits" for "read-only" and "was + archived" as needed) is much simplified and no longer + requires figuring out if the OS/2 or MS-DOS server at the + other end of the connection can handle extended file + attributes (unless you desperately want to, of course). + +- Changing the attributes of a file/directory (as in: flip the + "read-only" and "was archived" bits) is now distinct from + changing the size of a file. diff --git a/source_code/GNUmakefile.68k b/source_code/GNUmakefile.68k index d4ae847..b4dc072 100644 --- a/source_code/GNUmakefile.68k +++ b/source_code/GNUmakefile.68k @@ -71,7 +71,7 @@ cp850.o : cp850.c crypt.o : crypt.c system_headers.h assert.h smbfs.h dump_smb.o : dump_smb.c system_headers.h assert.h smbfs.h dump_smb.h quad_math.h main.o : main.c system_headers.h assert.h smbfs.h smb_abstraction.h utf-8-iso-8859-1-conversion.h cp437.h cp850.h errors.h dump_smb.h smbfs_rev.h -proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h +proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h smbfs_rev.h quad_math.o : quad_math.c quad_math.h smb_abstraction.o : smb_abstraction.c system_headers.h assert.h smbfs.h smb_abstraction.h sock.o : sock.c system_headers.h assert.h smbfs.h smb_abstraction.h dump_smb.h errors.h @@ -80,4 +80,4 @@ utf-8-iso-8859-1-conversion.o : utf-8-iso-8859-1-conversion.c utf-8-iso-8859-1-c ############################################################################### clean: - -delete #?.o $(PROJECT)(%|.debug) + -delete #?.o #?.map $(PROJECT)(%|.debug) diff --git a/source_code/GNUmakefile.os4 b/source_code/GNUmakefile.os4 index 10e586a..7c831a2 100644 --- a/source_code/GNUmakefile.os4 +++ b/source_code/GNUmakefile.os4 @@ -76,7 +76,7 @@ cp850.o : cp850.c crypt.o : crypt.c system_headers.h assert.h smbfs.h dump_smb.o : dump_smb.c system_headers.h assert.h smbfs.h dump_smb.h quad_math.h main.o : main.c system_headers.h assert.h smbfs.h smb_abstraction.h utf-8-iso-8859-1-conversion.h cp437.h cp850.h errors.h dump_smb.h smbfs_rev.h -proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h +proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h smbfs_rev.h quad_math.o : quad_math.c quad_math.h smb_abstraction.o : smb_abstraction.c system_headers.h assert.h smbfs.h smb_abstraction.h sock.o : sock.c system_headers.h assert.h smbfs.h smb_abstraction.h dump_smb.h errors.h @@ -85,4 +85,4 @@ utf-8-iso-8859-1-conversion.o : utf-8-iso-8859-1-conversion.c utf-8-iso-8859-1-c ########################################################################### clean: - rm *.o $(PROJECT) $(PROJECT).debug $(PROJECT).map + -rm *.o $(PROJECT) $(PROJECT).debug $(PROJECT).map diff --git a/source_code/include/smb/smb.h b/source_code/include/smb/smb.h old mode 100755 new mode 100644 index bc10585..23b54a1 --- a/source_code/include/smb/smb.h +++ b/source_code/include/smb/smb.h @@ -52,10 +52,10 @@ enum smb_conn_state struct smb_dskattr { - word total; - word allocblocks; - word blocksize; - word free; + dword total; + dword allocblocks; + dword blocksize; + dword free; }; /* @@ -65,7 +65,7 @@ struct smb_dirent { int opened; /* is it open on the fileserver? */ word fileid; /* What id to handle a file with? */ - word attr; /* Attribute fields, DOS value */ + dword attr; /* Attribute fields, DOS value */ time_t atime, mtime, wtime, ctime; /* Times, as seen by the server, normalized */ @@ -73,6 +73,7 @@ struct smb_dirent /* proc.c */ unsigned long size; /* File size. */ + char * complete_path; /* Complete path, MS-DOS notation, with '\' */ size_t complete_path_size; /* Number of bytes allocated for name */ int len; /* Namelength. */ diff --git a/source_code/include/smb/smb_fs.h b/source_code/include/smb/smb_fs.h index 08972e1..86d428a 100644 --- a/source_code/include/smb/smb_fs.h +++ b/source_code/include/smb/smb_fs.h @@ -47,13 +47,14 @@ int smb_len(const byte *packet); void smb_translate_error_class_and_code(int errcls,int error,char ** class_ptr,char ** code_ptr); int smb_errno(int errcls, int error); int smb_payload_size(const struct smb_server *server, int wct, int bcc); -int smb_proc_open(struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry, int * error_ptr); +int smb_proc_open(struct smb_server *server, const char *pathname, int len, int writable, int truncate, struct smb_dirent *entry, int * error_ptr); int smb_proc_close(struct smb_server *server, word fileid, dword mtime, int * error_ptr); int smb_proc_read(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int * error_ptr); int smb_proc_read_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int * error_ptr); int smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data, int * error_ptr); int smb_proc_write_raw(struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data, int * error_ptr); -int smb_proc_lseek (struct smb_server *server, struct smb_dirent *finfo, off_t offset, int mode, off_t * new_position_ptr, int * error_ptr); +int smb_proc_writex (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, const char *data, int * error_ptr); +int smb_proc_readx (struct smb_server *server, struct smb_dirent *finfo, off_t offset, long count, char *data, int * error_ptr); int smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct smb_lkrng *locks, int num_entries, int mode, long timeout, int * error_ptr); int smb_proc_create(struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr); int smb_proc_mv(struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen, int * error_ptr); @@ -64,6 +65,8 @@ int smb_proc_trunc(struct smb_server *server, word fid, dword length, int * erro int smb_proc_readdir(struct smb_server *server, char *path, int fpos, int cache_size, struct smb_dirent *entry, int * error_ptr); int smb_proc_getattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr); int smb_proc_getattrE(struct smb_server *server, struct smb_dirent *entry, int * error_ptr); +int smb_query_path_information(struct smb_server *server, const char *path, int len, int fid, struct smb_dirent *entry, int * error_ptr); +int smb_set_file_information(struct smb_server *server, struct smb_dirent *entry, const dword * size_ptr, int * error_ptr); int smb_proc_setattr_core(struct smb_server *server, const char *path, int len, struct smb_dirent *new_finfo, int * error_ptr); int smb_proc_setattrE(struct smb_server *server, word fid, struct smb_dirent *new_entry, int * error_ptr); int smb_proc_dskattr (struct smb_server *server, struct smb_dskattr *attr, int * error_ptr); @@ -73,8 +76,8 @@ int smb_proc_connect(struct smb_server *server, int * error_ptr); void smb_check_server_connection(struct smb_server *server, int error); void smb_release(struct smb_server *server); int smb_connect(struct smb_server *server, int * error_ptr); -int smb_request(struct smb_server *server,void * input_payload,const void * output_payload,int payload_size, int * error_ptr); -int smb_trans2_request(struct smb_server *server, int *data_len, int *param_len, char **data, char **param, int * error_ptr); +int smb_request(struct smb_server *server, int command, void * input_payload,const void * output_payload,int payload_size, int * error_ptr); +int smb_trans2_request(struct smb_server *server, int command, int *data_len, int *param_len, char **data, char **param, int * error_ptr); int smb_request_read_raw(struct smb_server *server, unsigned char *target, int max_len, int * error_ptr); int smb_request_write_raw(struct smb_server *server, unsigned const char *source, int length, int * error_ptr); diff --git a/source_code/include/smb/smb_fs_sb.h b/source_code/include/smb/smb_fs_sb.h index 13215c8..d1ed00e 100644 --- a/source_code/include/smb/smb_fs_sb.h +++ b/source_code/include/smb/smb_fs_sb.h @@ -65,6 +65,12 @@ struct smb_server /* olsen (2018-05-09): Timeout for send/receive operations in seconds. */ int timeout; + + /* olsen (2018-05-14): Don't retry establishing a server connection. */ + int dont_retry; + + /* olsen (2018-05-18): Override the "Native OS" name passed to the server. */ + char * native_os; }; #define NEGOTIATE_USER_SECURITY 0x01 /* If set, the server supports @@ -83,4 +89,10 @@ struct smb_server #define CAP_RAW_MODE 0x00000001 /* The server supports SMB_COM_WRITE_RAW and SMB_COM_READ_RAW requests. */ +#define CAP_LARGE_READX 0x00004000 /* The server permits SMB_COM_READ_ANDX + to read up to 65535 bytes. */ + +#define CAP_LARGE_WRITEX 0x00008000 /* The server permits SMB_COM_WRITE_ANDX + to write up to 65535 bytes. */ + #endif diff --git a/source_code/include/smb/smbno.h b/source_code/include/smb/smbno.h old mode 100755 new mode 100644 index 94f246f..5225b44 --- a/source_code/include/smb/smbno.h +++ b/source_code/include/smb/smbno.h @@ -51,6 +51,7 @@ #define ERRnoresource 89 #define ERRtoomanyuids 90 #define ERRbaduid 91 +#define ERRinvalidname 123 /* Object name collision */ #define ERRbadpipe 230 /* Named pipe invalid */ #define ERRpipebusy 231 /* All instances of pipe are busy */ #define ERRpipeclosing 232 /* named pipe close in progress */ @@ -265,28 +266,66 @@ #define SMBfclose 0x84 /* find close */ #define SMBinvalid 0xFE /* invalid command */ - -/* Extended 2.0 protocol */ +/* Extended 2.0 protocol (LAN Manager 2.0) */ #define SMBtrans2 0x32 /* TRANS2 protocol set */ #define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ #define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ #define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ #define SMBulogoffX 0x74 /* user logoff */ +/* NT LAN Manager */ +#define SMBntcreateX 0xA2 /* Create or open file */ + /* these are the TRANS2 sub commands */ #define TRANSACT2_OPEN 0 #define TRANSACT2_FINDFIRST 1 #define TRANSACT2_FINDNEXT 2 -#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_QFSINFO 3 /* TRANS2_QUERY_FS_INFORMATION */ #define TRANSACT2_SETFSINFO 4 -#define TRANSACT2_QPATHINFO 5 -#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QPATHINFO 5 /* TRANS2_QUERY_PATH_INFORMATION */ +#define TRANSACT2_SETPATHINFO 6 /* TRANS2_SET_PATH_INFORMATION */ #define TRANSACT2_QFILEINFO 7 #define TRANSACT2_SETFILEINFO 8 #define TRANSACT2_FSCTL 9 #define TRANSACT2_IOCTL 10 #define TRANSACT2_FINDNOTIFYFIRST 11 #define TRANSACT2_FINDNOTIFYNEXT 12 -#define TRANSACT2_MKDIR 13 +#define TRANSACT2_MKDIR 13 /* TRANS2_CREATE_DIRECTORY */ + +/* These are used by SMBntcreateX */ +#define FILE_READ_DATA 0x00000001 +#define FILE_WRITE_DATA 0x00000002 +#define FILE_READ_ATTRIBUTES 0x00000080 +#define FILE_WRITE_ATTRIBUTES 0x00000100 +#define FILE_DELETE 0x00001000 +#define GENERIC_WRITE 0x40000000 +#define GENERIC_READ 0x80000000 + +#define FILE_SHARE_READ 0x00000001 +#define FILE_SHARE_WRITE 0x00000002 + +#define SEC_ANONYMOUS 0x00000000 + +#define FILE_OPEN 0x00000001 +#define FILE_CREATE 0x00000002 +#define FILE_OVERWRITE_IF 0x00000005 + +#define NT_CREATE_REQUEST_OPBATCH 0x00000004 + +/* Directory entry formats supported by TRANSACT2_FINDFIRST/TRANSACT2_FINDNEXT */ +#define SMB_INFO_STANDARD 0x0001 /* LAN Manager 2.0 */ +#define SMB_FILE_BOTH_DIRECTORY_INFO 0x0104 /* NT LAN Manager */ + +/* File system information formats supported by TRANSACT2_QFSINFO (actually, + * we only need this one). + */ +#define SMB_QUERY_FS_SIZE_INFO 0x0103 + +/* Path information formats supported by TRANSACT2_QPATHINFO and TRANSACT2_QFILEINFO. */ +#define SMB_QUERY_FILE_ALL_INFO 0x0107 + +/* Set information formats supported by TRANSACT2_SETFILEINFO. */ +#define SMB_SET_FILE_BASIC_INFO 0x0101 /* NT LAN Manager */ +#define SMB_SET_FILE_END_OF_FILE_INFO 0x0104 /* NT LAN Manager */ #endif /* _SMBNO_H_ */ diff --git a/source_code/main.c b/source_code/main.c index 8ebd5fa..f1347fa 100644 --- a/source_code/main.c +++ b/source_code/main.c @@ -26,7 +26,7 @@ * copy "amiga:Public/Documents/Amiga Files/Shared/dir/Windows-Export/LP2NRFP.h" ram: * smbfs.debug user=guest volume=sicherung //192.168.1.76/sicherung-smb * smbfs maxtransmit=16600 debuglevel=2 dumpsmb dumpsmblevel=2 domain=workgroup user=olsen password=... volume=olsen //felix/olsen - * Samba 4.6.7: smbfs debuglevel=2 dumpsmb dumpsmblevel=2 volume=ubuntu-test //192.168.1.33/test + * Samba 4.6.7: smbfs debuglevel=2 dumpsmb dumpsmblevel=2 volume=ubuntu-test //ubuntu-17-olaf/test * Samba 3.0.25: smbfs debuglevel=2 dumpsmb dumpsmblevel=1 user=olsen password=... volume=olsen //192.168.1.118/olsen */ @@ -49,29 +49,6 @@ /****************************************************************************/ -/* A quick workaround for the timeval/timerequest->TimeVal/TimeRequest - change in the recent OS4 header files. */ -#if defined(__NEW_TIMEVAL_DEFINITION_USED__) - -#define timeval TimeVal -#define tv_secs Seconds -#define tv_micro Microseconds - -#define timerequest TimeRequest -#define tr_node Request -#define tr_time Time - -#endif /* __NEW_TIMEVAL_DEFINITION_USED__ */ - -/****************************************************************************/ - -/* This is for backwards compatibility only. */ -#if defined(__amigaos4__) -#define fib_EntryType fib_Obsolete -#endif /* __amigaos4__ */ - -/****************************************************************************/ - #include "smbfs_rev.h" STRPTR Version = VERSTAG; @@ -158,7 +135,7 @@ STATIC ULONG stack_usage_exit(const struct StackSwapStruct * stk); STATIC LONG CVSPrintf(STRPTR format_string, APTR args); STATIC VOID VSPrintf(STRPTR buffer, STRPTR formatString, APTR args); STATIC VOID Cleanup(VOID); -STATIC BOOL Setup(STRPTR program_name, STRPTR service, STRPTR workgroup, STRPTR username, STRPTR opt_password, BOOL opt_changecase, STRPTR opt_clientname, STRPTR opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, LONG *opt_time_zone_offset, LONG *opt_dst_offset, BOOL opt_raw_smb, BOOL opt_write_behind, BOOL opt_prefer_write_raw, BOOL opt_disable_write_raw, BOOL opt_disable_read_raw, STRPTR device_name, STRPTR volume_name, STRPTR translation_file); +STATIC BOOL Setup(STRPTR program_name, STRPTR service, STRPTR workgroup, STRPTR username, STRPTR opt_password, BOOL opt_changecase, STRPTR opt_clientname, STRPTR opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, LONG *opt_time_zone_offset, LONG *opt_dst_offset, BOOL opt_raw_smb, BOOL opt_write_behind, BOOL opt_prefer_write_raw, BOOL opt_disable_write_raw, BOOL opt_disable_read_raw, STRPTR opt_native_os, STRPTR device_name, STRPTR volume_name, STRPTR translation_file); STATIC VOID HandleFileSystem(STRPTR device_name, STRPTR volume_name, STRPTR service_name); /****************************************************************************/ @@ -567,6 +544,7 @@ main(VOID) SWITCH PreferWriteRaw; SWITCH DisableWriteRaw; SWITCH DisableReadRaw; + KEY NativeOS; SWITCH DumpSMB; NUMBER DumpSMBLevel; KEY DumpSMBFile; @@ -601,6 +579,7 @@ main(VOID) "PREFERWRITERAW/S," "DISABLEWRITERAW/S," "DISABLEREADRAW/S," + "NATIVEOS/K," "DUMPSMB/S," "DUMPSMBLEVEL/N/K," "DUMPSMBFILE/K," @@ -839,6 +818,10 @@ main(VOID) if(FindToolType(Icon->do_ToolTypes,"DISABLEREADRAW") != NULL) args.DisableReadRaw = TRUE; + str = FindToolType(Icon->do_ToolTypes,"NATIVEOS"); + if(str != NULL) + args.NativeOS = str; + str = FindToolType(Icon->do_ToolTypes,"TRANSLATE"); if(str == NULL) str = FindToolType(Icon->do_ToolTypes,"TRANSLATIONFILE"); @@ -1123,6 +1106,7 @@ main(VOID) args.PreferWriteRaw, args.DisableWriteRaw, args.DisableReadRaw, + args.NativeOS, args.DeviceName, args.VolumeName, args.TranslationFile)) @@ -1247,9 +1231,9 @@ posix_strerror(int error) SocketBaseTagList(tags); result = (STRPTR)tags[0].ti_Data; - - return(result); } + + return(result); } /****************************************************************************/ @@ -2201,7 +2185,7 @@ MapErrnoToIoErr(int error) */ STATIC const LONG map_posix_to_amigados[][2] = { - { EPERM, ERROR_OBJECT_NOT_FOUND }, /* Operation not permitted */ + { EPERM, ERROR_READ_PROTECTED }, /* Operation not permitted */ { ENOENT, ERROR_OBJECT_NOT_FOUND }, /* No such file or directory */ { ESRCH, ERROR_OBJECT_NOT_FOUND }, /* No such process */ { EINTR, ERROR_BREAK }, /* Interrupted system call */ @@ -2209,7 +2193,7 @@ MapErrnoToIoErr(int error) { E2BIG, ERROR_TOO_MANY_ARGS }, /* Argument list too long */ { EBADF, ERROR_INVALID_LOCK }, /* Bad file descriptor */ { ENOMEM, ERROR_NO_FREE_STORE }, /* Cannot allocate memory */ - { EACCES, ERROR_OBJECT_NOT_FOUND }, /* Permission denied */ + { EACCES, ERROR_READ_PROTECTED }, /* Permission denied */ { ENOTBLK, ERROR_OBJECT_WRONG_TYPE }, /* Block device required */ { EBUSY, ERROR_OBJECT_IN_USE }, /* Device busy */ { EEXIST, ERROR_OBJECT_EXISTS }, /* File exists */ @@ -2233,12 +2217,12 @@ MapErrnoToIoErr(int error) { EPFNOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Protocol family not supported */ { EAFNOSUPPORT, ERROR_NOT_IMPLEMENTED }, /* Address family not supported by protocol family */ { EADDRINUSE, ERROR_OBJECT_IN_USE }, /* Address already in use */ - { EADDRNOTAVAIL, ERROR_OBJECT_NOT_FOUND }, /* Can't assign requested address */ - { ENETDOWN, ERROR_OBJECT_NOT_FOUND }, /* Network is down */ - { ENETUNREACH, ERROR_OBJECT_NOT_FOUND }, /* Network is unreachable */ - { ENETRESET, ERROR_OBJECT_NOT_FOUND }, /* Network dropped connection on reset */ - { ECONNABORTED, ERROR_OBJECT_NOT_FOUND }, /* Software caused connection abort */ - { ECONNRESET, ERROR_OBJECT_NOT_FOUND }, /* Connection reset by peer */ + { EADDRNOTAVAIL, ERROR_DIR_NOT_FOUND }, /* Can't assign requested address */ + { ENETDOWN, ERROR_DIR_NOT_FOUND }, /* Network is down */ + { ENETUNREACH, ERROR_DIR_NOT_FOUND }, /* Network is unreachable */ + { ENETRESET, ERROR_DIR_NOT_FOUND }, /* Network dropped connection on reset */ + { ECONNABORTED, ERROR_DIR_NOT_FOUND }, /* Software caused connection abort */ + { ECONNRESET, ERROR_DIR_NOT_FOUND }, /* Connection reset by peer */ { ENOBUFS, ERROR_BUFFER_OVERFLOW }, /* No buffer space available */ { EISCONN, ERROR_OBJECT_IN_USE }, /* Socket is already connected */ { ENOTCONN, ERROR_OBJECT_WRONG_TYPE }, /* Socket is not connected */ @@ -2246,8 +2230,8 @@ MapErrnoToIoErr(int error) { ECONNREFUSED, ERROR_OBJECT_IN_USE }, /* Connection refused */ { ELOOP, ERROR_TOO_MANY_LEVELS }, /* Too many levels of symbolic links */ { ENAMETOOLONG, ERROR_LINE_TOO_LONG }, /* File name too long */ - { EHOSTDOWN, ERROR_OBJECT_NOT_FOUND }, /* Host is down */ - { EHOSTUNREACH, ERROR_OBJECT_NOT_FOUND }, /* No route to host */ + { EHOSTDOWN, ERROR_DIR_NOT_FOUND }, /* Host is down */ + { EHOSTUNREACH, ERROR_DIR_NOT_FOUND }, /* No route to host */ { ENOTEMPTY, ERROR_DIRECTORY_NOT_EMPTY }, /* Directory not empty */ { EPROCLIM, ERROR_TASK_TABLE_FULL }, /* Too many processes */ { EUSERS, ERROR_TASK_TABLE_FULL }, /* Too many users */ @@ -2606,6 +2590,7 @@ Setup( BOOL opt_prefer_write_raw, BOOL opt_disable_write_raw, BOOL opt_disable_read_raw, + STRPTR opt_native_os, STRPTR device_name, STRPTR volume_name, STRPTR translation_file) @@ -2785,6 +2770,7 @@ Setup( opt_prefer_write_raw, opt_disable_write_raw, opt_disable_read_raw, + (char *)opt_native_os, &error, &smb_error_class, &smb_error, @@ -3258,7 +3244,7 @@ Action_Parent( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -3453,7 +3439,7 @@ Action_DeleteObject( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&file,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_writable,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -3658,7 +3644,7 @@ Action_CreateDir( ln->ln_FileLock.fl_Volume = MKBADDR(VolumeNode); ln->ln_FullName = full_name; - if(smba_open(ServerData,dir_name,dir_name_size,&dir,&error) < 0) + if(smba_open(ServerData,dir_name,dir_name_size,open_read_only,open_dont_truncate,&dir,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -3675,7 +3661,7 @@ Action_CreateDir( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -3805,7 +3791,7 @@ Action_LocateObject( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -3898,7 +3884,7 @@ Action_CopyDir( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -4091,7 +4077,7 @@ Action_SetProtect( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&file,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -4104,7 +4090,6 @@ Action_SetProtect( st.atime = -1; st.ctime = -1; st.mtime = -1; - st.size = -1; if((mask & (FIBF_WRITE|FIBF_DELETE)) != (FIBF_WRITE|FIBF_DELETE)) { @@ -4124,7 +4109,7 @@ Action_SetProtect( /* The 'system' attribute is associated with the 'pure' bit for now. */ st.is_system = ((mask & FIBF_PURE) != 0); - if(smba_setattr(file,&st,&error) < 0) + if(smba_setattr(file,&st,NULL,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -5368,7 +5353,7 @@ Action_Find( int ignored_error; smba_stat_t st; - if(smba_open(ServerData,full_name,full_name_size,&file,&ignored_error) == OK && + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&ignored_error) == OK && smba_getattr(file,&st,&ignored_error) == OK) { /* File apparently opens Ok and information on it @@ -5441,7 +5426,7 @@ Action_Find( SHOWMSG("creating a file; finding parent path first"); SHOWSTRING(parent_path); - if(smba_open(ServerData,parent_path,strlen(full_name)+3,&dir,&error) < 0) + if(smba_open(ServerData,parent_path,strlen(full_name)+3,open_read_only,open_dont_truncate,&dir,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -5472,7 +5457,7 @@ Action_Find( } /* Now for the remainder... */ - if(smba_open(ServerData,full_name,full_name_size,&fn->fn_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,action != ACTION_FINDINPUT,create_new_file,&fn->fn_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -5513,6 +5498,8 @@ Action_Read( ENTER(); + SHOWVALUE(length); + if(length > 0) { result = smba_read(fn->fn_File,mem,length,fn->fn_Offset,&error); @@ -5555,6 +5542,8 @@ Action_Write( goto out; } + SHOWVALUE(length); + if(length > 0) { result = smba_write(fn->fn_File,mem,length,fn->fn_Offset,&error); @@ -5607,143 +5596,46 @@ Action_Seek( { LONG previous_position = fn->fn_Offset; LONG result = -1; + smba_stat_t st; LONG offset; int error; ENTER(); - /* olsen: This doesn't really work with Microsoft SMB servers, but it works with Samba. */ - #if 0 + switch(mode) { - switch(mode) - { - case OFFSET_BEGINNING: + case OFFSET_BEGINNING: - mode = 0; - break; + offset = position; + break; - case OFFSET_CURRENT: + case OFFSET_CURRENT: - mode = 1; - break; + offset = fn->fn_Offset + position; + break; - case OFFSET_END: + case OFFSET_END: - mode = 2; - break; - - default: - - error = ERROR_ACTION_NOT_KNOWN; + if(smba_getattr(fn->fn_File,&st,&error) < 0) + { + error = MapErrnoToIoErr(error); goto out; - } + } - error = smba_seek (fn->fn_File, position, mode, (off_t *) &offset); - if(error < 0) - { - error = MapErrnoToIoErr(error); + offset = st.size + position; + break; + + default: + + error = ERROR_ACTION_NOT_KNOWN; goto out; - } } - #endif - /* olsen: This is the original implementation. */ - #if 0 + if(offset < 0) { - smba_stat_t st; - - error = smba_getattr(fn->fn_File,&st); - if(error < 0) - { - error = MapErrnoToIoErr(error); - goto out; - } - - offset = fn->fn_Offset; - - switch(mode) - { - case OFFSET_BEGINNING: - - offset = position; - break; - - case OFFSET_CURRENT: - - offset += position; - break; - - case OFFSET_END: - - offset = st.size + position; - break; - - default: - - error = ERROR_ACTION_NOT_KNOWN; - goto out; - } - - if(offset < 0 || offset > st.size) - { - error = ERROR_SEEK_ERROR; - goto out; - } + error = ERROR_SEEK_ERROR; + goto out; } - #endif - - /* olsen: This is a mix of the two above. First we calculate the absolute - * position, then seek to that position. The SMB server is supposed - * to do its housekeeping before the position is changed. I wish this - * worked differently, but it seems we've got the best of both worlds - * here... - */ - #if 1 - { - smba_stat_t st; - - switch(mode) - { - case OFFSET_BEGINNING: - - offset = position; - break; - - case OFFSET_CURRENT: - - offset = fn->fn_Offset + position; - break; - - case OFFSET_END: - - if(smba_getattr(fn->fn_File,&st,&error) < 0) - { - error = MapErrnoToIoErr(error); - goto out; - } - - offset = st.size + position; - break; - - default: - - error = ERROR_ACTION_NOT_KNOWN; - goto out; - } - - if(offset < 0) - { - error = ERROR_SEEK_ERROR; - goto out; - } - - if(smba_seek (fn->fn_File, offset, 0, (off_t *) &offset, &error) < 0) - { - error = MapErrnoToIoErr(error); - goto out; - } - } - #endif error = OK; @@ -5768,10 +5660,11 @@ Action_SetFileSize( LONG mode, LONG * error_ptr) { - smba_stat_t st; LONG result = -1; + smba_stat_t st; int error; long offset; + dword size; ENTER(); @@ -5818,12 +5711,9 @@ Action_SetFileSize( goto out; } - st.atime = -1; - st.ctime = -1; - st.mtime = -1; - st.size = offset; + size = (dword)offset; - if(smba_setattr(fn->fn_File,&st,&error) < 0) + if(smba_setattr(fn->fn_File,NULL,&size,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -5923,7 +5813,7 @@ Action_SetDate( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&file,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -5937,12 +5827,13 @@ Action_SetDate( seconds = (ds->ds_Days * 24 * 60 + ds->ds_Minute) * 60 + (ds->ds_Tick / TICKS_PER_SECOND); + memset(&st,0,sizeof(st)); + st.atime = -1; st.ctime = -1; st.mtime = seconds + UNIX_TIME_OFFSET + GetTimeZoneDelta(); - st.size = -1; - if(smba_setattr(file,&st,&error) < 0) + if(smba_setattr(file,&st,NULL,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -6144,7 +6035,7 @@ Action_ParentFH( SHOWSTRING(full_name); - if(smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if(smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -6219,7 +6110,7 @@ Action_CopyDirFH( SHOWSTRING(full_name); - if (smba_open(ServerData,full_name,full_name_size,&ln->ln_File,&error) < 0) + if (smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&ln->ln_File,&error) < 0) { error = MapErrnoToIoErr(error); goto out; @@ -6636,7 +6527,7 @@ Action_SetComment( SHOWSTRING(full_name); - if (smba_open(ServerData,full_name,full_name_size,&file,&error) < 0) + if (smba_open(ServerData,full_name,full_name_size,open_read_only,open_dont_truncate,&file,&error) < 0) { error = MapErrnoToIoErr(error); goto out; diff --git a/source_code/proc.c b/source_code/proc.c index 6f3119f..93108f6 100644 --- a/source_code/proc.c +++ b/source_code/proc.c @@ -23,6 +23,43 @@ /*****************************************************************************/ +#include "smbfs_rev.h" + +/*****************************************************************************/ + +/* smbfs uses the following commands: + * + * 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): * @@ -106,12 +143,14 @@ static const err_code_struct dos_msgs[] = {"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"}, @@ -173,7 +212,7 @@ static const err_code_struct hard_msgs[] = {"ERRbadunit", 20, "Unknown unit"}, {"ERRnotready", 21, "Drive not ready"}, {"ERRbadcmd", 22, "Unknown command"}, - {"ERRdata", 23, "Data error (CRC)"}, + {"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"}, @@ -216,11 +255,23 @@ static const err_class_struct err_classes[] = /*****************************************************************************/ +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); + +/*****************************************************************************/ + /***************************************************************************** * * 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) { @@ -229,6 +280,23 @@ smb_encode_word (byte * p, word data) 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) { @@ -714,7 +782,7 @@ smb_request_ok_with_payload ( server->err = 0; /* Send the message and wait for the response to arrive. */ - result = smb_request (server, input_payload, output_payload, payload_size, error_ptr); + result = smb_request (server, command, input_payload, output_payload, payload_size, error_ptr); /* smb_request() failed? */ if (result < 0) @@ -770,8 +838,9 @@ smb_request_ok (struct smb_server *s, int command, int wct, int bcc, int * error 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 * error_ptr) +smb_retry (struct smb_server *server) { + int ignored_error; int result = 0; if (server->state == CONN_VALID) @@ -779,7 +848,11 @@ smb_retry (struct smb_server *server, int * error_ptr) smb_release (server); - if (smb_proc_reconnect (server, error_ptr) < 0) + /* 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; @@ -796,6 +869,9 @@ smb_retry (struct smb_server *server, int * error_ptr) /* 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) @@ -881,19 +957,136 @@ smb_payload_size(const struct smb_server *server, int wct, int bcc) ****************************************************************************/ int -smb_proc_open (struct smb_server *server, const char *pathname, int len, struct smb_dirent *entry,int * error_ptr) +smb_proc_open (struct smb_server *server, const char *pathname, int len, int writable, int truncate, struct smb_dirent *entry,int * error_ptr) { int result; char *p; char *buf = server->transmit_buffer; const word o_attr = aSYSTEM | aHIDDEN | aDIR; - LOG (("path=%s\n", pathname)); + ENTER(); - retry: + SHOWSTRING(pathname); + + if (server->protocol >= PROTOCOL_NT1) + { + int desired_access; + int share_access; + int create_disposition; + dword ext_file_attributes; + dword end_of_file_low; + dword end_of_file_high; + char *params; + char *data; + + SHOWMSG("we'll try SMB_COM_NT_CREATE_ANDX"); + + if((*pathname) == '\\') + { + pathname++; + len--; + } + + ASSERT( smb_payload_size(server, 24, len+1) >= 0 ); + + if(writable) + { + SHOWMSG("write access required"); + // desired_access = FILE_READ_DATA|FILE_WRITE_DATA|FILE_DELETE; + desired_access = GENERIC_READ|GENERIC_WRITE; + } + else + { + SHOWMSG("read access is sufficient"); + // desired_access = FILE_READ_DATA; + desired_access = GENERIC_READ; + } + + // desired_access |= FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES; + + share_access = FILE_SHARE_READ|FILE_SHARE_WRITE; + + if(writable && truncate) + create_disposition = FILE_OVERWRITE_IF; + else + create_disposition = FILE_OPEN; + + while(TRUE) + { + data = smb_setup_header (server, SMBntcreateX, 24, len+1); + + 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, len+1); /* 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, o_attr); /* ExtFileAttributes */ + params = smb_encode_dword(params, share_access); /* ShareAccess */ + params = smb_encode_dword(params, create_disposition); /* CreateDisposition */ + params = smb_encode_dword(params, 0); /* CreateOptions */ + params = smb_encode_dword(params, SEC_ANONYMOUS); /* ImpersonationLevel */ + (void) smb_encode_byte(params, 0); /* SecurityFlags */ + + /* Now for the data portion of the message */ + (void) smb_copy_data(data, pathname, len+1); + + LOG(("requesting SMBntcreateX\n")); + + result = smb_request_ok(server, SMBntcreateX, 34, 0, error_ptr); + if (result < 0) + { + SHOWMSG("that didn't work; retrying"); + + 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 */ + params += 2 * sizeof(dword); /* CreateTime */ + params += 2 * sizeof(dword); /* LastAccessTime */ + params += 2 * sizeof(dword); /* LastWriteTime */ + + entry->ctime = entry->atime = entry->mtime = entry->wtime = local2utc (convert_long_date_to_time_t(params)); + params += 2 * sizeof(dword); /* LastChangeTime */ + + params = smb_decode_dword(params, &ext_file_attributes); + entry->attr = ext_file_attributes; + + params += 2 * sizeof(dword); /* AllocationSize */ + + params = smb_decode_dword(params, &end_of_file_low); + (void) smb_decode_dword(params, &end_of_file_high); + + entry->size = end_of_file_low; + + entry->opened = 1; + + goto out; + } ASSERT( smb_payload_size(server, 2, 2 + len) >= 0 ); + SHOWMSG("using the old SMB_COM_OPEN"); + + retry: + p = smb_setup_header (server, SMBopen, 2, 2 + len); WSET (buf, smb_vwv0, 0x42); /* read/write */ WSET (buf, smb_vwv1, o_attr); @@ -902,15 +1095,19 @@ smb_proc_open (struct smb_server *server, const char *pathname, int len, struct result = smb_request_ok (server, SMBopen, 7, 0, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; - /* ZZZ Why is the file opened in read-only mode here? */ - if ((*error_ptr) == EACCES || ((*error_ptr) == error_check_smb_error && smb_errno(server->rcls,server->err) == EACCES)) { + /* Don't retry in read-only mode. */ + if(writable) + goto out; + ASSERT( smb_payload_size(server, 2, 2 + len) >= 0 ); + retry_read_only: + p = smb_setup_header (server, SMBopen, 2, 2 + len); WSET (buf, smb_vwv0, 0x40); /* read only */ WSET (buf, smb_vwv1, o_attr); @@ -919,8 +1116,8 @@ smb_proc_open (struct smb_server *server, const char *pathname, int len, struct result = smb_request_ok (server, SMBopen, 7, 0, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) - goto retry; + if (smb_retry (server)) + goto retry_read_only; goto out; } @@ -938,27 +1135,10 @@ smb_proc_open (struct smb_server *server, const char *pathname, int len, struct entry->size = DVAL (buf, smb_vwv4); entry->opened = 1; - #if DEBUG - { - struct tm tm; - - GMTime(entry->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->mtime,&tm); - LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - } - #endif /* DEBUG */ - out: - return result; + RETURN(result); + return(result); } /* smb_proc_close: in finfo->mtime we can send a modification time to @@ -1000,6 +1180,8 @@ smb_proc_read (struct smb_server *server, struct smb_dirent *finfo, off_t offset ASSERT( count <= 65535 ); + retry: + smb_setup_header (server, SMBread, 5, 0); WSET (buf, smb_vwv0, finfo->fileid); @@ -1011,7 +1193,12 @@ smb_proc_read (struct smb_server *server, struct smb_dirent *finfo, off_t offset result = smb_request_ok_with_payload (server, SMBread, 5, -1, data, NULL, count, error_ptr); if (result < 0) - goto out; + { + if (smb_retry (server)) + 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)); @@ -1038,6 +1225,8 @@ smb_proc_read_raw (struct smb_server *server, struct smb_dirent *finfo, off_t of ASSERT( count <= 65535 ); + retry: + smb_setup_header (server, SMBreadbraw, 8, 0); WSET (buf, smb_vwv0, finfo->fileid); @@ -1048,6 +1237,11 @@ smb_proc_read_raw (struct smb_server *server, struct smb_dirent *finfo, off_t of WSET (buf, smb_vwv7, 0); /* reserved */ result = smb_request_read_raw (server, data, count, error_ptr); + if (result < 0) + { + if (smb_retry (server)) + goto retry; + } return result; } @@ -1061,6 +1255,8 @@ smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offse ASSERT( count <= 65535 ); + retry: + p = smb_setup_header (server, SMBwrite, 5, count + 3); WSET (buf, smb_vwv0, finfo->fileid); WSET (buf, smb_vwv1, count); @@ -1071,8 +1267,15 @@ smb_proc_write (struct smb_server *server, struct smb_dirent *finfo, off_t offse 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 (result < 0) + { + if (smb_retry (server)) + goto retry; + } + else + { result = WVAL (buf, smb_vwv0); + } return result; } @@ -1117,7 +1320,7 @@ smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t o if(max_len > 65535) max_len = 65535; - max_len -= 59 - NETBIOS_HEADER_SIZE; + max_len -= 59; LOG(("maximum length for payload = %ld bytes\n", max_len)); @@ -1138,6 +1341,8 @@ smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t o LOG(("count (%ld) > max_len (%ld) -- send %ld bytes with the message.\n",count,max_len,len)); } + retry: + p = smb_setup_header (server, SMBwritebraw, server->protocol > PROTOCOL_COREPLUS ? 12 : 11, len); WSET (buf, smb_vwv0, finfo->fileid); @@ -1166,7 +1371,12 @@ smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t o result = smb_request_ok_with_payload (server, SMBwritebraw, 1, 0, NULL, data, len, error_ptr); if (result < 0) - goto out; + { + if (smb_retry (server)) + goto retry; + else + goto out; + } num_bytes_written += len; @@ -1183,7 +1393,18 @@ smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t o result = smb_request_write_raw (server, data, count, error_ptr); if (result < 0) - goto out; + { + /* 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) { @@ -1230,30 +1451,117 @@ smb_proc_write_raw (struct smb_server *server, struct smb_dirent *finfo, off_t o } int -smb_proc_lseek (struct smb_server *server, struct smb_dirent *finfo, off_t offset, int mode, off_t * new_position_ptr, int * error_ptr) +smb_proc_writex (struct smb_server *server, struct smb_dirent *finfo, off_t offset, 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: - ASSERT( smb_payload_size(server, 4, 0) >= 0 ); + p = smb_setup_header (server, SMBwriteX, server->protocol >= PROTOCOL_NT1 ? 14 : 12, 2+1+count); - smb_setup_header (server, SMBlseek, 4, 0); + 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); /* 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, 0); /* high offset */ + } + else + { + WSET (buf, smb_vwv11, 60+2); /* data offset */ + } - WSET (buf, smb_vwv0, finfo->fileid); - WSET (buf, smb_vwv1, mode); - DSET (buf, smb_vwv2, offset); + /* Now for the data portion of the message */ - result = smb_request_ok (server, SMBlseek, 1, 0, error_ptr); + 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\n")); + + result = smb_request_ok_with_payload (server, SMBwriteX, 6, 0, NULL, data, count, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } else { - (*new_position_ptr) = DVAL(buf, smb_vwv0); + 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, off_t offset, 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); /* 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, 0); /* OffsetHigh */ + + /* Now for the data portion of the message */ + + WSET (p, 0, 0); /* Byte count */ + + LOG(("requesting SMBreadX: offset=%ld, count=%ld\n", offset, count)); + + result = smb_request_ok_with_payload (server, SMBreadX, 7, 0, data, NULL, count, error_ptr); + if (result < 0) + { + if (smb_retry (server)) + 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; @@ -1277,10 +1585,10 @@ smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct s else num_locks = num_entries; - retry: - 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); @@ -1301,7 +1609,7 @@ smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct s result = smb_request_ok (server, SMBlockingX, 0, 0, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } @@ -1309,8 +1617,8 @@ smb_proc_lockingX (struct smb_server *server, struct smb_dirent *finfo, struct s } /* smb_proc_do_create: We expect entry->attry & entry->ctime to be set. */ -static int -smb_proc_do_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, word command, int * error_ptr) +int +smb_proc_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr) { int result; char *p; @@ -1321,19 +1629,19 @@ smb_proc_do_create (struct smb_server *server, const char *path, int len, struct ASSERT( smb_payload_size(server, 3, len + 2) >= 0 ); - p = smb_setup_header (server, command, 3, len + 2); + p = smb_setup_header (server, SMBcreate, 3, len + 2); WSET (buf, smb_vwv0, entry->attr); local_time = utc2local (entry->ctime); DSET (buf, smb_vwv1, local_time); smb_encode_ascii (p, path, len); - result = smb_request_ok (server, command, 1, 0, error_ptr); + result = smb_request_ok (server, SMBcreate, 1, 0, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; - - goto out; + else + goto out; } entry->opened = 1; @@ -1346,12 +1654,6 @@ smb_proc_do_create (struct smb_server *server, const char *path, int len, struct return result; } -int -smb_proc_create (struct smb_server *server, const char *path, int len, struct smb_dirent *entry, int * error_ptr) -{ - return smb_proc_do_create (server, path, len, entry, SMBcreate, error_ptr); -} - int smb_proc_mv (struct smb_server *server, const char *opath, const int olen, const char *npath, const int nlen, int * error_ptr) { @@ -1359,10 +1661,10 @@ smb_proc_mv (struct smb_server *server, const char *opath, const int olen, const char *buf = server->transmit_buffer; int result; - retry: - ASSERT( smb_payload_size(server, 1, olen + nlen + 4) >= 0 ); + retry: + p = smb_setup_header (server, SMBmv, 1, olen + nlen + 4); WSET (buf, smb_vwv0, 0); @@ -1373,7 +1675,7 @@ smb_proc_mv (struct smb_server *server, const char *opath, const int olen, const result = smb_request_ok (server, SMBmv, 0, 0, error_ptr); if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } @@ -1386,19 +1688,81 @@ smb_proc_mkdir (struct smb_server *server, const char *path, const int len, int int result; char *p; - retry: - ASSERT( smb_payload_size(server, 0, 2 + len) >= 0 ); - p = smb_setup_header (server, SMBmkdir, 0, 2 + len); + retry: - smb_encode_ascii (p, path, len); - - if ((result = smb_request_ok (server, SMBmkdir, 0, 0, error_ptr)) < 0) + if (server->protocol >= PROTOCOL_LANMAN2) { - if (smb_retry (server, error_ptr)) - goto retry; + unsigned char *outbuf = server->transmit_buffer; + + ASSERT( smb_payload_size(server, 15, 3 + 4 + len+1) >= 0 ); + + smb_setup_header (server, SMBtrans2, 15, 3 + 4 + len+1); + + WSET (outbuf, smb_tpscnt, 4 + len+1); /* 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_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; + + 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 + { + p = smb_setup_header (server, SMBmkdir, 0, 2 + len); + + 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; } @@ -1409,17 +1773,18 @@ smb_proc_rmdir (struct smb_server *server, const char *path, const int len, int int result; char *p; - retry: - ASSERT( smb_payload_size(server, 0, 2 + len) >= 0 ); + retry: + p = smb_setup_header (server, SMBrmdir, 0, 2 + len); smb_encode_ascii (p, path, len); - if ((result = smb_request_ok (server, SMBrmdir, 0, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBrmdir, 0, 0, error_ptr); + if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } @@ -1433,19 +1798,20 @@ smb_proc_unlink (struct smb_server *server, const char *path, const int len, int char *buf = server->transmit_buffer; int result; - retry: - ASSERT( smb_payload_size(server, 1, 2 + len) >= 0 ); + retry: + p = smb_setup_header (server, SMBunlink, 1, 2 + len); WSET (buf, smb_vwv0, 0); smb_encode_ascii (p, path, len); - if ((result = smb_request_ok (server, SMBunlink, 0, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBunlink, 0, 0, error_ptr); + if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } @@ -1461,6 +1827,8 @@ smb_proc_trunc (struct smb_server *server, word fid, dword length, int * error_p 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); @@ -1469,8 +1837,15 @@ smb_proc_trunc (struct smb_server *server, word fid, dword length, int * error_p smb_encode_ascii (p, "", 0); result = smb_request_ok (server, SMBwrite, 1, 0, error_ptr); - if (result >= 0) + if(result < 0) + { + if (smb_retry (server)) + goto retry; + } + else + { result = DVAL(buf, smb_vwv0); + } return result; } @@ -1501,17 +1876,8 @@ smb_decode_dirent (char *p, struct smb_dirent *entry) { struct tm tm; - GMTime(entry->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - GMTime(entry->mtime,&tm); LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); } #endif /* DEBUG */ @@ -1592,7 +1958,7 @@ smb_proc_readdir_short (struct smb_server *server, char *path, int fpos, int cac } else { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; result = -1; @@ -1673,7 +2039,7 @@ smb_proc_readdir_short (struct smb_server *server, char *path, int fpos, int cac * calculation using integer operations only. */ static time_t -interpret_long_date(char * p) +convert_long_date_to_time_t(const char * p) { QUAD adjust; QUAD long_date; @@ -1689,10 +2055,11 @@ interpret_long_date(char * p) 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). */ + * 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; + adjust.High = 0x00000002; underflow = subtract_64_from_64_to_64(&long_date,&adjust,&long_date); @@ -1709,39 +2076,49 @@ interpret_long_date(char * p) /*****************************************************************************/ +/* 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 1: /* OS/2 understands this */ + case SMB_INFO_STANDARD: - (*name_ptr) = p + 27; - (*len_ptr) = strlen(p + 27); + (*len_ptr) = p[26]; + (*name_ptr) = &p[27]; break; - case 2: /* this is what OS/2 uses */ + case SMB_FILE_BOTH_DIRECTORY_INFO: - (*name_ptr) = p + 31; - (*len_ptr) = strlen(p + 31); - break; - - case 3: /* untested */ - - (*name_ptr) = p + 33; - (*len_ptr) = strlen(p + 33); - break; - - case 4: /* untested */ - - (*name_ptr) = p + 37; - (*len_ptr) = strlen(p + 37); - break; - - case 260: /* NT uses this, but also accepts 2 */ - - (*name_ptr) = p + 94; - (*len_ptr) = min (DVAL (p+60, 0), SMB_MAXNAMELEN); + (*len_ptr) = DVAL (p, 60); + (*name_ptr) = &p[94]; break; default: @@ -1752,212 +2129,178 @@ smb_get_dirent_name(char *p,int level,char ** name_ptr,int * len_ptr) } } -/* interpret a long filename structure - this is mostly guesses at the - moment. The length of the structure is returned. The structure of - a long filename depends on the info level. 260 is used by NT and 2 - is used by OS/2. */ -static char * -smb_decode_long_dirent (char *p, struct smb_dirent *finfo, int level) +/* interpret a long filename structure */ +static int +smb_decode_long_dirent (char *p, struct smb_dirent *finfo, int level, int * entry_length_ptr) { - char *result; + int success = TRUE; + + ENTER(); + + ASSERT( entry_length_ptr != NULL ); switch (level) { - case 1: /* OS/2 understands this */ + case SMB_INFO_STANDARD: - #if DEBUG - { - char buffer[255]; + SHOWMSG("SMB_INFO_STANDARD"); - memcpy(buffer,p + 27,sizeof(buffer)-1); - buffer[sizeof(buffer)-1] = '\0'; - - LOG(("type=%ld, name='%s'\n",level,buffer)); - } - #endif /* DEBUG */ + (*entry_length_ptr) = 28 + BVAL (p, 26); if (finfo != NULL) { - strlcpy (finfo->complete_path, p + 27, finfo->complete_path_size); + int name_len; + + name_len = BVAL (p, 26); + + 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; + } - finfo->len = strlen (finfo->complete_path); - finfo->size = DVAL (p, 16); - finfo->attr = BVAL (p, 24); finfo->ctime = date_dos2unix (WVAL (p, 6), WVAL (p, 4)); finfo->atime = date_dos2unix (WVAL (p, 10), WVAL (p, 8)); finfo->mtime = date_dos2unix (WVAL (p, 14), WVAL (p, 12)); finfo->wtime = finfo->mtime; - - #if DEBUG - { - struct tm tm; - - GMTime(finfo->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->mtime,&tm); - LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - } - #endif /* DEBUG */ - } - - result = p + 28 + BVAL (p, 26); - - break; - - case 2: /* this is what OS/2 uses */ - - #if DEBUG - { - char buffer[255]; - - memcpy(buffer,p + 31,sizeof(buffer)-1); - buffer[sizeof(buffer)-1] = '\0'; - - LOG(("type=%ld, name='%s'\n",level,buffer)); - } - #endif /* DEBUG */ - - if (finfo != NULL) - { - strlcpy (finfo->complete_path, p + 31, finfo->complete_path_size); - - finfo->len = strlen (finfo->complete_path); finfo->size = DVAL (p, 16); - finfo->attr = BVAL (p, 24); - finfo->ctime = date_dos2unix (WVAL (p, 6), WVAL (p, 4)); - finfo->atime = date_dos2unix (WVAL (p, 10), WVAL (p, 8)); - finfo->mtime = date_dos2unix (WVAL (p, 14), WVAL (p, 12)); - finfo->wtime = finfo->mtime; + finfo->attr = WVAL (p, 24); + finfo->len = name_len; - #if DEBUG + /* Don't return names with NUL bytes. */ + if(memchr(&p[27], '\0', name_len) != NULL) { - struct tm tm; + SHOWMSG("name contains NUL bytes (skipping it)"); - GMTime(finfo->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->mtime,&tm); - LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); + success = FALSE; + break; } - #endif /* DEBUG */ - } - result = p + 32 + BVAL (p, 30); + memcpy(finfo->complete_path, &p[27], name_len); + finfo->complete_path[name_len] = '\0'; + } break; - case 260: /* NT uses this, but also accepts 2 */ + case SMB_FILE_BOTH_DIRECTORY_INFO: - #if DEBUG - { - char buffer[255]; - int len; + SHOWMSG("SMB_FILE_BOTH_DIRECTORY_INFO"); - len = min (DVAL (p+60, 0), sizeof(buffer)-1); - - memcpy(buffer,p+94,len); - buffer[len] = '\0'; - - LOG(("type=%ld, name='%s'\n",level,buffer)); - } - #endif /* DEBUG */ - - result = p + WVAL (p, 0); + (*entry_length_ptr) = WVAL (p, 0); if (finfo != NULL) { - int namelen; + int name_len; time_t swap; p += 4; /* next entry offset */ + p += 4; /* fileindex */ - finfo->ctime = interpret_long_date(p); + + finfo->ctime = convert_long_date_to_time_t(p); p += 8; - finfo->atime = interpret_long_date(p); + + finfo->atime = convert_long_date_to_time_t(p); p += 8; - finfo->wtime = interpret_long_date(p); + + finfo->wtime = convert_long_date_to_time_t(p); p += 8; - finfo->mtime = interpret_long_date(p); + + finfo->mtime = convert_long_date_to_time_t(p); p += 8; - finfo->size = DVAL (p, 0); - p += 8; - p += 8; /* alloc size */ - finfo->attr = BVAL (p, 0); - p += 4; - namelen = min (DVAL (p, 0), SMB_MAXNAMELEN); - p += 4; - p += 4; /* EA size */ - p += 2; /* short name len? */ - p += 24; /* short name? */ - - if(namelen > (int)finfo->complete_path_size-1) - namelen = finfo->complete_path_size-1; - + /* If the modification time is not set, try to - substitute the write time for it. */ + * substitute the write time for it. + */ if(finfo->mtime == 0) finfo->mtime = finfo->wtime; /* Swap last modification time and last write time. */ - swap = finfo->mtime; - finfo->mtime = finfo->wtime; - finfo->wtime = swap; + swap = finfo->mtime; + finfo->mtime = finfo->wtime; + finfo->wtime = swap; - memcpy (finfo->complete_path, p, namelen); - finfo->complete_path[namelen] = '\0'; - finfo->len = namelen; + /* Note: size is a 64 bit integer, we only use the least significant 32 bits. */ + finfo->size = DVAL (p, 0); + p += 8; + + p += 8; /* alloc size */ + + finfo->attr = DVAL (p, 0); + p += 4; + + name_len = DVAL (p, 0); + p += 4; - #if DEBUG + /* Skip directory entries whose names we cannot store. */ + if(name_len == 0) { - struct tm tm; - - GMTime(finfo->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(finfo->mtime,&tm); - LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); + success = FALSE; + break; } - #endif /* DEBUG */ + + SHOWVALUE(name_len); + + p += 4; /* EA size */ + + p += 1; /* short name length */ + p += 1; /* reserved */ + + p += 12*2; /* short name (12 WCHAR characters) */ + + /* Skip directory entries whose names we cannot store. */ + if(name_len == 0) + { + success = FALSE; + break; + } + + if(p[name_len-1] == '\0') + 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; + } + + /* Don't return names with NUL bytes. */ + if(memchr(p, '\0', name_len) != NULL) + { + SHOWMSG("name contains NUL bytes (skipping it)"); + + success = FALSE; + break; + } + + memcpy (finfo->complete_path, p, name_len); + finfo->complete_path[name_len] = '\0'; + finfo->len = name_len; } break; + /* This should never happen. */ default: - if (finfo != NULL) - { - /* I have to set times to 0 here, because I do not - have specs about this for all info levels. */ - finfo->ctime = finfo->mtime = finfo->wtime = finfo->atime = 0; - } - - LOG (("Unknown long filename format %ld\n", level)); - - result = p + WVAL (p, 0); - + (*entry_length_ptr) = 0; + + success = FALSE; break; } - return result; + D(("entry_length = %ld",(*entry_length_ptr))); + + RETURN(success); + return(success); } static int @@ -1965,12 +2308,11 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach { int max_matches = 512; /* this should actually be based on the max_recv value */ - /* NT uses 260, OS/2 uses 2. Both accept 1. */ - int info_level = server->protocol < PROTOCOL_NT1 ? 1 : 260; + int info_level = server->protocol < PROTOCOL_NT1 ? SMB_INFO_STANDARD : SMB_FILE_BOTH_DIRECTORY_INFO; char *p; int i; - int first; + int is_first; int total_count = 0; struct smb_dirent *current_entry; @@ -1993,12 +2335,14 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach int dirlen = strlen (path) + 2 + 1; char *mask = NULL; int masklen; - + + int entry_length; + ENTER(); /* ZZZ experimental 'max_matches' adjustment */ /* - if(info_level == 260) + if(info_level == SMB_FILE_BOTH_DIRECTORY_INFO) max_matches = server->max_recv / 360; else max_matches = server->max_recv / 40; @@ -2030,7 +2374,7 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach retry: - first = 1; + is_first = TRUE; total_count = 0; current_entry = entry; @@ -2047,37 +2391,35 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach break; } - memset (outbuf, 0, 39); + memset (outbuf, 0, sizeof(word) * smb_setup1); - ASSERT( smb_payload_size(server, 15, 5 + 12 + masklen + 1) >= 0 ); + ASSERT( smb_payload_size(server, 15, 3 + 12 + masklen + 1) >= 0 ); - smb_setup_header (server, SMBtrans2, 15, 5 + 12 + masklen + 1); + smb_setup_header (server, SMBtrans2, 15, 3 + 12 + masklen + 1); - WSET (outbuf, smb_tpscnt, 12 + masklen + 1); - WSET (outbuf, smb_tdscnt, 0); - WSET (outbuf, smb_mprcnt, 10); - WSET (outbuf, smb_mdrcnt, server->max_recv); - WSET (outbuf, smb_msrcnt, 0); - WSET (outbuf, smb_flags, 0); - DSET (outbuf, smb_timeout, 0); - WSET (outbuf, smb_pscnt, WVAL (outbuf, smb_tpscnt)); - WSET (outbuf, smb_psoff, ((SMB_BUF (outbuf) + 3) - outbuf) - 4); - WSET (outbuf, smb_dscnt, 0); - WSET (outbuf, smb_dsoff, 0); - WSET (outbuf, smb_suwcnt, 1); - WSET (outbuf, smb_setup0, first == 1 ? TRANSACT2_FINDFIRST : TRANSACT2_FINDNEXT); + WSET (outbuf, smb_tpscnt, 12 + masklen + 1); /* 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 */ + (*p++) = '\0'; /* put in a null smb_name */ - /* ZZZ the following may be unnecessary, because they - * likely represent random data used for alignment - * padding purposes. - */ - (*p++) = 'D'; - (*p++) = ' '; /* this was added because OS/2 does it */ + /* Align the parameter data to a 4-byte boundary. */ + (*p++) = '\0'; + (*p++) = '\0'; - if (first != 0) + if (is_first) { LOG (("first match\n")); WSET (p, 0, attribute); /* attribute */ @@ -2102,19 +2444,18 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach memcpy (p, mask, masklen); p += masklen; - (*p++) = 0; - (*p) = 0; + (*p) = '\0'; - result = smb_trans2_request (server, &resp_data_len, &resp_param_len, &resp_data, &resp_param, error_ptr); + 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 first if it's a protocol + /* 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, error_ptr)) + if (smb_retry (server)) goto retry; LOG (("got error from trans2_request\n")); @@ -2147,15 +2488,16 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach break; } - /* ZZZ bail out if this is empty. */ + /* Bail out if this is empty. */ if (resp_param == NULL) break; /* parse out some important return info */ p = resp_param; - if (first != 0) + if (is_first) { ff_dir_handle = WVAL (p, 0); + ff_searchcount = WVAL (p, 2); ff_eos = WVAL (p, 4); } @@ -2169,7 +2511,7 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach if (ff_searchcount == 0) break; - /* ZZZ bail out if this is empty. */ + /* Bail out if this is empty. */ if (resp_data == NULL) break; @@ -2177,7 +2519,7 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach p = resp_data; /* Now we are ready to parse smb directory entries. */ - for (i = 0; i < ff_searchcount; i++) + for (i = 0 ; i < ff_searchcount; i++, p += entry_length) { if(i == ff_searchcount - 1) { @@ -2190,17 +2532,6 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach if(len > 0) { - #if DEBUG - { - char buffer[SMB_MAXNAMELEN+1]; - - memcpy(buffer,last_name,len); - buffer[len] = '\0'; - - LOG(("last name = '%s'\n",buffer)); - } - #endif /* DEBUG */ - if(len + 1 > dirlen) { /* Grow the buffer in steps of 16 bytes, @@ -2239,13 +2570,17 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach if (total_count < fpos) { - p = smb_decode_long_dirent (p, NULL, info_level); + smb_decode_long_dirent (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) { - p = smb_decode_long_dirent (p, NULL, info_level); + smb_decode_long_dirent (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)); @@ -2253,12 +2588,26 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach } else { - p = smb_decode_long_dirent (p, current_entry, info_level); + /* 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 (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); @@ -2275,7 +2624,7 @@ smb_proc_readdir_long (struct smb_server *server, char *path, int fpos, int cach resp_param = NULL; } - first = 0; + is_first = FALSE; if (ff_searchcount > 0) loop_count = 0; @@ -2322,19 +2671,20 @@ smb_proc_getattr_core (struct smb_server *server, const char *path, int len, str LOG (("path=%s\n", path)); - retry: - 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); - if ((result = smb_request_ok (server, SMBgetatr, 10, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBgetatr, 10, 0, error_ptr); + if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; - - goto out; + else + goto out; } entry->attr = WVAL (buf, smb_vwv0); @@ -2348,17 +2698,8 @@ smb_proc_getattr_core (struct smb_server *server, const char *path, int len, str { struct tm tm; - GMTime(entry->ctime,&tm); - LOG(("ctime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->atime,&tm); - LOG(("atime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - GMTime(entry->mtime,&tm); LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); - - GMTime(entry->wtime,&tm); - LOG(("wtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); } #endif /* DEBUG */ @@ -2367,6 +2708,130 @@ smb_proc_getattr_core (struct smb_server *server, const char *path, int len, str 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; + + retry: + + if(len > 0) + parameter_count = 2 + 4 + len + 1; + else + parameter_count = 2 + 2; + + 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; + + memcpy(p,path,len); + p[len] = '\0'; + } + else + { + WSET (p, 0, fid); + WSET (p, 2, SMB_QUERY_FILE_ALL_INFO); + } + + result = smb_trans2_request (server, SMBtrans2, &resp_data_len, NULL, &resp_data, NULL, error_ptr); + if (result < 0) + { + if((*error_ptr) != error_check_smb_error) + { + if (smb_retry (server)) + goto retry; + } + + goto out; + } + + if(server->rcls != 0) + { + (*error_ptr) = error_check_smb_error; + + result = -1; + goto out; + } + + ASSERT( resp_data != NULL ); + + p = resp_data; + + p += 2 * sizeof(dword); /* CreateTime */ + p += 2 * sizeof(dword); /* LastAccessTime */ + p += 2 * sizeof(dword); /* LastWriteTime */ + + entry->ctime = entry->atime = entry->mtime = entry->wtime = local2utc (convert_long_date_to_time_t(p)); + p += 2 * sizeof(dword); /* LastChangeTime */ + + p = smb_decode_dword(p, &ext_file_attributes); + entry->attr = ext_file_attributes & 0xffff; + + 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 = end_of_file_low; + + #if DEBUG + { + struct tm tm; + + GMTime(entry->mtime,&tm); + LOG(("mtime = %ld-%02ld-%02ld %ld:%02ld:%02ld\n",tm.tm_year + 1900,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec)); + LOG(("size = %lu (0x%08lx)\n",entry->size,entry->size)); + 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) @@ -2374,11 +2839,19 @@ smb_proc_getattrE (struct smb_server *server, struct smb_dirent *entry, int * er char *buf = server->transmit_buffer; int result; + retry: + smb_setup_header (server, SMBgetattrE, 1, 0); WSET (buf, smb_vwv0, entry->fileid); - if ((result = smb_request_ok (server, SMBgetattrE, 11, 0, error_ptr)) < 0) - goto out; + 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)); @@ -2410,6 +2883,112 @@ smb_proc_getattrE (struct smb_server *server, struct smb_dirent *entry, int * er return result; } +int +smb_set_file_information(struct smb_server *server, struct smb_dirent *entry, const dword * size_ptr, int * error_ptr) +{ + unsigned char *outbuf = server->transmit_buffer; + int data_size; + char * p; + int result; + QUAD change_time; + + retry: + + data_size = (size_ptr == 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_ptr == 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_ptr == 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); + + /* Last write time */ + DSET(p, 16, change_time.Low); + DSET(p, 20, change_time.High); + + /* Change time */ + DSET(p, 24, change_time.Low); + DSET(p, 28, change_time.High); + + /* Extended file attributes */ + DSET(p, 32, entry->attr); + + /* Reserved */ + DSET(p, 36, 0); + } + else + { + /* End of file */ + DSET(p, 0, (*size_ptr)); + DSET(p, 4, 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; + } + + out: + + return result; +} + /* In core protocol, there is only 1 time to be set, we use * entry->mtime, to make touch work. */ @@ -2421,10 +3000,10 @@ smb_proc_setattr_core (struct smb_server *server, const char *path, int len, str int result; int local_time; - retry: - ASSERT( smb_payload_size(server, 8, 4 + len) >= 0 ); + retry: + p = smb_setup_header (server, SMBsetatr, 8, 4 + len); WSET (buf, smb_vwv0, new_finfo->attr); local_time = utc2local (new_finfo->mtime); @@ -2432,9 +3011,10 @@ smb_proc_setattr_core (struct smb_server *server, const char *path, int len, str p = smb_encode_ascii (p, path, len); (void) smb_encode_ascii (p, "", 0); - if ((result = smb_request_ok (server, SMBsetatr, 0, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBsetatr, 0, 0, error_ptr); + if (result < 0) { - if (smb_retry (server, error_ptr)) + if (smb_retry (server)) goto retry; } @@ -2451,6 +3031,8 @@ smb_proc_setattrE (struct smb_server *server, word fid, struct smb_dirent *new_e word date, time_value; int result; + retry: + smb_setup_header (server, SMBsetattrE, 7, 0); WSET (buf, smb_vwv0, fid); @@ -2468,6 +3050,11 @@ smb_proc_setattrE (struct smb_server *server, word fid, struct smb_dirent *new_e WSET (buf, smb_vwv6, time_value); result = smb_request_ok (server, SMBsetattrE, 0, 0, error_ptr); + if (result < 0) + { + if (smb_retry (server)) + goto retry; + } return result; } @@ -2475,31 +3062,133 @@ smb_proc_setattrE (struct smb_server *server, word fid, struct smb_dirent *new_e 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: - ASSERT( smb_payload_size(server, 0, 0) >= 0 ); - - smb_setup_header (server, SMBdskattr, 0, 0); - - if ((result = smb_request_ok (server, SMBdskattr, 5, 0, error_ptr)) < 0) + if (server->protocol >= PROTOCOL_NT1) { - if (smb_retry (server, error_ptr)) - goto retry; + 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; - goto out; + 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; + word allocblocks; + word blocksize; + word free; + + 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); + p = smb_decode_word (p, &allocblocks); + p = smb_decode_word (p, &blocksize); + (void) smb_decode_word (p, &free); + + attr->total = total; + attr->allocblocks = allocblocks; + attr->blocksize = blocksize; + attr->free = free; } - p = SMB_VWV (server->transmit_buffer); - p = smb_decode_word (p, &attr->total); - p = smb_decode_word (p, &attr->allocblocks); - p = smb_decode_word (p, &attr->blocksize); - (void) smb_decode_word (p, &attr->free); - out: + if (resp_data != NULL) + free (resp_data); + return result; } @@ -2643,7 +3332,7 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) packet[0] = 0x81; /* SESSION REQUEST */ - result = smb_request (server, NULL, NULL, 0, error_ptr); + result = smb_request (server, 0, NULL, NULL, 0, error_ptr); if (result < 0) { LOG (("Failed to send SESSION REQUEST.\n")); @@ -2684,7 +3373,9 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) p = smb_encode_dialect (p, prots[i].name, strlen (prots[i].name)); LOG (("Request SMBnegprot...\n")); - if ((result = smb_request_ok (server, SMBnegprot, 1, -1, error_ptr)) < 0) + + result = smb_request_ok (server, SMBnegprot, 1, -1, error_ptr); + if (result < 0) { LOG (("Failure requesting SMBnegprot\n")); goto fail; @@ -2840,14 +3531,14 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) /* NT LAN Manager or newer. */ if (server->protocol >= PROTOCOL_NT1) { - char *OS_id = "AmigaOS"; - char *client_id = "smbfs"; + const char *native_os = server->native_os != NULL ? server->native_os : "AmigaOS"; + const char *native_lanman = VERS; 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 (OS_id)+1 + strlen (client_id)+1) >= 0 ); + 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 (OS_id)+1 + strlen (client_id)+1); + 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); WSET (packet, smb_vwv0, 0xff); WSET (packet, smb_vwv2, given_max_xmit); @@ -2885,10 +3576,10 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) strcpy (p, server->mount_data.workgroup_name); p += strlen (p) + 1; - strcpy (p, OS_id); + strcpy (p, native_os); p += strlen (p) + 1; - strcpy (p, client_id); + strcpy (p, native_lanman); } /* LAN Manager 2.0 or older */ else @@ -2914,7 +3605,8 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) memcpy (p, server->mount_data.username, user_len); } - if ((result = smb_request_ok (server, SMBsesssetupX, 3, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBsesssetupX, 3, 0, error_ptr); + if (result < 0) { LOG (("SMBsessetupX failed\n")); goto fail; @@ -2972,7 +3664,8 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) strcpy(p,dev); - if ((result = smb_request_ok (server, SMBtconX, 3, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBtconX, 3, 0, error_ptr); + if (result < 0) { SHOWVALUE(SMB_WCT(packet)); @@ -2998,7 +3691,8 @@ smb_proc_reconnect (struct smb_server *server, int * error_ptr) p = smb_encode_ascii (p, server->mount_data.password, strlen (server->mount_data.password)); (void) smb_encode_ascii (p, dev, strlen (dev)); - if ((result = smb_request_ok (server, SMBtcon, 2, 0, error_ptr)) < 0) + result = smb_request_ok (server, SMBtcon, 2, 0, error_ptr); + if (result < 0) { LOG (("SMBtcon not verified.\n")); goto fail; diff --git a/source_code/quad_math.c b/source_code/quad_math.c old mode 100755 new mode 100644 index 8c172eb..1b0ce89 --- a/source_code/quad_math.c +++ b/source_code/quad_math.c @@ -26,8 +26,94 @@ /****************************************************************************/ +/* This function comes from Harry S. Warren, Jr.'s book "Hacker's delight". */ +static int +carry(ULONG x, ULONG y) +{ + int result; + ULONG z; + + z = (x & y) | ((x | y) & ~(x + y)); + + result = ((LONG)z) < 0 ? 1 : 0; + + return(result); +} + +/****************************************************************************/ + +/* This function comes from Harry S. Warren, Jr.'s book "Hacker's delight". + * Computes the high-order half of the 64-bit product, unsigned. Derived + * from Knuth's Algorithm M. + */ +static ULONG mulhu (ULONG u, ULONG v) +{ + ULONG u0, u1, v0, v1, w0, w1, w2, t; + + u0 = u & 0xFFFF; + u1 = u >> 16; + + v0 = v & 0xFFFF; + v1 = v >> 16; + + w0 = u0 * v0; + + t = u1 * v0 + (w0 >> 16); + + w1 = t & 0xFFFF; + w2 = t >> 16; + w1 = u0 * v1 + w1; + + return u1 * v1 + w2 + (w1 >> 16); +} + +/* Multiply two 32 bit numbers, yielding a 64 bit result value. */ +VOID +multiply_32_by_32_to_64(ULONG ab,ULONG cd,QUAD * product) +{ + product->High = mulhu(ab,cd); + product->Low = ab * cd; +} + +/* Multiply an unsigned 64 bit quantity by a 32 bit quantity, yielding a 64 + * bit product. Returns the most significant component of the product, which + * is effectively the 32 bit overflow from the 96 bit product. + */ +ULONG +multiply_64_by_32_to_64(const QUAD * const abcd,ULONG ef,QUAD * abcdef) +{ + QUAD cdef; + QUAD abef; + ULONG ab,cd; + + /* Split the factors so that the following is true: + * + * abcd = (ab * 65536 * 65536) + cd; + */ + ab = abcd->High; + cd = abcd->Low; + + /* We need to calculate the following: + * + * abcd * ef = (ab * 65536 * 65536 + cd) * ef + * = (ab * 65536 * 65536) * ef + cd * ef + * = (abef) * 65536 * 65536 + cdef + */ + multiply_32_by_32_to_64(cd,ef,&cdef); + multiply_32_by_32_to_64(ab,ef,&abef); + + /* Let's put it all together as: (cd+ef) * 65536 * 65536 + ef. */ + abcdef->Low = cdef.Low; + abcdef->High = abef.Low + cdef.High; + + return(abef.High); +} + +/****************************************************************************/ + /* Divide a 64 bit integer by a 32 bit integer, filling in a 64 bit quotient - and returning a 32 bit remainder. */ + * and returning a 32 bit remainder. + */ ULONG divide_64_by_32(const QUAD * const dividend,ULONG divisor,QUAD * quotient) { @@ -83,27 +169,63 @@ divide_64_by_32(const QUAD * const dividend,ULONG divisor,QUAD * quotient) /****************************************************************************/ +/* Add an unsigned 64 bit quantity to a 64 bit quantity, yielding a + * 64 bit sum. + */ +ULONG +add_64_plus_64_to_64(const QUAD * const a,const QUAD * const b,QUAD * ab) +{ + ULONG low,high,overflow = 0; + + /* Add the first summand to the least significant + * component of the second summand and check for + * overflow. + */ + low = a->Low + b->Low; + high = carry(a->Low, b->Low); + + /* Put it all together. */ + ab->Low = low; + + /* Add the most significant components and check + * for overflow. + */ + overflow += carry(high, a->High); + high += a->High; + + overflow += carry(high, b->High); + ab->High = high + b->High; + + return(overflow); +} + +/****************************************************************************/ + /* Subtract a 64 bit integer from another 64 bit integer, producing a - 64 bit integer difference, returning a 32 bit integer that indicates - whether or not an underflow occured. */ + * 64 bit integer difference, returning a 32 bit integer that indicates + * whether or not an underflow occured. + */ ULONG subtract_64_from_64_to_64(const QUAD * const minuend,const QUAD * const subtrahend,QUAD * difference) { QUAD extended_minuend; /* We may have to borrow if the minuend is less than the - subtrahend, so we set up a local variable to track - any underflow this might produce. */ + * subtrahend, so we set up a local variable to track + * any underflow this might produce. + */ extended_minuend.High = 0; extended_minuend.Low = minuend->High; /* First step: take care of the least significant word. If - that produces a local underflow, borrow from the most - significant word. */ + * that produces a local underflow, borrow from the most + * significant word. + */ if(minuend->Low < subtrahend->Low) { /* Borrow, and if there's nothing to be borrowed, - remember that we had an underflow. */ + * remember that we had an underflow. + */ if(extended_minuend.Low-- == 0) extended_minuend.High--; } @@ -111,7 +233,8 @@ subtract_64_from_64_to_64(const QUAD * const minuend,const QUAD * const subtrahe difference->Low = minuend->Low - subtrahend->Low; /* Second step: take care of the most significant word. If - that produces a local underflow, remember that. */ + * that produces a local underflow, remember that. + */ if(extended_minuend.Low < subtrahend->High) extended_minuend.High--; diff --git a/source_code/quad_math.h b/source_code/quad_math.h old mode 100755 new mode 100644 index 7cc7c83..5e6f85c --- a/source_code/quad_math.h +++ b/source_code/quad_math.h @@ -39,7 +39,10 @@ typedef struct /****************************************************************************/ +VOID multiply_32_by_32_to_64(ULONG ab,ULONG cd,QUAD * product); +ULONG multiply_64_by_32_to_64(const QUAD * const abcd,ULONG ef,QUAD * abcdef); ULONG divide_64_by_32(const QUAD * const dividend,ULONG divisor,QUAD * quotient); +ULONG add_64_plus_64_to_64(const QUAD * const a,const QUAD * const b,QUAD * ab); ULONG subtract_64_from_64_to_64(const QUAD * const minuend,const QUAD * const subtrahend,QUAD * difference); /****************************************************************************/ diff --git a/source_code/smakefile b/source_code/smakefile index 627b3a8..acee14c 100644 --- a/source_code/smakefile +++ b/source_code/smakefile @@ -107,7 +107,7 @@ cp850.o : cp850.c crypt.o : crypt.c system_headers.h assert.h smbfs.h dump_smb.o : dump_smb.c system_headers.h assert.h smbfs.h dump_smb.h quad_math.h main.o : main.c system_headers.h assert.h smbfs.h smb_abstraction.h utf-8-iso-8859-1-conversion.h cp437.h cp850.h errors.h dump_smb.h smbfs_rev.h -proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h +proc.o : proc.c system_headers.h assert.h smbfs.h quad_math.h errors.h smbfs_rev.h quad_math.o : quad_math.c quad_math.h smb_abstraction.o : smb_abstraction.c system_headers.h assert.h smbfs.h smb_abstraction.h sock.o : sock.c system_headers.h assert.h smbfs.h smb_abstraction.h dump_smb.h errors.h @@ -120,7 +120,7 @@ clean: -delete \#?.o $(PROJECT)(%|.debug) realclean: clean - -delete system_headers.gst tags tagfiles \#?.map all + -delete system_headers.gst tags tagfiles \#?.map ID all ############################################################################### @@ -132,11 +132,3 @@ update: version: bumprev $(VERSION) $(PROJECT) - -############################################################################### - -cvs-tag: - cvs -q tag V$(VERSION)_`type $(PROJECT)_rev.rev` - -cvs-update: - cvs update diff --git a/source_code/smb_abstraction.c b/source_code/smb_abstraction.c index 3eb3c70..d7e8d90 100644 --- a/source_code/smb_abstraction.c +++ b/source_code/smb_abstraction.c @@ -62,20 +62,6 @@ struct smba_file /*****************************************************************************/ -/* 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 - -/* The NetBIOS frame payload cannot be larger than 131071 bytes, - * which is (2^71)-1. - */ -#define MAX_NETBIOS_FRAME_SIZE 131071 - -/*****************************************************************************/ - #include "smb_abstraction.h" /*****************************************************************************/ @@ -99,6 +85,7 @@ smba_connect ( int opt_prefer_write_raw, int opt_disable_write_raw, int opt_disable_read_raw, + char * opt_native_os, int * error_ptr, int * smb_error_class_ptr, int * smb_error_ptr, @@ -152,6 +139,9 @@ smba_connect ( /* olsen (2018-05-09): Timeout for send/receive operations in seconds. */ res->server.timeout = timeout; + /* olsen (2018-05-18): Override the "Native OS" name passed to the server. */ + res->server.native_os = opt_native_os; + if(smba_setup_dircache (res,cache_size,error_ptr) < 0) { ReportError("Directory cache initialization failed (%ld, %s).",(*error_ptr),posix_strerror(*error_ptr)); @@ -280,7 +270,7 @@ smba_disconnect (smba_server_t * server) /*****************************************************************************/ static int -make_open (smba_file_t * f, int need_fid, int * error_ptr) +make_open (smba_file_t * f, int need_fid, int writable, int truncate, int * error_ptr) { smba_server_t *s; int result; @@ -293,17 +283,32 @@ make_open (smba_file_t * f, int need_fid, int * error_ptr) if (!f->is_valid || f->attr_time == 0 || (now > f->attr_time && now - f->attr_time > ATTR_CACHE_TIME)) { - result = smb_proc_getattr_core (&s->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + if (f->server->server.protocol >= PROTOCOL_LANMAN2) + result = smb_query_path_information (&s->server, f->dirent.complete_path, f->dirent.len, 0, &f->dirent, error_ptr); + else + result = smb_proc_getattr_core (&s->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + if (result < 0) goto out; } if ((f->dirent.attr & aDIR) == 0) /* a regular file */ { - if (need_fid || !s->supports_E_known || s->supports_E) + if (f->server->server.protocol >= PROTOCOL_LANMAN2) + { + if(need_fid) + { + LOG (("opening file %s\n", f->dirent.complete_path)); + + result = smb_proc_open (&s->server, f->dirent.complete_path, f->dirent.len, writable, truncate, &f->dirent, error_ptr); + if (result < 0) + goto out; + } + } + else if (need_fid || !s->supports_E_known || s->supports_E) { LOG (("opening file %s\n", f->dirent.complete_path)); - result = smb_proc_open (&s->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + result = smb_proc_open (&s->server, f->dirent.complete_path, f->dirent.len, writable, truncate, &f->dirent, error_ptr); if (result < 0) goto out; @@ -350,7 +355,7 @@ make_open (smba_file_t * f, int need_fid, int * error_ptr) /*****************************************************************************/ int -smba_open (smba_server_t * s, char *name, size_t name_size, smba_file_t ** file, int * error_ptr) +smba_open (smba_server_t * s, char *name, size_t name_size, int writable, int truncate, smba_file_t ** file, int * error_ptr) { smba_file_t *f; int result; @@ -373,7 +378,7 @@ smba_open (smba_server_t * s, char *name, size_t name_size, smba_file_t ** file, f->dirent.len = strlen (name); f->server = s; - result = make_open (f, 0, error_ptr); + result = make_open (f, open_dont_need_fid, writable, truncate, error_ptr); if (result < 0) goto out; @@ -399,14 +404,25 @@ write_attr (smba_file_t * f, int * error_ptr) LOG (("file %s\n", f->dirent.complete_path)); - result = make_open (f, 0, error_ptr); - if (result < 0) - goto out; + if(f->server->server.protocol >= PROTOCOL_LANMAN2) + { + result = make_open (f, open_need_fid, open_writable, open_dont_truncate, error_ptr); + if (result < 0) + goto out; - if (f->dirent.opened && f->server->supports_E) - result = smb_proc_setattrE (&f->server->server, f->dirent.fileid, &f->dirent, error_ptr); + result = smb_set_file_information (&f->server->server, &f->dirent, NULL, error_ptr); + } else - result = smb_proc_setattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + { + result = make_open (f, open_dont_need_fid, open_read_only, open_dont_truncate, error_ptr); + if (result < 0) + goto out; + + if (f->dirent.opened && f->server->supports_E) + result = smb_proc_setattrE (&f->server->server, f->dirent.fileid, &f->dirent, error_ptr); + else + result = smb_proc_setattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + } if (result < 0) { @@ -414,7 +430,7 @@ write_attr (smba_file_t * f, int * error_ptr) goto out; } - f->attr_dirty = 0; + f->attr_dirty = FALSE; out: @@ -460,14 +476,75 @@ smba_read (smba_file_t * f, char *data, long len, long offset, int * error_ptr) int num_bytes_read = 0; int result; - result = make_open (f, 1, error_ptr); + result = make_open (f, open_need_fid, open_read_only, open_dont_truncate, error_ptr); if (result < 0) goto out; D(("read %ld bytes from offset %ld",len,offset)); + if (f->server->server.protocol >= PROTOCOL_LANMAN1) + { + int max_readx_size; + int count; + + /* Calculate maximum number of bytes that could be transferred with + * a single SMBreadX packet... + * + * 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_READ_ANDX response account for + * 1(wordcount)+1(andxcommand)+1(andxreserved)+2(andxoffset)+ + * 2(available)+2(datacompactionmode)+2(reserved)+2(datalength)+ + * 2(dataoffset)+10(reserved2) + * = 25 bytes + * + * The data part of a SMB_COM_READ_ANDX response account for + * 2(bytecount)+1(pad) = 3 bytes, + * not including the actual payload + * + * This leaves 'max_receive' - 60 for the payload. + */ + + max_readx_size = f->server->server.max_buffer_size - 60; + + if((f->server->server.capabilities & CAP_LARGE_READX) != 0) + max_readx_size = 65535; + + /* ZZZ SMB_COM_READ_ANDX uses the packet buffer to receive + * the data, which is why there is another limit to how + * much data can be received. + */ + if(max_readx_size > f->server->server.transmit_buffer_size - 62) + max_readx_size = f->server->server.transmit_buffer_size - 62; + + LOG (("len = %ld, max_readx_size = %ld\n", len, max_readx_size)); + + do + { + count = min(len, max_readx_size); + + result = smb_proc_readx (&f->server->server, &f->dirent, offset, count, data, error_ptr); + if (result < 0) + goto out; + + num_bytes_read += result; + len -= result; + offset += result; + data += result; + + if(result < count) + { + D(("read returned fewer characters than expected (%ld < %ld)",result,count)); + break; + } + } + while (len > 0); + } /* SMB_COM_READ_RAW and SMB_COM_WRITE_RAW supported? */ - if ((f->server->server.capabilities & CAP_RAW_MODE) != 0 && !f->server->server.disable_read_raw) + else if ((f->server->server.capabilities & CAP_RAW_MODE) != 0 && !f->server->server.disable_read_raw) { int max_raw_size = f->server->server.max_raw_size; int n; @@ -534,14 +611,14 @@ smba_read (smba_file_t * f, char *data, long len, long offset, int * error_ptr) * This leaves 'max_buffer_size' - 48 for the payload. */ /*max_size_smb_com_read = f->server->server.max_buffer_size - SMB_HEADER_LEN - 5 * 2 - 5;*/ - max_size_smb_com_read = f->server->server.max_buffer_size - 48 - NETBIOS_HEADER_SIZE; + max_size_smb_com_read = f->server->server.max_buffer_size - 48; /* ZZZ SMB_COM_READ uses the packet buffer to receive * the data, which is why there is another limit to how * much data can be received. */ - if(max_size_smb_com_read > f->server->server.transmit_buffer_size - 48 - NETBIOS_HEADER_SIZE) - max_size_smb_com_read = f->server->server.transmit_buffer_size - 48 - NETBIOS_HEADER_SIZE; + if(max_size_smb_com_read > f->server->server.transmit_buffer_size - 48) + max_size_smb_com_read = f->server->server.transmit_buffer_size - 48; do { @@ -591,7 +668,7 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro int max_buffer_size; int result; - result = make_open (f, 1, error_ptr); + result = make_open (f, open_need_fid, open_writable, open_dont_truncate, error_ptr); if (result < 0) goto out; @@ -599,7 +676,68 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro max_buffer_size = f->server->server.max_buffer_size; - if ((f->server->server.capabilities & CAP_RAW_MODE) != 0 && !f->server->server.disable_write_raw) + /* SMB_COM_WRITE_ANDX supported? */ + if (f->server->server.protocol >= PROTOCOL_LANMAN1) + { + int max_writex_size; + int n; + + /* Calculate maximum number of bytes that could be transferred with + * a single SMB_COM_WRITE_ANDX 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_ANDX command account for + * 1(word_count)+1(and_x_command)+1(and_x_reserved)+2(and_x_offset)+ + * 2(fid)+4(offset)+4(timeout)+2(write_mode)+2(remaining)+2(reserved)+ + * 2(data_length)+2(data_offset)+4(offset_high) + * = 29 bytes + * + * The data part of a SMB_COM_WRITE_ANDX command account for + * 2(byte_count)+1(pad) = 3 bytes, not including + * the actual payload + * + * This leaves 'max_buffer_size' - 64 for the payload. + */ + + if(f->server->server.protocol >= PROTOCOL_NT1) + max_writex_size = max_buffer_size - 64; + else + max_writex_size = max_buffer_size - 60; + + if((f->server->server.capabilities & CAP_LARGE_WRITEX) != 0) + max_writex_size = 65535; + + LOG (("len = %ld, max_writex_size = %ld\n", len, max_writex_size)); + + do + { + n = min(len, max_writex_size); + + ASSERT( n > 0 ); + + LOG(("writing %ld bytes; offset=%lu, len=%ld\n", n, offset, len)); + + result = smb_proc_writex(&f->server->server, &f->dirent, offset, n, data, error_ptr); + if(result < 0) + goto out; + + LOG(("number of bytes written = %ld\n", result)); + + data += result; + offset += result; + len -= result; + num_bytes_written += result; + } + while(len > 0); + } + else if ((f->server->server.capabilities & CAP_RAW_MODE) != 0 && !f->server->server.disable_write_raw) { int max_raw_size = f->server->server.max_raw_size; int max_size_smb_com_write_raw; @@ -624,7 +762,7 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro * This leaves 'max_buffer_size' - 59 for the payload. */ /*max_size_smb_com_write_raw = 2 * f->server->server.max_buffer_size - (SMB_HEADER_LEN + 12 * sizeof (word) + 4) - 8;*/ - max_size_smb_com_write_raw = max(max_buffer_size, max_raw_size) - 59 - NETBIOS_HEADER_SIZE; + max_size_smb_com_write_raw = max(max_buffer_size, max_raw_size) - 59; /* SMB_COM_WRITE_RAW cannot transmit more than 65535 bytes. */ if(max_size_smb_com_write_raw > 65535) @@ -680,7 +818,7 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro * This leaves 'max_buffer_size' - 48 for the payload. */ /*max_size_smb_com_write = f->server->server.max_buffer_size - (SMB_HEADER_LEN + 5 * sizeof (word) + 5) - 4;*/ - max_size_smb_com_write = max_buffer_size - 48 - NETBIOS_HEADER_SIZE; + max_size_smb_com_write = max_buffer_size - 48; /* SMB_COM_WRITE cannot transmit more than 65535 bytes. */ if(max_size_smb_com_write > 65535) @@ -708,6 +846,8 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro while (len > 0); } + LOG(("num_bytes_written=%ld\n", num_bytes_written)); + result = num_bytes_written; out: @@ -729,26 +869,6 @@ smba_write (smba_file_t * f, const char *data, long len, long offset, int * erro /*****************************************************************************/ -int -smba_seek (smba_file_t *f, long offset, long mode, off_t * new_position_ptr, int * error_ptr) -{ - int result; - - D(("seek %ld bytes from position %s",offset,mode > 0 ? (mode == 2 ? "SEEK_END" : "SEEK_CUR") : "SEEK_SET")); - - result = make_open (f, 1, error_ptr); - if(result < 0) - goto out; - - result = smb_proc_lseek (&f->server->server, &f->dirent, offset, mode, new_position_ptr, error_ptr); - - out: - - return result; -} - -/*****************************************************************************/ - /* perform a single record-lock */ int smba_lockrec (smba_file_t *f, long offset, long len, long mode, int unlocked, long timeout, int * error_ptr) @@ -756,7 +876,7 @@ smba_lockrec (smba_file_t *f, long offset, long len, long mode, int unlocked, lo struct smb_lkrng *rec_lock = NULL; int result; - result = make_open (f, 1, error_ptr); + result = make_open (f, open_need_fid, open_read_only, open_dont_truncate, error_ptr); if(result < 0) goto out; @@ -793,7 +913,7 @@ smba_getattr (smba_file_t * f, smba_stat_t * data, int * error_ptr) int result; ULONG now; - result = make_open (f, 0, error_ptr); + result = make_open (f, open_dont_need_fid, open_read_only, open_dont_truncate, error_ptr); if (result < 0) goto out; @@ -803,10 +923,20 @@ smba_getattr (smba_file_t * f, smba_stat_t * data, int * error_ptr) { LOG (("file %s\n", f->dirent.complete_path)); - if (f->dirent.opened && f->server->supports_E) - result = smb_proc_getattrE (&f->server->server, &f->dirent, error_ptr); + if (f->server->server.protocol >= PROTOCOL_LANMAN2) + { + if (f->dirent.opened) + result = smb_query_path_information (&f->server->server, NULL, 0, f->dirent.fileid, &f->dirent, error_ptr); + else + result = smb_query_path_information (&f->server->server, f->dirent.complete_path, f->dirent.len, 0, &f->dirent, error_ptr); + } else - result = smb_proc_getattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + { + if (f->dirent.opened && f->server->supports_E) + result = smb_proc_getattrE (&f->server->server, &f->dirent, error_ptr); + else + result = smb_proc_getattr_core (&f->server->server, f->dirent.complete_path, f->dirent.len, &f->dirent, error_ptr); + } if (result < 0) goto out; @@ -833,51 +963,78 @@ smba_getattr (smba_file_t * f, smba_stat_t * data, int * error_ptr) /*****************************************************************************/ int -smba_setattr (smba_file_t * f, smba_stat_t * data, int * error_ptr) +smba_setattr (smba_file_t * f, const smba_stat_t * data, const dword * size_ptr, int * error_ptr) { + BOOL times_changed = FALSE; + dword attrs; int result; - if (data->atime != -1) - f->dirent.atime = data->atime; - - if (data->ctime != -1) - f->dirent.ctime = data->ctime; - - if (data->mtime != -1) - f->dirent.mtime = data->mtime; - - if (data->is_wp) - f->dirent.attr |= aRONLY; - else - f->dirent.attr &= ~aRONLY; - - if (data->is_archive) - f->dirent.attr |= aARCH; - else - f->dirent.attr &= ~aARCH; - - if (data->is_system) - f->dirent.attr |= aSYSTEM; - else - f->dirent.attr &= ~aSYSTEM; - - f->attr_dirty = 1; - - result = write_attr (f, error_ptr); - if (result < 0) - goto out; - - if (data->size != -1 && data->size != (int)f->dirent.size) + if (data != NULL) { - result = make_open (f, 1, error_ptr); + if (data->atime != -1 && f->dirent.atime != data->atime) + { + f->dirent.atime = data->atime; + times_changed = TRUE; + } + + if (data->ctime != -1 && f->dirent.ctime != data->ctime) + { + f->dirent.ctime = data->ctime; + times_changed = TRUE; + } + + if (data->mtime != -1 && f->dirent.mtime != data->mtime) + { + f->dirent.mtime = data->mtime; + times_changed = TRUE; + } + + attrs = f->dirent.attr; + + if (data->is_wp) + attrs |= aRONLY; + else + attrs &= ~aRONLY; + + if (data->is_archive) + attrs |= aARCH; + else + attrs &= ~aARCH; + + if (data->is_system) + attrs |= aSYSTEM; + else + attrs &= ~aSYSTEM; + + if(f->dirent.attr != attrs) + { + f->dirent.attr = attrs; + f->attr_dirty = TRUE; + } + + if(f->attr_dirty || times_changed) + { + result = write_attr (f, error_ptr); + if (result < 0) + goto out; + } + } + + if (size_ptr != NULL && (*size_ptr) != f->dirent.size) + { + result = make_open (f, open_need_fid, open_writable, open_dont_truncate, error_ptr); if(result < 0) goto out; - result = smb_proc_trunc (&f->server->server, f->dirent.fileid, data->size, error_ptr); + if(f->server->server.protocol >= PROTOCOL_LANMAN2) + result = smb_set_file_information (&f->server->server, &f->dirent, size_ptr, error_ptr); + else + result = smb_proc_trunc (&f->server->server, f->dirent.fileid, (*size_ptr), error_ptr); + if(result < 0) goto out; - f->dirent.size = data->size; + f->dirent.size = (*size_ptr); } out: @@ -896,7 +1053,7 @@ smba_readdir (smba_file_t * f, long offs, void *d, smba_callback_t callback, int int result; ULONG now; - result = make_open (f, 0, error_ptr); + result = make_open (f, open_dont_need_fid, open_read_only, open_dont_truncate, error_ptr); if (result < 0) goto out; @@ -1063,7 +1220,7 @@ smba_create (smba_file_t * dir, const char *name, smba_stat_t * attr, int * erro char *path = NULL; int result; - result = make_open (dir, 0, error_ptr); + result = make_open (dir, open_dont_need_fid, open_read_only, open_dont_truncate, error_ptr); if (result < 0) goto out; @@ -1093,7 +1250,11 @@ smba_create (smba_file_t * dir, const char *name, smba_stat_t * attr, int * erro path[dir->dirent.len] = DOS_PATHSEP; strcpy (&path[dir->dirent.len + 1], name); - result = smb_proc_create (&dir->server->server, path, strlen (path), &entry, error_ptr); + if (dir->server->server.protocol >= PROTOCOL_LANMAN2) + result = smb_proc_open (&dir->server->server, path, strlen(path), open_writable, open_truncate, &entry, error_ptr); + else + result = smb_proc_create (&dir->server->server, path, strlen (path), &entry, error_ptr); + if(result < 0) goto out; @@ -1115,7 +1276,7 @@ smba_mkdir (smba_file_t * dir, const char *name, int * error_ptr) char *path = NULL; int result; - result = make_open (dir, 0, error_ptr); + result = make_open (dir, open_dont_need_fid, open_read_only, open_dont_truncate, error_ptr); if (result < 0) goto out; @@ -1430,6 +1591,7 @@ smba_start( int opt_prefer_write_raw, int opt_disable_write_raw, int opt_disable_read_raw, + char * opt_native_os, int * error_ptr, int * smb_error_class_ptr, int * smb_error_ptr, @@ -1602,6 +1764,7 @@ smba_start( opt_prefer_write_raw, opt_disable_write_raw, opt_disable_read_raw, + opt_native_os, error_ptr, smb_error_class_ptr, smb_error_ptr, diff --git a/source_code/smb_abstraction.h b/source_code/smb_abstraction.h index f167486..764ab28 100644 --- a/source_code/smb_abstraction.h +++ b/source_code/smb_abstraction.h @@ -21,6 +21,21 @@ struct smb_server; #endif /* _SMB_FS_SB */ +/*****************************************************************************/ + +/* These should make the respective purpose of each make_open() + * parameter clearer. The read/write and truncate options are + * also used by smba_open(). + */ +#define open_dont_need_fid FALSE +#define open_need_fid TRUE + +#define open_read_only FALSE +#define open_writable TRUE + +#define open_dont_truncate FALSE +#define open_truncate TRUE + /****************************************************************************/ typedef struct smba_connect_parameters @@ -57,14 +72,13 @@ typedef int (*smba_callback_t) (void *d, int fpos, int nextpos, char *name, int /****************************************************************************/ -int smba_open(smba_server_t *s, char *name, size_t name_size, smba_file_t **file, int * error_ptr); +int smba_open(smba_server_t *s, char *name, size_t name_size, int writable, int truncate, smba_file_t **file, int * error_ptr); void smba_close(smba_file_t *f, int * error_ptr); int smba_read(smba_file_t *f, char *data, long len, long offset, int * error_ptr); int smba_write(smba_file_t *f, const char *data, long len, long offset, int * error_ptr); -int smba_seek (smba_file_t *f, long offset, long mode, off_t * new_position_ptr, int * error_ptr); int smba_lockrec (smba_file_t *f, long offset, long len, long mode, int unlocked, long timeout, int * error_ptr); int smba_getattr(smba_file_t *f, smba_stat_t *data, int * error_ptr); -int smba_setattr(smba_file_t *f, smba_stat_t *data, int * error_ptr); +int smba_setattr(smba_file_t *f, const smba_stat_t *data, const unsigned long * size_ptr, int * error_ptr); int smba_readdir(smba_file_t *f, long offs, void *d, smba_callback_t callback, int * error_ptr); int smba_create(smba_file_t *dir, const char *name, smba_stat_t *attr, int * error_ptr); int smba_mkdir(smba_file_t *dir, const char *name, int * error_ptr); @@ -73,7 +87,7 @@ int smba_rmdir(smba_server_t *s, char *path, int * error_ptr); int smba_rename(smba_server_t *s, char *from, char *to, int * error_ptr); int smba_statfs(smba_server_t *s, long *bsize, long *blocks, long *bfree, int * error_ptr); void smb_invalidate_all_inodes(struct smb_server *server); -int smba_start(char *service, char *opt_workgroup, char *opt_username, char *opt_password, char *opt_clientname, char *opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, int opt_raw_smb, int opt_write_behind, int opt_prefer_write_raw, int opt_disable_write_raw, int opt_disable_read_raw, int * error_ptr, int * smb_error_class_ptr, int * smb_error_ptr, smba_server_t **result); +int smba_start(char *service, char *opt_workgroup, char *opt_username, char *opt_password, char *opt_clientname, char *opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, int opt_raw_smb, int opt_write_behind, int opt_prefer_write_raw, int opt_disable_write_raw, int opt_disable_read_raw, char * opt_native_os, int * error_ptr, int * smb_error_class_ptr, int * smb_error_ptr, smba_server_t **result); void smba_disconnect(smba_server_t *server); int smba_get_dircache_size(struct smba_server * server); int smba_change_dircache_size(struct smba_server * server,int cache_size); diff --git a/source_code/smbfs_rev.h b/source_code/smbfs_rev.h index 55b461a..949e41b 100644 --- a/source_code/smbfs_rev.h +++ b/source_code/smbfs_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 130 -#define DATE "13.5.2018" -#define VERS "smbfs 1.130" -#define VSTRING "smbfs 1.130 (13.5.2018)\r\n" -#define VERSTAG "\0$VER: smbfs 1.130 (13.5.2018)" +#define REVISION 132 +#define DATE "19.5.2018" +#define VERS "smbfs 1.132" +#define VSTRING "smbfs 1.132 (19.5.2018)\r\n" +#define VERSTAG "\0$VER: smbfs 1.132 (19.5.2018)" diff --git a/source_code/smbfs_rev.rev b/source_code/smbfs_rev.rev index fd03ab2..94361d4 100644 --- a/source_code/smbfs_rev.rev +++ b/source_code/smbfs_rev.rev @@ -1 +1 @@ -130 +132 diff --git a/source_code/sock.c b/source_code/sock.c index 25d045a..35c86b4 100644 --- a/source_code/sock.c +++ b/source_code/sock.c @@ -10,22 +10,6 @@ #include "smbfs.h" -/****************************************************************************/ - -/* A quick workaround for the timeval/timerequest->TimeVal/TimeRequest - change in the recent OS4 header files. */ -#if defined(__NEW_TIMEVAL_DEFINITION_USED__) - -#define timeval TimeVal -#define tv_secs Seconds -#define tv_micro Microseconds - -#define timerequest TimeRequest -#define tr_node Request -#define tr_time Time - -#endif /* __NEW_TIMEVAL_DEFINITION_USED__ */ - /*****************************************************************************/ #include @@ -113,6 +97,7 @@ receive_all(int fd, void * _data, int len, int * error_ptr) static int smb_receive_raw ( struct smb_server * server, + int command, int sock_fd, unsigned char * target, int max_raw_length, @@ -155,7 +140,7 @@ smb_receive_raw ( #if defined(DUMP_SMB) { - if(netbios_session_buf[0] != 0x00 && netbios_session_payload_size > 0) + if(command != 0 && netbios_session_buf[0] != 0x00 && netbios_session_payload_size > 0) { /* We only want to show what's in the first few * bytes of a session packet. Since we only support @@ -213,12 +198,21 @@ smb_receive_raw ( /* 0x84 == retarget session response */ default: - LOG (("Invalid session header type 0x%02lx\n", netbios_session_buf[0])); + /* The session setup may need to know about the + * NetBIOS session response, but for any command + * these message types are invalid. + */ + if(command != 0) + { + LOG (("Invalid session header type 0x%02lx\n", netbios_session_buf[0])); - (*error_ptr) = error_invalid_netbios_session; + (*error_ptr) = error_invalid_netbios_session; - result = -1; - goto out; + result = -1; + goto out; + } + + break; } /* The length in the NetBIOS header is the raw data length (17 bits) */ @@ -236,158 +230,113 @@ smb_receive_raw ( /* Prepend the NetBIOS header to what is read? */ if (want_header) { - /* Check for buffer overflow. */ - if(NETBIOS_HEADER_SIZE + len > max_raw_length) - { - LOG (("Received length (%ld) > max_xmit (%ld)!\n", len, max_raw_length)); - - (*error_ptr) = error_message_exceeds_buffer_size; - - result = -1; - goto out; - } - memcpy (target, netbios_session_buf, NETBIOS_HEADER_SIZE); target += NETBIOS_HEADER_SIZE; } - /* This is an optimization for the SMB_COM_READ command, which - * tries to avoid copying the received data twice. To this end - * we receive the SMB_COM_READ response up to the point at which + /* This is an optimization for the SMB_COM_READ and SMB_COM_READ_ANDX + * commands, which tries to avoid copying the received data twice. To + * this end we receive the command response up to the point at which * the message header ends and the data returned by the server * begins. Then we read the data, storing it directly in the * receive buffer rather than in the packet buffer, from which - * it would have to be retrieved later. + * it would otherwise have to be retrieved later. */ if(input_payload != NULL) { - int count_of_bytes_returned; - int count_of_bytes_to_read; int num_bytes_received = 0; - int buffer_format; LOG(("input_payload=0x%08lx, payload_size=%ld\n", input_payload, input_payload_size)); - /* We need to read the following data: - * - * 0: 32 bytes of SMB message header - * 32: 1 byte of word count - * 33: 2 bytes of 'count of bytes returned' (1 word) - * 35: 8 bytes of reserved data (4 words) - * 43: 2 bytes of 'byte count' (1 word) - * 45: 1 byte of 'buffer format' - * 46: 2 bytes of 'count of bytes to read' (1 word). - * - * This adds up to 48 bytes. - */ - - LOG(("reading the first %ld bytes\n", 48)); - - result = receive_all (sock_fd, target, 48, error_ptr); - if (result < 0) + if(command == SMBreadX) { - LOG (("recv error = %ld\n", (*error_ptr))); - goto out; - } + int data_length; + int data_offset; - num_bytes_received += result; + /* We need to read the following data: + * + * 0: 32 bytes of SMB message header + * 32: 1 byte of word count + * 33: 1 byte of andxcommand + * 34: 1 byte of andxreserved + * 35: 2 bytes of andxoffset + * 37: 2 bytes of available + * 39: 2 bytes of datacompactionmode + * 41: 2 bytes of reserved + * 43: 2 bytes of datalength + * 45: 2 bytes of dataoffset + * 47: 10 bytes of reserved + * 57: 2 bytes of bytecount + * + * This adds up to 59 bytes. + */ - ASSERT( num_bytes_received == 48 ); + LOG(("SMBreadX: reading the first %ld bytes\n", 59)); - /* End of file reached? */ - if(num_bytes_received < 48) - { - /* End of file */ - LOG (("EOF\n")); - - (*error_ptr) = error_end_of_file; - - result = -1; - goto out; - } - - /* So we read the header. Now we need to figure out if the - * data is in the expected format, and how many bytes are - * waiting to be read. - */ - - /* The buffer format must be 1. */ - buffer_format = BVAL(target, 45); - - LOG(("buffer format = %ld, should be %ld\n", buffer_format, 1)); - - if(buffer_format != 1) - { - LOG(("buffer format %ld not supported\n", buffer_format)); - - (*error_ptr) = error_invalid_buffer_format; - - result = -1; - goto out; - } - - count_of_bytes_returned = WVAL(target, 33); - count_of_bytes_to_read = WVAL(target, 46); - - /* That should never be more data than the read buffer may hold. */ - ASSERT( count_of_bytes_to_read <= input_payload_size ); - ASSERT( count_of_bytes_to_read <= count_of_bytes_returned ); - ASSERT( count_of_bytes_returned <= input_payload_size ); - - LOG(("count of bytes to read = %ld, should be <= %ld\n", count_of_bytes_to_read, input_payload_size)); - - if(count_of_bytes_returned > input_payload_size) - { - (*error_ptr) = error_message_exceeds_buffer_size; - - result = -1; - goto out; - } - - result = receive_all (sock_fd, input_payload, count_of_bytes_to_read, error_ptr); - if (result < 0) - { - LOG (("recv error = %ld\n", (*error_ptr))); - goto out; - } - - if(result < count_of_bytes_to_read) - { - /* End of file */ - LOG (("EOF\n")); - - (*error_ptr) = error_end_of_file; - - result = -1; - goto out; - } - - num_bytes_received += result; - - ASSERT( count_of_bytes_to_read == count_of_bytes_returned ); - - if(count_of_bytes_to_read < count_of_bytes_returned) - { - LOG(("fewer data available than should be delivered; setting the remainder (%ld bytes) to 0.\n", count_of_bytes_returned - count_of_bytes_to_read)); - - memset(&input_payload[count_of_bytes_to_read],0,count_of_bytes_returned - count_of_bytes_to_read); - } - - /* This should never happen, but then we better make sure to - * read the entire message. - */ - if(num_bytes_received < len) - { - LOG(("reading the remaining %ld bytes; this should never happen\n", len - num_bytes_received )); - - result = receive_all (sock_fd, &target[num_bytes_received], len - num_bytes_received, error_ptr); + result = receive_all (sock_fd, target, 59, error_ptr); if (result < 0) { LOG (("recv error = %ld\n", (*error_ptr))); goto out; } - if(result < len - num_bytes_received) + num_bytes_received += result; + + ASSERT( num_bytes_received == 59 ); + + /* End of file reached? */ + if(num_bytes_received < 59) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + + data_offset = WVAL(target, 45); + + SHOWVALUE(data_offset); + + /* Skip the padding bytes, if any. */ + if(data_offset > 59) + { + result = receive_all (sock_fd, target + 59, data_offset - 59, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + /* End of file reached? */ + if(result < data_offset - 59) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + + num_bytes_received += result; + } + + data_length = WVAL(target, 43); + + SHOWVALUE(data_length); + + result = receive_all (sock_fd, input_payload, data_length, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + if(result < data_length) { /* End of file */ LOG (("EOF\n")); @@ -397,20 +346,187 @@ smb_receive_raw ( result = -1; goto out; } - } - #if defined(DUMP_SMB) + num_bytes_received += result; + + /* This should never happen, but then we better make sure to + * read the entire message. + */ + if(num_bytes_received < len) + { + LOG(("reading the remaining %ld bytes; this should never happen\n", len - num_bytes_received )); + + result = receive_all (sock_fd, &target[num_bytes_received], len - num_bytes_received, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + if(result < len - num_bytes_received) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + } + } + else { - /* If want_header==0 then this is the data returned by SMB_COM_READ_RAW. */ - dump_smb(__FILE__,__LINE__,!want_header,target,48,smb_packet_to_consumer,server->max_recv); + int count_of_bytes_returned; + int count_of_bytes_to_read; + int buffer_format; - if(buffer_format == 1) - dump_smb(__FILE__,__LINE__,!want_header,input_payload,num_bytes_received - 48,smb_packet_to_consumer,server->max_recv); + ASSERT( command == SMBread ); - if(num_bytes_received < len && result > 0) - dump_smb(__FILE__,__LINE__,!want_header,&target[num_bytes_received],result,smb_packet_to_consumer,server->max_recv); + /* We need to read the following data: + * + * 0: 32 bytes of SMB message header + * 32: 1 byte of word count + * 33: 2 bytes of 'count of bytes returned' (1 word) + * 35: 8 bytes of reserved data (4 words) + * 43: 2 bytes of 'byte count' (1 word) + * 45: 1 byte of 'buffer format' + * 46: 2 bytes of 'count of bytes to read' (1 word). + * + * This adds up to 48 bytes. + */ + + LOG(("SMBread: reading the first %ld bytes\n", 48)); + + result = receive_all (sock_fd, target, 48, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + num_bytes_received += result; + + ASSERT( num_bytes_received == 48 ); + + /* End of file reached? */ + if(num_bytes_received < 48) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + + /* So we read the header. Now we need to figure out if the + * data is in the expected format, and how many bytes are + * waiting to be read. + */ + + /* The buffer format must be 1. */ + buffer_format = BVAL(target, 45); + + LOG(("buffer format = %ld, should be %ld\n", buffer_format, 1)); + + if(buffer_format != 1) + { + LOG(("buffer format %ld not supported\n", buffer_format)); + + (*error_ptr) = error_invalid_buffer_format; + + result = -1; + goto out; + } + + count_of_bytes_returned = WVAL(target, 33); + count_of_bytes_to_read = WVAL(target, 46); + + /* That should never be more data than the read buffer may hold. */ + ASSERT( count_of_bytes_to_read <= input_payload_size ); + ASSERT( count_of_bytes_to_read <= count_of_bytes_returned ); + ASSERT( count_of_bytes_returned <= input_payload_size ); + + LOG(("count of bytes to read = %ld, should be <= %ld\n", count_of_bytes_to_read, input_payload_size)); + + if(count_of_bytes_returned > input_payload_size) + { + (*error_ptr) = error_message_exceeds_buffer_size; + + result = -1; + goto out; + } + + result = receive_all (sock_fd, input_payload, count_of_bytes_to_read, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + if(result < count_of_bytes_to_read) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + + num_bytes_received += result; + + ASSERT( count_of_bytes_to_read == count_of_bytes_returned ); + + if(count_of_bytes_to_read < count_of_bytes_returned) + { + LOG(("fewer data available than should be delivered; setting the remainder (%ld bytes) to 0.\n", count_of_bytes_returned - count_of_bytes_to_read)); + + memset(&input_payload[count_of_bytes_to_read],0,count_of_bytes_returned - count_of_bytes_to_read); + } + + /* This should never happen, but then we better make sure to + * read the entire message. + */ + if(num_bytes_received < len) + { + LOG(("reading the remaining %ld bytes; this should never happen\n", len - num_bytes_received )); + + result = receive_all (sock_fd, &target[num_bytes_received], len - num_bytes_received, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + if(result < len - num_bytes_received) + { + /* End of file */ + LOG (("EOF\n")); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + } + + #if defined(DUMP_SMB) + { + /* If want_header==0 then this is the data returned by SMB_COM_READ_RAW. */ + dump_smb(__FILE__,__LINE__,!want_header,target,48,smb_packet_to_consumer,server->max_recv); + + if(buffer_format == 1) + dump_smb(__FILE__,__LINE__,!want_header,input_payload,num_bytes_received - 48,smb_packet_to_consumer,server->max_recv); + + if(num_bytes_received < len && result > 0) + dump_smb(__FILE__,__LINE__,!want_header,&target[num_bytes_received],result,smb_packet_to_consumer,server->max_recv); + } + #endif /* defined(DUMP_SMB) */ } - #endif /* defined(DUMP_SMB) */ result = num_bytes_received; } @@ -448,7 +564,7 @@ smb_receive_raw ( } static int -smb_receive (struct smb_server *server, int sock_fd, void * input_payload, int payload_size, int * error_ptr) +smb_receive (struct smb_server *server, int command, int sock_fd, void * input_payload, int payload_size, int * error_ptr) { byte * packet = server->transmit_buffer; int result; @@ -456,11 +572,26 @@ smb_receive (struct smb_server *server, int sock_fd, void * input_payload, int p ASSERT( error_ptr != NULL ); ASSERT( server->max_recv <= server->transmit_buffer_size ); - result = smb_receive_raw (server, sock_fd, packet, - server->max_recv, - input_payload, payload_size, - TRUE, /* We want the NetBIOS session service header */ - error_ptr); + result = smb_receive_raw ( + server, + command, + sock_fd, + packet, + /* Note: Worst case, if the inbound data has to go into the buffer, with + * no separate payload buffer provided. This is a "workaround" for + * SMB_COM_READ_ANDX which can end up receiving the regular + * SMB message header plus 65535 bytes of payload data, which + * easily breaks the limit set by server->max_recv (which only + * accounts for the in-band reception limit, not the out-of-band + * limit if the "large readx" capability is in effect). + * ZZZ this merits another check in smb_receive_raw(). + */ + server->transmit_buffer_allocation_size, + input_payload, payload_size, + /* We want the NetBIOS session service header */ + TRUE, + error_ptr); + if (result < 0) { LOG (("receive error: %ld\n", (*error_ptr))); @@ -482,6 +613,7 @@ smb_receive (struct smb_server *server, int sock_fd, void * input_payload, int p static int smb_receive_trans2 ( struct smb_server * server, + int command, int sock_fd, int * data_len_ptr, int * param_len_ptr, @@ -507,16 +639,12 @@ smb_receive_trans2 ( int total_param; int result; - ASSERT( data_len_ptr != NULL ); - ASSERT( param_len_ptr != NULL ); - ASSERT( data_ptr != NULL ); - ASSERT( param_ptr != NULL ); ASSERT( error_ptr != NULL ); (*data_len_ptr) = (*param_len_ptr) = 0; (*param_ptr) = (*data_ptr) = NULL; - result = smb_receive (server, sock_fd, NULL, 0, error_ptr); + result = smb_receive (server, command, sock_fd, NULL, 0, error_ptr); if (result < 0) goto out; @@ -641,7 +769,7 @@ smb_receive_trans2 ( if (total_data <= data_len && total_param <= param_len) break; - result = smb_receive (server, sock_fd, NULL, 0, error_ptr); + result = smb_receive (server, command, sock_fd, NULL, 0, error_ptr); if (result < 0) goto out; @@ -772,6 +900,10 @@ smb_check_server_connection(struct smb_server *server, int error) if(close_connection) { + /* Stop means stop: EINTR is equivalent to Ctrl+C */ + if(error == EINTR) + server->dont_retry = TRUE; + server->state = CONN_INVALID; smb_invalidate_all_inodes (server); } @@ -782,7 +914,7 @@ smb_check_server_connection(struct smb_server *server, int error) * case of error. */ int -smb_request (struct smb_server *server, void * input_payload, const void * output_payload, int payload_size, int * error_ptr) +smb_request (struct smb_server *server, int command, void * input_payload, const void * output_payload, int payload_size, int * error_ptr) { unsigned char *buffer = server->transmit_buffer; int sock_fd = server->mount_data.fd; @@ -856,7 +988,7 @@ smb_request (struct smb_server *server, void * input_payload, const void * outpu } } - result = smb_receive (server, sock_fd, input_payload, payload_size, error_ptr); + result = smb_receive (server, command, sock_fd, input_payload, payload_size, error_ptr); out: @@ -872,7 +1004,7 @@ smb_request (struct smb_server *server, void * input_payload, const void * outpu * one packet to send. */ int -smb_trans2_request (struct smb_server *server, int *data_len, int *param_len, char **data, char **param, int * error_ptr) +smb_trans2_request (struct smb_server *server, int command, int *data_len, int *param_len, char **data, char **param, int * error_ptr) { unsigned char *buffer = server->transmit_buffer; int sock_fd = server->mount_data.fd; @@ -911,7 +1043,7 @@ smb_trans2_request (struct smb_server *server, int *data_len, int *param_len, ch goto out; } - result = smb_receive_trans2 (server, sock_fd, data_len, param_len, data, param, error_ptr); + result = smb_receive_trans2 (server, command, sock_fd, data_len, param_len, data, param, error_ptr); out: @@ -968,7 +1100,7 @@ smb_request_read_raw (struct smb_server *server, unsigned char *target, int max_ } /* Wait for the raw data to be sent by the server. */ - result = smb_receive_raw (server, sock_fd, target, max_len, NULL, 0, FALSE, error_ptr); + result = smb_receive_raw (server, SMBreadbraw, sock_fd, target, max_len, NULL, 0, FALSE, error_ptr); out: @@ -1037,7 +1169,7 @@ smb_request_write_raw (struct smb_server *server, unsigned const char *source, i /* Wait for the server to respond. */ if(!server->write_behind) { - result = smb_receive (server, sock_fd, NULL, 0, error_ptr); + result = smb_receive (server, SMBwritebraw, sock_fd, NULL, 0, error_ptr); if(result < 0) goto out; } diff --git a/source_code/system_headers.h b/source_code/system_headers.h old mode 100755 new mode 100644 index 5c0bfb9..7fc995f --- a/source_code/system_headers.h +++ b/source_code/system_headers.h @@ -79,6 +79,22 @@ /*****************************************************************************/ +/* A quick workaround for the timeval/timerequest->TimeVal/TimeRequest + change in the recent OS4 header files. */ +#if defined(__NEW_TIMEVAL_DEFINITION_USED__) + +#define timeval TimeVal +#define tv_secs Seconds +#define tv_micro Microseconds + +#define timerequest TimeRequest +#define tr_node Request +#define tr_time Time + +#endif /* __NEW_TIMEVAL_DEFINITION_USED__ */ + +/*****************************************************************************/ + #include #include #include @@ -90,6 +106,13 @@ /*****************************************************************************/ +/* This is for backwards compatibility only. */ +#if defined(__amigaos4__) +#define fib_EntryType fib_Obsolete +#endif /* __amigaos4__ */ + +/*****************************************************************************/ + #include #include #include