diff --git a/documentation/history.doc b/documentation/history.doc index afc4fd8..988737e 100644 --- a/documentation/history.doc +++ b/documentation/history.doc @@ -2779,3 +2779,20 @@ smbfs 2.9 (24.12.2018) - Added the TCP_NODELAY, SO_RCVBUF and SO_SNDBUF tuning options which serve the same purposes as their Samba configuration option counterparts. + + +smbfs 2.10 (29.12.2018) + +- The smb_receive_raw() function in "sock.c" again copies the NetBIOS + header into the receive buffer separately, rolling back an earlier + change which did not seem to be sound, and also served to make + complicated code even more complicated :-/ + +- Added the SCATTERGATHER tuning option which defaults to "no". Instead + of breaking down write operations into two separate send() calls (one + short, one very large), setting SCATTERGATHER=yes can call sendmsg() + instead which allows the entire operation to be completed in one + single step. This approach was suggested by Patrik Axelsson. Currently, + I am uncertain if all AmiTCP V3/V4 TCP/IP stacks implement sendmsg() + for TCP sockets in the same consistent manner, which is why this + feature is not enabled by default. diff --git a/source_code/include/smb/smb_fs_sb.h b/source_code/include/smb/smb_fs_sb.h index 891f16c..d3a7b40 100644 --- a/source_code/include/smb/smb_fs_sb.h +++ b/source_code/include/smb/smb_fs_sb.h @@ -33,6 +33,7 @@ struct smb_server both in a single combined send() operation rather than separately. */ dword smb_read_threshold; /* Same as above, but for recv() operations. */ + int scatter_gather; /* Use sendmsg() rather than send() where useful? */ int tcp_no_delay; /* Disable the Nagle algorithm for send()? */ int socket_receive_buffer_size; /* Desired socket receive buffer size, if > 0. */ int socket_send_buffer_size; /* Desired socket transmit buffer size, if > 0. */ diff --git a/source_code/main.c b/source_code/main.c index e8a994d..c4800ca 100644 --- a/source_code/main.c +++ b/source_code/main.c @@ -261,7 +261,7 @@ static ULONG stack_usage_exit(const struct StackSwapStruct * stk); static LONG CVSPrintf(const TEXT * format_string, APTR args); static int LocalVSNPrintf(STRPTR buffer, int limit, const TEXT * formatString, APTR args); static void cleanup(void); -static BOOL setup(const TEXT * program_name, const TEXT * service, const TEXT * workgroup, STRPTR username, STRPTR opt_password, BOOL opt_change_username_case, BOOL opt_change_password_case, const TEXT * opt_clientname, const TEXT * 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_unicode, BOOL opt_prefer_core_protocol, BOOL opt_session_setup_delay_unicode, BOOL opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, BOOL tcp_no_delay, int socket_receive_buffer_size, int socket_send_buffer_size, const TEXT * device_name, const TEXT * volume_name, BOOL add_volume, const TEXT * translation_file); +static BOOL setup(const TEXT * program_name, const TEXT * service, const TEXT * workgroup, STRPTR username, STRPTR opt_password, BOOL opt_change_username_case, BOOL opt_change_password_case, const TEXT * opt_clientname, const TEXT * 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_unicode, BOOL opt_prefer_core_protocol, BOOL opt_session_setup_delay_unicode, BOOL opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, BOOL scatter_gather, BOOL tcp_no_delay, int socket_receive_buffer_size, int socket_send_buffer_size, const TEXT * device_name, const TEXT * volume_name, BOOL add_volume, const TEXT * translation_file); static void file_system_handler(BOOL raise_priority, const TEXT * device_name, const TEXT * volume_name, const TEXT * service_name); /****************************************************************************/ @@ -760,7 +760,9 @@ main(void) SWITCH WriteBehind; NUMBER WriteThreshold; NUMBER ReadThreshold; + KEY ScatterGather; SWITCH TCPNoDelay; + KEY TCPDelay; NUMBER SocketReceiveBuf; NUMBER SocketSendBuf; KEY SessionSetup; @@ -805,9 +807,11 @@ main(void) "WRITEBEHIND/S," "WRITETHRESHOLD/N/K," "READTHRESHOLD/N/K," + "SCATTERGATHER/K," "TCP_NODELAY=TCPNODELAY/S," + "TCPDELAY/K," "SO_RCVBUF=SOCKETRECEIVEBUFFER/N/K," - "SO_SNDBUF=SOCKETSENDBUFER/N/K," + "SO_SNDBUF=SOCKETSENDBUFFER/N/K," "SESSIONSETUP/K," "UNICODE/K," "CP437/S," @@ -1001,6 +1005,10 @@ main(void) if(args.ChangePasswordCase == NULL && get_icon_tool_type_value("CHANGECASE", NULL) != NULL) args.ChangePasswordCase = "yes"; + args.TCPDelay = get_icon_tool_type_value("TCPDELAY", NULL); + if(args.TCPDelay == NULL && get_icon_tool_type_value("TCPNODELAY", "TCP_NODELAY") != NULL) + args.TCPDelay = "no"; + args.DisableExAll = get_icon_tool_type_value("DISABLEEXALL", NULL) != NULL; args.OmitHidden = get_icon_tool_type_value("OMITHIDDEN", NULL) != NULL; args.Quiet = get_icon_tool_type_value("QUIET", NULL) != NULL; @@ -1008,7 +1016,6 @@ main(void) args.CaseSensitive = get_icon_tool_type_value("CASE", "CASESENSITIVE") != NULL; args.NetBIOSTransport = get_icon_tool_type_value("NETBIOS", NULL) != NULL; args.WriteBehind = get_icon_tool_type_value("WRITEBEHIND", NULL) != NULL; - args.TCPNoDelay = get_icon_tool_type_value("TCPNODELAY", "TCP_NODELAY") != NULL; args.ClientName = get_icon_tool_type_value("CLIENT", "CLIENTNAME"); args.ServerName = get_icon_tool_type_value("SERVER", "SERVERNAME"); @@ -1054,6 +1061,8 @@ main(void) args.ReadThreshold = &smb_read_threshold; } + args.ScatterGather = get_icon_tool_type_value("SCATTERGATHER", NULL); + str = get_icon_tool_type_value("TZ","TIMEZONEOFFSET"); if(str != NULL) { @@ -1306,6 +1315,10 @@ main(void) if(args.ChangePasswordCase == NULL && args.ChangeCase) args.ChangePasswordCase = "yes"; + /* Disable the TCP delay? */ + if(args.TCPDelay == NULL && args.TCPNoDelay) + args.TCPDelay = "no"; + /* Use the default if no device or volume name is given. */ if(args.DeviceName == NULL && args.VolumeName == NULL) { @@ -1454,7 +1467,9 @@ main(void) D(("read threshold = %ld", (*args.ReadThreshold))); - D(("tcp no delay = %s", args.TCPNoDelay ? "requested" : "not requested")); + D(("scatter gather = %s", get_switch_status(args.ScatterGather, FALSE) ? "enabled" : "disabled")); + + D(("tcp delay = %s", get_switch_status(args.TCPDelay, FALSE) ? "enabled" : "disabled")); if(args.SocketReceiveBuf == NULL) args.SocketReceiveBuf = &socket_receive_buffer; @@ -1579,7 +1594,8 @@ main(void) args.WriteBehind, (*args.WriteThreshold), (*args.ReadThreshold), - args.TCPNoDelay, + get_switch_status(args.ScatterGather, FALSE), + get_switch_status(args.TCPDelay, FALSE), (*args.SocketReceiveBuf), (*args.SocketSendBuf), args.DeviceName, @@ -3221,21 +3237,40 @@ name_already_in_use(const TEXT * name) return(error); } -/* Check whether an Amiga file name uses special characters which - * should be avoided when used with the SMB file sharing protocol. +/* Check whether an AmigaDOS path name uses special characters which should be + * avoided when used with the SMB file sharing protocol. This test accepts '/' + * and ':' as valid characters that may appear in an AmigaDOS path name. + * + * Reserved characters are control codes in the range 0..31, as well as + * '<' (less than), '>' (greater than), ':' (colon), '"' (double quote), + * '/' (forward slash), '\' (backslash), '|' (vertical bar or pipe), + * '?' (question mark) and '*' (asterisk). The '?' and '*' are the MS-DOS + * wildcard pattern characters which the SMB delete and rename commands + * would process if they were part of the "file name". + * + * This list can be found (2018-12-28) here: + * https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file + * + * We also reject the use of "." and ".." as path names. */ static BOOL -name_contains_reserved_smb_characters(const TEXT * name) +path_name_is_invalid(const TEXT * name, int name_len) { BOOL result = TRUE; + TEXT c; + int i; /* Disallow "." and "..". */ - if(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) + if((name_len == 1 && name[0] == '.') || (name_len == 1 && name[0] == '.' && name[1] == '.')) goto out; - /* Disallow the use of the backslash in file names. */ - if(strchr(name,SMB_PATH_SEPARATOR) != NULL) - goto out; + for(i = 0 ; i < name_len ; i++) + { + c = name[i]; + + if(c < ' ' || strchr("<>\"\\|?*", c) != NULL) + goto out; + } result = FALSE; @@ -3427,7 +3462,7 @@ is_valid_device_name(const TEXT * name, int len) /****************************************************************************/ /* Check if a file name is right and proper for AmigaDOS use, - * which excludes the use unprintable characters, the path + * which excludes the use of unprintable characters, the path * delimiters ':' and '/', but also the SMB path delimeter * character '\'. * @@ -3809,6 +3844,7 @@ setup( BOOL opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, + BOOL opt_scatter_gather, BOOL opt_tcp_no_delay, int opt_socket_receive_buffer_size, int opt_socket_send_buffer_size, @@ -4053,6 +4089,7 @@ setup( opt_write_behind, opt_smb_request_write_threshold, opt_smb_request_read_threshold, + opt_scatter_gather, opt_tcp_no_delay, opt_socket_receive_buffer_size, opt_socket_send_buffer_size, @@ -4360,8 +4397,8 @@ truncate_64_bit_position(const QUAD * position_quad) * characters through the use of 'C' style escape sequences. Returns a * pointer to a local static buffer which contains the escaped string. * If the escape form of the name is too long to fit into the buffer, - * the text " [...]" will be appended to contents of the buffer, to indicate - * that the name was truncated. + * the text " [...]" will be appended to the contents of the buffer, to + * indicate that the name was truncated. */ TEXT * escape_name(const TEXT * name) @@ -5485,46 +5522,6 @@ file_is_invalid(const struct FileNode * fn,int * error_ptr) /****************************************************************************/ -/* Check if the name of a file or drawer contains MS-DOS - * wildcard characters ("?" and "*") which may not be - * suitable for some file system operations, e.g. rename - * or delete. - */ -static BOOL -name_contains_wildcard_characters(const TEXT * name) -{ - const TEXT * file_name; - BOOL result = FALSE; - int len; - TEXT c; - int i; - - ENTER(); - - SHOWSTRING(name); - - file_name = FilePart(name); - len = strlen(file_name); - - for(i = 0 ; i < len ; i++) - { - c = file_name[i]; - - if(c == '?' || c == '*') - { - D(("found a wildcard in '%s'",name)); - - result = TRUE; - break; - } - } - - RETURN(result); - return(result); -} - -/****************************************************************************/ - /* Try to obtain the path name stored in a FileLock which * a file or directory name is associated with. The parent * FileLock can be NULL, which is interpreted as being a @@ -5725,19 +5722,10 @@ Action_DeleteObject( */ name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); - /* The SMB_COM_DELETE command supports deleting sets - * of matching files/drawers through wildcards. Only - * the last part of the path (the name of the file - * or directory) may contain the wildcard. - */ - if(name_contains_wildcard_characters(name)) + if(path_name_is_invalid(name, name_len)) { - D(("name '%s' is not safe to use with delete operation", name)); - - /* Do not try to delete sets of matching files - * and drawers. We only came to delete a single - * directory entry. - */ + D(("'%s' is not a valid path name", name)); + error = ERROR_OBJECT_NOT_FOUND; goto out; } @@ -5917,14 +5905,10 @@ Action_CreateDir( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); - /* Do not allow for a directory to be created whose - * name contains MS-DOS wildcard characters. This will - * only end in tears later... - */ - if(name_contains_wildcard_characters(name)) + if(path_name_is_invalid(name, name_len)) { - D(("will not create a directory '%s' which contains wildcard characters", name)); - + D(("'%s' is not a valid path name", name)); + error = ERROR_INVALID_COMPONENT_NAME; goto out; } @@ -6049,6 +6033,14 @@ Action_LocateObject( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); + if(path_name_is_invalid(name, name_len)) + { + D(("'%s' is not a valid path name", name)); + + error = ERROR_OBJECT_NOT_FOUND; + goto out; + } + if(NOT ServerData->server.unicode_enabled) { error = translate_amiga_name_to_smb_name(name,name_len,sizeof(name)); @@ -6056,12 +6048,6 @@ Action_LocateObject( goto out; } - if(name_contains_reserved_smb_characters(FilePart(name))) - { - error = ERROR_OBJECT_NOT_FOUND; - goto out; - } - error = build_full_path_name(parent_name,name,name_len,&full_name); if(error != OK) goto out; @@ -6438,6 +6424,14 @@ Action_SetProtect( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); + if(path_name_is_invalid(name, name_len)) + { + D(("'%s' is not a valid path name", name)); + + error = ERROR_OBJECT_NOT_FOUND; + goto out; + } + if(NOT ServerData->server.unicode_enabled) { error = translate_amiga_name_to_smb_name(name,name_len,sizeof(name)); @@ -6595,18 +6589,10 @@ Action_RenameObject( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),source_bcpl_name); - /* The SMB_COM_RENAME command supports renaming through - * wildcards. Only the last part of the path (the name - * of the file or directory) may contain the wildcard. - */ - if(name_contains_wildcard_characters(name)) + if(path_name_is_invalid(name, name_len)) { - D(("found a wildcard in the source path '%s'; this is unsafe to use with the rename operation",name)); - - /* Do not rename/move sets of matching files and - * directories. We only came to rename/move a - * single directory entry. - */ + D(("'%s' is not a valid path name", name)); + error = ERROR_OBJECT_NOT_FOUND; goto out; } @@ -6638,14 +6624,10 @@ Action_RenameObject( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),destination_bcpl_name); - if(name_contains_wildcard_characters(name)) + if(path_name_is_invalid(name, name_len)) { - D(("found a wildcard in the destination path '%s'; this is unsafe to use with the rename operation",name)); - - /* Do not allow the destination name to contain - * MS-DOS wildcard characters. This will only end - * in tears later... - */ + D(("'%s' is not a valid path name", name)); + error = ERROR_INVALID_COMPONENT_NAME; goto out; } @@ -8138,7 +8120,6 @@ Action_Find( struct FileNode * fn = NULL; STRPTR parent_name; TEXT name[MAX_FILENAME_LEN+1]; - BOOL wildcard_characters_found_in_name = FALSE; int name_len; BOOL create_new_file = FALSE; STRPTR temp = NULL; @@ -8178,28 +8159,16 @@ Action_Find( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); - /* Do not allow MS-DOS wildcard characters to be used - * when creating a new file. - */ - if(action != ACTION_FINDINPUT) + if(path_name_is_invalid(name, name_len)) { - if(name_contains_wildcard_characters(name)) - { - if(action == ACTION_FINDOUTPUT) - { - D(("will not create a file with wildcard characters in its name")); + D(("'%s' is not a valid path name", name)); - error = ERROR_INVALID_COMPONENT_NAME; - goto out; - } + if(action == ACTION_FINDINPUT) + error = ERROR_OBJECT_NOT_FOUND; + else + error = ERROR_INVALID_COMPONENT_NAME; - /* We don't know yet if MODE_READWRITE will - * succeed in opening the file whose name - * contains wildcard characters. This will - * be checked later, if needed. - */ - wildcard_characters_found_in_name = TRUE; - } + goto out; } if(NOT ServerData->server.unicode_enabled) @@ -8209,12 +8178,6 @@ Action_Find( goto out; } - if(name_contains_reserved_smb_characters(FilePart(name))) - { - error = ERROR_OBJECT_NOT_FOUND; - goto out; - } - error = build_full_path_name(parent_name,name,name_len,&full_name); if(error != OK) goto out; @@ -8279,16 +8242,6 @@ Action_Find( goto out; } - /* Do not create a file whose name would contain - * MS-DOS wildcard characters. This will only - * end in tears later... - */ - if(wildcard_characters_found_in_name) - { - error = ERROR_INVALID_COMPONENT_NAME; - goto out; - } - error = split_path_name(full_name,strlen(full_name),&temp,&dir_name,&base_name); if(error != OK) { @@ -8646,10 +8599,38 @@ Action_Seek( default: + D(("seek mode %ld not known", mode)); + error = ERROR_ACTION_NOT_KNOWN; goto out; } + #if DEBUG + { + const TEXT * mode_name; + + switch(mode) + { + case OFFSET_BEGINNING: + + mode_name = "OFFSET_BEGINNING"; + break; + + case OFFSET_CURRENT: + + mode_name = "OFFSET_CURRENT"; + break; + + default: + + mode_name = "OFFSET_END"; + break; + } + + D(("Seek(..., %ld, %s); current position = %s", position, mode_name, convert_quad_to_string(&reference_position_quad))); + } + #endif /* DEBUG */ + if(position < 0) { QUAD position_quad; @@ -8660,6 +8641,8 @@ Action_Seek( /* We cannot seek back beyond the beginning of the file. */ if(compare_64_to_64(&reference_position_quad,&position_quad) < 0) { + D(("cannot seek back beyond the beginning of the file.")); + error = ERROR_SEEK_ERROR; goto out; } @@ -8671,6 +8654,8 @@ Action_Seek( /* Careful, we need to check for overflow, too. */ if(add_64_plus_32_to_64(&reference_position_quad,position,&new_position_quad) > 0) { + D(("position is too large")); + error = ERROR_SEEK_ERROR; goto out; } @@ -8682,6 +8667,8 @@ Action_Seek( result = truncate_64_bit_position(&previous_position_quad); + D(("new position = %s; returning %ld", convert_quad_to_string(&new_position_quad), result)); + out: (*error_ptr) = error; @@ -8852,6 +8839,14 @@ Action_SetDate( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); + if(path_name_is_invalid(name, name_len)) + { + D(("'%s' is not a valid path name", name)); + + error = ERROR_OBJECT_NOT_FOUND; + goto out; + } + if(NOT ServerData->server.unicode_enabled) { error = translate_amiga_name_to_smb_name(name,name_len,sizeof(name)); @@ -9712,6 +9707,14 @@ Action_SetComment( name_len = convert_from_bcpl_to_c_string(name,sizeof(name),bcpl_name); + if(path_name_is_invalid(name, name_len)) + { + D(("'%s' is not a valid path name", name)); + + error = ERROR_OBJECT_NOT_FOUND; + goto out; + } + if(NOT ServerData->server.unicode_enabled) { error = translate_amiga_name_to_smb_name(name,name_len,sizeof(name)); diff --git a/source_code/smb_abstraction.c b/source_code/smb_abstraction.c index d138263..2031f80 100644 --- a/source_code/smb_abstraction.c +++ b/source_code/smb_abstraction.c @@ -68,12 +68,12 @@ init_open_file_list(smba_server_t *res) NewList((struct List *)&res->open_files); #ifdef USE_SPLAY_TREE + { + splay_tree_init(&res->open_file_address_tree, (splay_key_compare_t)compare_files_by_address); - splay_tree_init(&res->open_file_address_tree, (splay_key_compare_t)compare_files_by_address); - - splay_tree_init(&res->open_file_name_tree, (splay_key_compare_t)compare_names); - res->open_file_name_tree.st_allow_duplicates = TRUE; - + splay_tree_init(&res->open_file_name_tree, (splay_key_compare_t)compare_names); + res->open_file_name_tree.st_allow_duplicates = TRUE; + } #endif /* USE_SPLAY_TREE */ } @@ -97,6 +97,7 @@ smba_connect ( int opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, + int opt_scatter_gather, int opt_tcp_no_delay, int opt_socket_receive_buffer_size, int opt_socket_send_buffer_size, @@ -173,6 +174,11 @@ smba_connect ( LOG(("SMB read request threshold size = %ld bytes\n", res->server.smb_read_threshold)); + /* Use sendmsg() instead of send() where useful? */ + res->server.scatter_gather = opt_scatter_gather; + + LOG(("use sendmsg() instead of send() where useful = %s\n",opt_scatter_gather ? "yes" : "no")); + /* Disable the Nagle algorithm, causing send() to immediately * result in the data being transmitted? */ @@ -506,15 +512,15 @@ add_smba_file(smba_server_t * s, smba_file_t *f) AddTail ((struct List *)&s->open_files, (struct Node *)f); #ifdef USE_SPLAY_TREE + { + f->splay_address_node.sn_key = f; + f->splay_address_node.sn_userdata = f; + splay_tree_add(&s->open_file_address_tree, &f->splay_address_node); - f->splay_address_node.sn_key = f; - f->splay_address_node.sn_userdata = f; - splay_tree_add(&s->open_file_address_tree, &f->splay_address_node); - - f->splay_name_node.sn_key = f->dirent.complete_path; - f->splay_name_node.sn_userdata = f; - splay_tree_add(&s->open_file_name_tree, &f->splay_name_node); - + f->splay_name_node.sn_key = f->dirent.complete_path; + f->splay_name_node.sn_userdata = f; + splay_tree_add(&s->open_file_name_tree, &f->splay_name_node); + } #endif /* USE_SPLAY_TREE */ } @@ -526,10 +532,10 @@ remove_smba_file(smba_server_t * s, smba_file_t *f) Remove((struct Node *)f); #ifdef USE_SPLAY_TREE - - splay_tree_remove(&s->open_file_address_tree, NULL, (splay_key_t)f); - splay_tree_remove(&s->open_file_name_tree, &f->splay_name_node, f->splay_name_node.sn_key); - + { + splay_tree_remove(&s->open_file_address_tree, NULL, (splay_key_t)f); + splay_tree_remove(&s->open_file_name_tree, &f->splay_name_node, f->splay_name_node.sn_key); + } #endif /* USE_SPLAY_TREE */ } @@ -687,26 +693,26 @@ file_is_valid(smba_server_t * s, const smba_file_t * f) int is_valid; #ifndef USE_SPLAY_TREE - - const smba_file_t * file; - - is_valid = FALSE; - - for (file = (smba_file_t *)s->open_files.mlh_Head ; - file->node.mln_Succ != NULL ; - file = (smba_file_t *)file->node.mln_Succ) { - if (file == f) + const smba_file_t * file; + + is_valid = FALSE; + + for (file = (smba_file_t *)s->open_files.mlh_Head ; + file->node.mln_Succ != NULL ; + file = (smba_file_t *)file->node.mln_Succ) { - is_valid = TRUE; - break; + if (file == f) + { + is_valid = TRUE; + break; + } } } - #else - - is_valid = (splay_tree_find(&s->open_file_address_tree, (splay_key_t)f) != NULL); - + { + is_valid = (splay_tree_find(&s->open_file_address_tree, (splay_key_t)f) != NULL); + } #endif /* USE_SPLAY_TREE */ return(is_valid); @@ -1660,37 +1666,12 @@ close_path (smba_server_t * s, const char *path, int * error_ptr) smba_file_t *p; #ifndef USE_SPLAY_TREE - - for (p = (smba_file_t *)s->open_files.mlh_Head; - p->node.mln_Succ != NULL; - p = (smba_file_t *)p->node.mln_Succ) { - if (p->is_valid && compare_names(p->dirent.complete_path, path) == SAME) + for (p = (smba_file_t *)s->open_files.mlh_Head; + p->node.mln_Succ != NULL; + p = (smba_file_t *)p->node.mln_Succ) { - result = invalidate_smba_file(s, p, path, error_ptr); - if(result < 0) - break; - } - } - - #else - - struct splay_node * sn; - - /* Find all files which match the same path name. */ - sn = splay_tree_find(&s->open_file_name_tree, (splay_key_t)path); - if(sn != NULL) - { - /* Walk through all the files, marking them as no longer - * valid and closing them, if necessary. Note that we - * do not remove them from the list of open files, we just - * mark them for reopening later, if needed. - */ - for((void)NULL ; sn != NULL ; sn = sn->sn_next) - { - p = sn->sn_userdata; - - if (p->is_valid) + if (p->is_valid && compare_names(p->dirent.complete_path, path) == SAME) { result = invalidate_smba_file(s, p, path, error_ptr); if(result < 0) @@ -1698,7 +1679,32 @@ close_path (smba_server_t * s, const char *path, int * error_ptr) } } } + #else + { + struct splay_node * sn; + /* Find all files which match the same path name. */ + sn = splay_tree_find(&s->open_file_name_tree, (splay_key_t)path); + if(sn != NULL) + { + /* Walk through all the files, marking them as no longer + * valid and closing them, if necessary. Note that we + * do not remove them from the list of open files, we just + * mark them for reopening later, if needed. + */ + for((void)NULL ; sn != NULL ; sn = sn->sn_next) + { + p = sn->sn_userdata; + + if (p->is_valid) + { + result = invalidate_smba_file(s, p, path, error_ptr); + if(result < 0) + break; + } + } + } + } #endif /* USE_SPLAY_TREE */ return(result); @@ -2032,6 +2038,7 @@ smba_start( int opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, + int opt_scatter_gather, int opt_tcp_no_delay, int opt_socket_receive_buffer_size, int opt_socket_send_buffer_size, @@ -2288,6 +2295,7 @@ smba_start( opt_write_behind, opt_smb_request_write_threshold, opt_smb_request_read_threshold, + opt_scatter_gather, opt_tcp_no_delay, opt_socket_receive_buffer_size, opt_socket_send_buffer_size, diff --git a/source_code/smb_abstraction.h b/source_code/smb_abstraction.h index 0463bf0..f39b142 100644 --- a/source_code/smb_abstraction.h +++ b/source_code/smb_abstraction.h @@ -162,7 +162,7 @@ int smba_rmdir(smba_server_t *s, const char *path, int *error_ptr); int smba_rename(smba_server_t *s, const char *from, const 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(const char *service, const char *opt_workgroup, const char *opt_username, const char *opt_password, const char *opt_clientname, const char *opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, int opt_raw_smb, int opt_unicode, int opt_prefer_core_protocol, int opt_case_sensitive, int opt_session_setup_delay_unicode, int opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, int opt_tcp_no_delay, int opt_socket_receive_buffer_size, int opt_socket_send_buffer_size, int *error_ptr, int *smb_error_class_ptr, int *smb_error_ptr, smba_connect_parameters_t *smba_connect_par, smba_server_t **smba_server_ptr); +int smba_start(const char *service, const char *opt_workgroup, const char *opt_username, const char *opt_password, const char *opt_clientname, const char *opt_servername, int opt_cachesize, int opt_max_transmit, int opt_timeout, int opt_raw_smb, int opt_unicode, int opt_prefer_core_protocol, int opt_case_sensitive, int opt_session_setup_delay_unicode, int opt_write_behind, int opt_smb_request_write_threshold, int opt_smb_request_read_threshold, int opt_scatter_gather, int opt_tcp_no_delay, int opt_socket_receive_buffer_size, int opt_socket_send_buffer_size, int *error_ptr, int *smb_error_class_ptr, int *smb_error_ptr, smba_connect_parameters_t *smba_connect_par, smba_server_t **smba_server_ptr); 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 245492c..cfa40c8 100644 --- a/source_code/smbfs_rev.h +++ b/source_code/smbfs_rev.h @@ -1,6 +1,6 @@ #define VERSION 2 -#define REVISION 9 -#define DATE "24.12.2018" -#define VERS "smbfs 2.9" -#define VSTRING "smbfs 2.9 (24.12.2018)\r\n" -#define VERSTAG "\0$VER: smbfs 2.9 (24.12.2018)" +#define REVISION 10 +#define DATE "29.12.2018" +#define VERS "smbfs 2.10" +#define VSTRING "smbfs 2.10 (29.12.2018)\r\n" +#define VERSTAG "\0$VER: smbfs 2.10 (29.12.2018)" diff --git a/source_code/smbfs_rev.rev b/source_code/smbfs_rev.rev index ec63514..f599e28 100644 --- a/source_code/smbfs_rev.rev +++ b/source_code/smbfs_rev.rev @@ -1 +1 @@ -9 +10 diff --git a/source_code/sock.c b/source_code/sock.c index 6f17aef..692e467 100644 --- a/source_code/sock.c +++ b/source_code/sock.c @@ -202,131 +202,138 @@ smb_receive_raw ( int want_header, int * error_ptr) { - unsigned char netbios_session_buf[256]; - unsigned char * netbios_session_data; + unsigned char netbios_session_buf[NETBIOS_HEADER_SIZE]; int netbios_session_payload_size; int len, result; ASSERT( error_ptr != NULL ); - server->rcls = 0; - server->err = 0; - - /* If the NetBIOS header needs to be returned, receive it along - * with the SMB message and whatever follows. + /* We need to read the NetBIOS session header before we can move + * on and read the SMB data. Because the NetBIOS session header + * may be a keepalive message or something else we can safely + * ignore, we will retry reading the header until we get to the + * point where it is safe to read the SMB data. */ - if (want_header) + while(TRUE) { - LOG(("NetBIOS header will be returned as part of the buffer\n")); + server->rcls = 0; + server->err = 0; - netbios_session_data = target; - } - /* Otherwise store the NetBIOS header in a separate buffer. */ - else - { - LOG(("NetBIOS header will be processed separately\n")); - - netbios_session_data = netbios_session_buf; - } - - re_recv: - - /* Read the NetBIOS session header (rfc-1002, section 4.3.1) */ - result = receive_all (sock_fd, netbios_session_data, NETBIOS_HEADER_SIZE, error_ptr); - if (result < 0) - { - LOG (("recv error = %ld\n", (*error_ptr))); - goto out; - } - - if (result < NETBIOS_HEADER_SIZE) - { - LOG (("expected %ld bytes, got %ld\n", NETBIOS_HEADER_SIZE, result)); - - (*error_ptr) = error_end_of_file; - - result = -1; - goto out; - } - - netbios_session_payload_size = (int)smb_len(netbios_session_data); - SHOWVALUE(netbios_session_payload_size); - - #if defined(DUMP_SMB) - { - if(command != 0 && netbios_session_data[0] != 0x00 && netbios_session_payload_size > 0) + /* Read the NetBIOS session header (rfc-1002, section 4.3.1) */ + result = receive_all (sock_fd, netbios_session_buf, NETBIOS_HEADER_SIZE, error_ptr); + if (result < 0) { - /* We only want to show what's in the first few - * bytes of a session packet. Since we only support - * two session packet types (session message and - * session keep alive) we will abort processing - * anyway so it doesn't matter if we ignore any - * data beyond the first 256 bytes. - */ - if(netbios_session_payload_size > 256 - NETBIOS_HEADER_SIZE) - netbios_session_payload_size = 256 - NETBIOS_HEADER_SIZE; - - result = receive_all (sock_fd, &netbios_session_data[NETBIOS_HEADER_SIZE], netbios_session_payload_size - NETBIOS_HEADER_SIZE, error_ptr); - if (result < 0) - { - LOG (("recv error = %ld\n", (*error_ptr))); - goto out; - } - - if(result < netbios_session_payload_size - NETBIOS_HEADER_SIZE) - { - LOG (("result (%ld) < %ld\n", result, netbios_session_payload_size - NETBIOS_HEADER_SIZE)); - - (*error_ptr) = error_end_of_file; - - result = -1; - goto out; - } - - dump_netbios_header(__FILE__,__LINE__,netbios_session_data,&netbios_session_data[NETBIOS_HEADER_SIZE],netbios_session_payload_size); + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; } - else + + if (result < NETBIOS_HEADER_SIZE) { - dump_netbios_header(__FILE__,__LINE__,netbios_session_data,NULL,0); + LOG (("expected %ld bytes, got %ld\n", NETBIOS_HEADER_SIZE, result)); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; } - } - #endif /* defined(DUMP_SMB) */ - /* Check the session type. */ - switch (netbios_session_data[0]) - { - /* 0x00 == session message */ - case 0x00: + netbios_session_payload_size = (int)smb_len(netbios_session_buf); + SHOWVALUE(netbios_session_payload_size); - break; + #if defined(DUMP_SMB) + { + if(command != 0 && netbios_session_buf[0] != 0x00 && netbios_session_payload_size > 0) + { + unsigned char netbios_session_payload[256]; - /* 0x85 == session keepalive */ - case 0x85: + /* We only want to show what's in the first few + * bytes of a session packet. Since we only support + * two session packet types (session message and + * session keep alive) we will abort processing + * anyway so it doesn't matter if we ignore any + * data beyond the first 256 bytes. + */ + if(netbios_session_payload_size > 256) + netbios_session_payload_size = 256; + result = receive_all (sock_fd, netbios_session_payload, netbios_session_payload_size, error_ptr); + if (result < 0) + { + LOG (("recv error = %ld\n", (*error_ptr))); + goto out; + } + + if(result < netbios_session_payload_size) + { + LOG (("result (%ld) < %ld\n", result, netbios_session_payload_size)); + + (*error_ptr) = error_end_of_file; + + result = -1; + goto out; + } + + dump_netbios_header(__FILE__,__LINE__,netbios_session_buf,netbios_session_payload,netbios_session_payload_size); + } + else + { + dump_netbios_header(__FILE__,__LINE__,netbios_session_buf,NULL,0); + } + } + #endif /* defined(DUMP_SMB) */ + + /* Is this a session keepalive message? If so, + * read the next frame. + */ + if (netbios_session_buf[0] == 0x85) + { LOG (("Got SESSION KEEP ALIVE\n")); - goto re_recv; - - /* 0x81 == session request */ - /* 0x82 == positive session response */ - /* 0x83 == negative session response */ - /* 0x84 == retarget session response */ - default: - - /* 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_data[0])); - - (*error_ptr) = error_invalid_netbios_session; - - result = -1; - goto out; - } - + continue; + } + /* Is this a regular session message? This is what + * we came for. + */ + else if (netbios_session_buf[0] == 0x00) + { break; + } + + /* Check the session type again, looking for + * anything peculiar. + */ + switch (netbios_session_buf[0]) + { + /* 0x00 == session message */ + case 0x00: + + /* This is what we came for. */ + break; + + /* 0x81 == session request */ + /* 0x82 == positive session response */ + /* 0x83 == negative session response */ + /* 0x84 == retarget session response */ + default: + + /* 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; + + result = -1; + goto out; + } + + /* Ignore this message type. */ + break; + } + + break; } /* The length in the NetBIOS header is the raw data length (17 bits) */ @@ -341,11 +348,12 @@ smb_receive_raw ( goto out; } - /* Did we read the NetBIOS header already? Make sure that - * the data to follow goes into the right receive buffer. - */ + /* Prepend the NetBIOS header to what is read? */ if (want_header) + { + memcpy (target, netbios_session_buf, NETBIOS_HEADER_SIZE); target += NETBIOS_HEADER_SIZE; + } /* 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 @@ -414,7 +422,7 @@ smb_receive_raw ( LOG (("EOF\n")); (*error_ptr) = error_end_of_file; - + result = -1; goto out; } @@ -442,7 +450,7 @@ smb_receive_raw ( LOG (("EOF\n")); (*error_ptr) = error_end_of_file; - + result = -1; goto out; } @@ -543,7 +551,7 @@ smb_receive_raw ( LOG (("EOF\n")); (*error_ptr) = error_end_of_file; - + result = -1; goto out; } @@ -612,7 +620,7 @@ smb_receive_raw ( 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)); + 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); } @@ -760,7 +768,7 @@ smb_receive_raw ( 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)); + 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); } @@ -1405,22 +1413,71 @@ smb_request ( dump_smb(__FILE__,__LINE__,0,buffer+NETBIOS_HEADER_SIZE,len-NETBIOS_HEADER_SIZE,smb_packet_from_consumer,server->max_recv); #endif /* defined(DUMP_SMB) */ - result = send (sock_fd, (void *) buffer, len, 0); - if (result < 0) - { - LOG(("send() for %ld bytes failed (errno=%ld)\n", len, errno)); - - (*error_ptr) = errno; - - goto out; - } - if(output_payload != NULL && payload_size > 0) { - result = send (sock_fd, (void *)output_payload, payload_size, 0); + /* Use two send() calls for header and payload? */ + if(!server->scatter_gather) + { + LOG(("using two send() calls\n")); + + result = send (sock_fd, (void *) buffer, len, 0); + if (result < 0) + { + LOG(("send() for %ld bytes failed (errno=%ld)\n", len, errno)); + + (*error_ptr) = errno; + + goto out; + } + + result = send (sock_fd, (void *)output_payload, payload_size, 0); + if (result < 0) + { + LOG(("payload send() for %ld bytes failed (errno=%ld)\n", payload_size, errno)); + + (*error_ptr) = errno; + + goto out; + } + } + /* No, use sendmsg() to transmit both header and payload + * in one single step. + */ + else + { + struct msghdr msg; + struct iovec iov[2]; + + LOG(("using sendmsg() for %ld+%ld = %ld bytes\n", len, payload_size, len+payload_size)); + + memset(&msg,0,sizeof(msg)); + + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + iov[0].iov_base = buffer; + iov[0].iov_len = len; + + iov[1].iov_base = (void *)output_payload; + iov[1].iov_len = payload_size; + + result = sendmsg (sock_fd, &msg, 0); + if (result < 0) + { + LOG(("sendmsg() for %ld+%ld bytes failed (errno=%ld)\n", len, payload_size, errno)); + + (*error_ptr) = errno; + + goto out; + } + } + } + else + { + result = send (sock_fd, (void *) buffer, len, 0); if (result < 0) { - LOG(("payload send() for %ld bytes failed (errno=%ld)\n", payload_size, errno)); + LOG(("send() for %ld bytes failed (errno=%ld)\n", len, errno)); (*error_ptr) = errno; @@ -1581,36 +1638,73 @@ smb_request_write_raw (struct smb_server *server, unsigned const char *source, i ASSERT( length <= 65535 ); - /* Send the NetBIOS header. */ + /* Prepare the NetBIOS header, which in this case is + * providing the length of the data to follow it. + */ smb_encode_smb_length (nb_header, length); #if defined(DUMP_SMB) dump_netbios_header(__FILE__,__LINE__,nb_header,NULL,0); - #endif /* defined(DUMP_SMB) */ - - result = send (sock_fd, nb_header, NETBIOS_HEADER_SIZE, 0); - if(result < 0) - { - LOG(("send() for %ld bytes failed (errno=%ld)\n", NETBIOS_HEADER_SIZE, errno)); - - (*error_ptr) = errno; - - goto out; - } - - #if defined(DUMP_SMB) dump_smb(__FILE__,__LINE__,0,source,length,smb_packet_from_consumer,server->max_recv); #endif /* defined(DUMP_SMB) */ - /* Now send the data to be written. */ - result = send (sock_fd, (void *)source, length, 0); - if(result < 0) + /* Use two send() calls to transmit header and data? */ + if(!server->scatter_gather) { - LOG(("send() for %ld bytes failed (errno=%ld)\n", length, errno)); + LOG(("using two send() calls\n")); - (*error_ptr) = errno; + /* Send the NetBIOS header. */ + result = send (sock_fd, nb_header, NETBIOS_HEADER_SIZE, 0); + if(result < 0) + { + LOG(("send() for %ld bytes failed (errno=%ld)\n", NETBIOS_HEADER_SIZE, errno)); - goto out; + (*error_ptr) = errno; + + goto out; + } + + /* Now send the data to be written. */ + result = send (sock_fd, (void *)source, length, 0); + if(result < 0) + { + LOG(("send() for %ld bytes failed (errno=%ld)\n", length, errno)); + + (*error_ptr) = errno; + + goto out; + } + } + /* No, use sendmsg() to transmit header and data in + * one single step. + */ + else + { + struct msghdr msg; + struct iovec iov[2]; + + LOG(("using sendmsg() for %ld+%ld = %ld bytes\n", NETBIOS_HEADER_SIZE, length, NETBIOS_HEADER_SIZE+length)); + + memset(&msg,0,sizeof(msg)); + + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + iov[0].iov_base = nb_header; + iov[0].iov_len = NETBIOS_HEADER_SIZE; + + iov[1].iov_base = (void *)source; + iov[1].iov_len = length; + + result = sendmsg (sock_fd, &msg, 0); + if (result < 0) + { + LOG(("sendmsg() for %ld+%ld bytes failed (errno=%ld)\n", NETBIOS_HEADER_SIZE, length, errno)); + + (*error_ptr) = errno; + + goto out; + } } /* Wait for the server to respond. */