/* Unix SMB/CIFS implementation. In-memory cache Copyright (C) Volker Lendecke 2007 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "torture/proto.h" #include "libsmb/libsmb.h" #include "libsmb/clirap.h" #include "../lib/util/tevent_ntstatus.h" static long long int ival(const char *str) { return strtoll(str, NULL, 0); } struct nbench_state { struct tevent_context *ev; struct cli_state *cli; const char *cliname; FILE *loadfile; struct ftable *ftable; void (*bw_report)(size_t nread, size_t nwritten, void *private_data); void *bw_report_private; }; struct lock_info { struct lock_info *next, *prev; off_t offset; int size; }; struct createx_params { char *fname; unsigned int cr_options; unsigned int cr_disposition; int handle; }; struct ftable { struct ftable *next, *prev; struct createx_params cp; struct lock_info *locks; uint16_t fnum; /* the fd that we got back from the server */ }; enum nbench_cmd { NBENCH_CMD_NTCREATEX, NBENCH_CMD_CLOSE, NBENCH_CMD_RENAME, NBENCH_CMD_UNLINK, NBENCH_CMD_DELTREE, NBENCH_CMD_RMDIR, NBENCH_CMD_MKDIR, NBENCH_CMD_QUERY_PATH_INFORMATION, NBENCH_CMD_QUERY_FILE_INFORMATION, NBENCH_CMD_QUERY_FS_INFORMATION, NBENCH_CMD_SET_FILE_INFORMATION, NBENCH_CMD_FIND_FIRST, NBENCH_CMD_WRITEX, NBENCH_CMD_WRITE, NBENCH_CMD_LOCKX, NBENCH_CMD_UNLOCKX, NBENCH_CMD_READX, NBENCH_CMD_FLUSH, NBENCH_CMD_SLEEP, }; struct nbench_cmd_struct { char **params; int num_params; NTSTATUS status; enum nbench_cmd cmd; }; static struct nbench_cmd_struct *nbench_parse(TALLOC_CTX *mem_ctx, const char *line) { struct nbench_cmd_struct *result; char *cmd; char *status; result = talloc(mem_ctx, struct nbench_cmd_struct); if (result == NULL) { return NULL; } result->params = str_list_make_shell(mem_ctx, line, " "); if (result->params == NULL) { goto fail; } result->num_params = talloc_array_length(result->params) - 1; if (result->num_params < 2) { goto fail; } status = result->params[result->num_params-1]; if (strncmp(status, "NT_STATUS_", 10) != 0 && strncmp(status, "0x", 2) != 0) { goto fail; } /* accept numeric or string status codes */ if (strncmp(status, "0x", 2) == 0) { result->status = NT_STATUS(strtoul(status, NULL, 16)); } else { result->status = nt_status_string_to_code(status); } cmd = result->params[0]; if (!strcmp(cmd, "NTCreateX")) { result->cmd = NBENCH_CMD_NTCREATEX; } else if (!strcmp(cmd, "Close")) { result->cmd = NBENCH_CMD_CLOSE; } else if (!strcmp(cmd, "Rename")) { result->cmd = NBENCH_CMD_RENAME; } else if (!strcmp(cmd, "Unlink")) { result->cmd = NBENCH_CMD_UNLINK; } else if (!strcmp(cmd, "Deltree")) { result->cmd = NBENCH_CMD_DELTREE; } else if (!strcmp(cmd, "Rmdir")) { result->cmd = NBENCH_CMD_RMDIR; } else if (!strcmp(cmd, "Mkdir")) { result->cmd = NBENCH_CMD_MKDIR; } else if (!strcmp(cmd, "QUERY_PATH_INFORMATION")) { result->cmd = NBENCH_CMD_QUERY_PATH_INFORMATION; } else if (!strcmp(cmd, "QUERY_FILE_INFORMATION")) { result->cmd = NBENCH_CMD_QUERY_FILE_INFORMATION; } else if (!strcmp(cmd, "QUERY_FS_INFORMATION")) { result->cmd = NBENCH_CMD_QUERY_FS_INFORMATION; } else if (!strcmp(cmd, "SET_FILE_INFORMATION")) { result->cmd = NBENCH_CMD_SET_FILE_INFORMATION; } else if (!strcmp(cmd, "FIND_FIRST")) { result->cmd = NBENCH_CMD_FIND_FIRST; } else if (!strcmp(cmd, "WriteX")) { result->cmd = NBENCH_CMD_WRITEX; } else if (!strcmp(cmd, "Write")) { result->cmd = NBENCH_CMD_WRITE; } else if (!strcmp(cmd, "LockX")) { result->cmd = NBENCH_CMD_LOCKX; } else if (!strcmp(cmd, "UnlockX")) { result->cmd = NBENCH_CMD_UNLOCKX; } else if (!strcmp(cmd, "ReadX")) { result->cmd = NBENCH_CMD_READX; } else if (!strcmp(cmd, "Flush")) { result->cmd = NBENCH_CMD_FLUSH; } else if (!strcmp(cmd, "Sleep")) { result->cmd = NBENCH_CMD_SLEEP; } else { goto fail; } return result; fail: TALLOC_FREE(result); return NULL; } static struct ftable *ft_find(struct ftable *ftlist, int handle) { while (ftlist != NULL) { if (ftlist->cp.handle == handle) { return ftlist; } ftlist = ftlist->next; } return NULL; } struct nbench_cmd_state { struct tevent_context *ev; struct nbench_state *state; struct nbench_cmd_struct *cmd; struct ftable *ft; bool eof; }; static void nbench_cmd_done(struct tevent_req *subreq); static struct tevent_req *nbench_cmd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct nbench_state *nb_state) { struct tevent_req *req, *subreq; struct nbench_cmd_state *state; char line[1024]; size_t len; req = tevent_req_create(mem_ctx, &state, struct nbench_cmd_state); if (req == NULL) { return NULL; } state->ev = ev; state->state = nb_state; if (fgets(line, sizeof(line), nb_state->loadfile) == NULL) { tevent_req_nterror(req, NT_STATUS_END_OF_FILE); return tevent_req_post(req, ev); } len = strlen(line); if (len == 0) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); return tevent_req_post(req, ev); } if (line[len-1] == '\n') { line[len-1] = '\0'; } state->cmd = nbench_parse(state, line); if (state->cmd == NULL) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); return tevent_req_post(req, ev); } switch (state->cmd->cmd) { case NBENCH_CMD_NTCREATEX: { uint32_t desired_access; uint32_t share_mode; unsigned int flags = 0; state->ft = talloc(state, struct ftable); if (tevent_req_nomem(state->ft, req)) { return tevent_req_post(req, ev); } state->ft->cp.fname = talloc_all_string_sub( state->ft, state->cmd->params[1], "client1", nb_state->cliname); if (tevent_req_nomem(state->ft->cp.fname, req)) { return tevent_req_post(req, ev); } state->ft->cp.cr_options = ival(state->cmd->params[2]); state->ft->cp.cr_disposition = ival(state->cmd->params[3]); state->ft->cp.handle = ival(state->cmd->params[4]); if (state->ft->cp.cr_options & FILE_DIRECTORY_FILE) { desired_access = SEC_FILE_READ_DATA; } else { desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_READ_ATTRIBUTE | SEC_FILE_WRITE_ATTRIBUTE; flags = EXTENDED_RESPONSE_REQUIRED | REQUEST_OPLOCK | REQUEST_BATCH_OPLOCK; } share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; subreq = cli_ntcreate_send( state, ev, nb_state->cli, state->ft->cp.fname, flags, desired_access, 0, share_mode, state->ft->cp.cr_disposition, state->ft->cp.cr_options, SMB2_IMPERSONATION_IMPERSONATION, 0); break; } case NBENCH_CMD_CLOSE: { state->ft = ft_find(state->state->ftable, ival(state->cmd->params[1])); if (state->ft == NULL) { tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); return tevent_req_post(req, ev); } subreq = cli_close_send(state, ev, nb_state->cli, state->ft->fnum, 0); break; } case NBENCH_CMD_MKDIR: { char *fname; fname = talloc_all_string_sub( state, state->cmd->params[1], "client1", nb_state->cliname); if (tevent_req_nomem(state->ft->cp.fname, req)) { return tevent_req_post(req, ev); } subreq = cli_mkdir_send(state, ev, nb_state->cli, fname); break; } case NBENCH_CMD_QUERY_PATH_INFORMATION: { char *fname; fname = talloc_all_string_sub( state, state->cmd->params[1], "client1", nb_state->cliname); if (tevent_req_nomem(state->ft->cp.fname, req)) { return tevent_req_post(req, ev); } subreq = cli_qpathinfo_send(state, ev, nb_state->cli, fname, ival(state->cmd->params[2]), 0, CLI_BUFFER_SIZE); break; } default: tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED); return tevent_req_post(req, ev); } if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, nbench_cmd_done, req); return req; } static bool status_wrong(struct tevent_req *req, NTSTATUS expected, NTSTATUS status) { if (NT_STATUS_EQUAL(expected, status)) { return false; } if (NT_STATUS_IS_OK(status)) { status = NT_STATUS_INVALID_NETWORK_RESPONSE; } tevent_req_nterror(req, status); return true; } static void nbench_cmd_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); struct nbench_cmd_state *state = tevent_req_data( req, struct nbench_cmd_state); struct nbench_state *nbstate = state->state; NTSTATUS status; switch (state->cmd->cmd) { case NBENCH_CMD_NTCREATEX: { struct ftable *ft; status = cli_ntcreate_recv(subreq, &state->ft->fnum, NULL); TALLOC_FREE(subreq); if (status_wrong(req, state->cmd->status, status)) { return; } if (!NT_STATUS_IS_OK(status)) { tevent_req_done(req); return; } ft = talloc_move(nbstate, &state->ft); DLIST_ADD(nbstate->ftable, ft); break; } case NBENCH_CMD_CLOSE: { status = cli_close_recv(subreq); TALLOC_FREE(subreq); if (status_wrong(req, state->cmd->status, status)) { return; } DLIST_REMOVE(state->state->ftable, state->ft); TALLOC_FREE(state->ft); break; } case NBENCH_CMD_MKDIR: { status = cli_mkdir_recv(subreq); TALLOC_FREE(subreq); if (status_wrong(req, state->cmd->status, status)) { return; } break; } case NBENCH_CMD_QUERY_PATH_INFORMATION: { status = cli_qpathinfo_recv(subreq, NULL, NULL, NULL); TALLOC_FREE(subreq); if (status_wrong(req, state->cmd->status, status)) { return; } break; } default: break; } tevent_req_done(req); } static NTSTATUS nbench_cmd_recv(struct tevent_req *req) { return tevent_req_simple_recv_ntstatus(req); } static void nbench_done(struct tevent_req *subreq); static struct tevent_req *nbench_send( TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, const char *cliname, FILE *loadfile, void (*bw_report)(size_t nread, size_t nwritten, void *private_data), void *bw_report_private) { struct tevent_req *req, *subreq; struct nbench_state *state; req = tevent_req_create(mem_ctx, &state, struct nbench_state); if (req == NULL) { return NULL; } state->ev = ev; state->cli = cli; state->cliname = cliname; state->loadfile = loadfile; state->bw_report = bw_report; state->bw_report_private = bw_report_private; subreq = nbench_cmd_send(state, ev, state); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, nbench_done, req); return req; } static void nbench_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); struct nbench_state *state = tevent_req_data( req, struct nbench_state); NTSTATUS status; status = nbench_cmd_recv(subreq); TALLOC_FREE(subreq); if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { tevent_req_done(req); return; } if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(req, status); return; } subreq = nbench_cmd_send(state, state->ev, state); if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, nbench_done, req); } static NTSTATUS nbench_recv(struct tevent_req *req) { return tevent_req_simple_recv_ntstatus(req); } bool run_nbench2(int dummy) { TALLOC_CTX *frame = talloc_stackframe(); struct tevent_context *ev; struct cli_state *cli = NULL; FILE *loadfile; bool ret = false; struct tevent_req *req; NTSTATUS status; loadfile = fopen("client.txt", "r"); if (loadfile == NULL) { fprintf(stderr, "Could not open \"client.txt\": %s\n", strerror(errno)); return false; } ev = samba_tevent_context_init(talloc_tos()); if (ev == NULL) { goto fail; } if (!torture_open_connection(&cli, 0)) { goto fail; } req = nbench_send(talloc_tos(), ev, cli, "client1", loadfile, NULL, NULL); if (req == NULL) { goto fail; } if (!tevent_req_poll(req, ev)) { goto fail; } status = nbench_recv(req); TALLOC_FREE(req); printf("nbench returned %s\n", nt_errstr(status)); ret = true; fail: if (cli != NULL) { torture_close_connection(cli); } TALLOC_FREE(ev); if (loadfile != NULL) { fclose(loadfile); loadfile = NULL; } TALLOC_FREE(frame); return ret; }