Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : SMB torture tester
4 : Copyright (C) Andrew Tridgell 1997-2003
5 : Copyright (C) Jelmer Vernooij 2006
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "includes.h"
22 : #include "lib/util/util_file.h"
23 : #include "libcli/raw/libcliraw.h"
24 : #include "libcli/raw/raw_proto.h"
25 : #include "system/time.h"
26 : #include "system/wait.h"
27 : #include "system/filesys.h"
28 : #include "../libcli/smb/smb_constants.h"
29 : #include "libcli/libcli.h"
30 : #include "lib/events/events.h"
31 : #include "libcli/resolve/resolve.h"
32 : #include "torture/smbtorture.h"
33 : #include "torture/util.h"
34 : #include "libcli/smb_composite/smb_composite.h"
35 : #include "libcli/composite/composite.h"
36 : #include "param/param.h"
37 : #include "torture/basic/proto.h"
38 : #include "lib/cmdline/cmdline.h"
39 :
40 0 : static bool wait_lock(struct smbcli_state *c, int fnum, uint32_t offset, uint32_t len)
41 : {
42 0 : while (NT_STATUS_IS_ERR(smbcli_lock(c->tree, fnum, offset, len, -1, WRITE_LOCK))) {
43 0 : if (!check_error(__location__, c, ERRDOS, ERRlock, NT_STATUS_LOCK_NOT_GRANTED)) return false;
44 : }
45 0 : return true;
46 : }
47 :
48 :
49 0 : static bool rw_torture(struct torture_context *tctx, struct smbcli_state *c)
50 : {
51 0 : const char *lockfname = "\\torture.lck";
52 0 : char *fname;
53 0 : int fnum;
54 0 : int fnum2;
55 0 : pid_t pid2, pid = getpid();
56 0 : int i, j;
57 0 : uint8_t buf[1024];
58 0 : bool correct = true;
59 :
60 0 : fnum2 = smbcli_open(c->tree, lockfname, O_RDWR | O_CREAT | O_EXCL,
61 : DENY_NONE);
62 0 : if (fnum2 == -1)
63 0 : fnum2 = smbcli_open(c->tree, lockfname, O_RDWR, DENY_NONE);
64 0 : if (fnum2 == -1) {
65 0 : torture_comment(tctx, "open of %s failed (%s)\n", lockfname, smbcli_errstr(c->tree));
66 0 : return false;
67 : }
68 :
69 0 : generate_random_buffer(buf, sizeof(buf));
70 :
71 0 : for (i=0;i<torture_numops;i++) {
72 0 : unsigned int n = (unsigned int)random()%10;
73 0 : int ret;
74 :
75 0 : if (i % 10 == 0) {
76 0 : if (torture_setting_bool(tctx, "progress", true)) {
77 0 : torture_comment(tctx, "%d\r", i);
78 0 : fflush(stdout);
79 : }
80 : }
81 0 : ret = asprintf(&fname, "\\torture.%u", n);
82 0 : torture_assert(tctx, ret != -1, "asprintf failed");
83 :
84 0 : if (!wait_lock(c, fnum2, n*sizeof(int), sizeof(int))) {
85 0 : return false;
86 : }
87 :
88 0 : fnum = smbcli_open(c->tree, fname, O_RDWR | O_CREAT | O_TRUNC, DENY_ALL);
89 0 : if (fnum == -1) {
90 0 : torture_comment(tctx, "open failed (%s)\n", smbcli_errstr(c->tree));
91 0 : correct = false;
92 0 : break;
93 : }
94 :
95 0 : if (smbcli_write(c->tree, fnum, 0, &pid, 0, sizeof(pid)) != sizeof(pid)) {
96 0 : torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
97 0 : correct = false;
98 : }
99 :
100 0 : for (j=0;j<50;j++) {
101 0 : if (smbcli_write(c->tree, fnum, 0, buf,
102 0 : sizeof(pid)+(j*sizeof(buf)),
103 : sizeof(buf)) != sizeof(buf)) {
104 0 : torture_comment(tctx, "write failed (%s)\n", smbcli_errstr(c->tree));
105 0 : correct = false;
106 : }
107 : }
108 :
109 0 : pid2 = 0;
110 :
111 0 : if (smbcli_read(c->tree, fnum, &pid2, 0, sizeof(pid)) != sizeof(pid)) {
112 0 : torture_comment(tctx, "read failed (%s)\n", smbcli_errstr(c->tree));
113 0 : correct = false;
114 : }
115 :
116 0 : if (pid2 != pid) {
117 0 : torture_comment(tctx, "data corruption!\n");
118 0 : correct = false;
119 : }
120 :
121 0 : if (NT_STATUS_IS_ERR(smbcli_close(c->tree, fnum))) {
122 0 : torture_comment(tctx, "close failed (%s)\n", smbcli_errstr(c->tree));
123 0 : correct = false;
124 : }
125 :
126 0 : if (NT_STATUS_IS_ERR(smbcli_unlink(c->tree, fname))) {
127 0 : torture_comment(tctx, "unlink failed (%s)\n", smbcli_errstr(c->tree));
128 0 : correct = false;
129 : }
130 :
131 0 : if (NT_STATUS_IS_ERR(smbcli_unlock(c->tree, fnum2, n*sizeof(int), sizeof(int)))) {
132 0 : torture_comment(tctx, "unlock failed (%s)\n", smbcli_errstr(c->tree));
133 0 : correct = false;
134 : }
135 0 : free(fname);
136 : }
137 :
138 0 : smbcli_close(c->tree, fnum2);
139 0 : smbcli_unlink(c->tree, lockfname);
140 :
141 0 : torture_comment(tctx, "%d\n", i);
142 :
143 0 : return correct;
144 : }
145 :
146 0 : bool run_torture(struct torture_context *tctx, struct smbcli_state *cli, int dummy)
147 : {
148 0 : return rw_torture(tctx, cli);
149 : }
150 :
151 :
152 : /*
153 : see how many RPC pipes we can open at once
154 : */
155 0 : bool run_pipe_number(struct torture_context *tctx,
156 : struct smbcli_state *cli1)
157 : {
158 0 : const char *pipe_name = "\\WKSSVC";
159 0 : int fnum;
160 0 : int num_pipes = 0;
161 :
162 0 : while(1) {
163 0 : fnum = smbcli_nt_create_full(cli1->tree, pipe_name, 0, SEC_FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL,
164 : NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE, NTCREATEX_DISP_OPEN_IF, 0, 0);
165 :
166 0 : if (fnum == -1) {
167 0 : torture_comment(tctx, "Open of pipe %s failed with error (%s)\n", pipe_name, smbcli_errstr(cli1->tree));
168 0 : break;
169 : }
170 0 : num_pipes++;
171 0 : if (torture_setting_bool(tctx, "progress", true)) {
172 0 : torture_comment(tctx, "%d\r", num_pipes);
173 0 : fflush(stdout);
174 : }
175 : }
176 :
177 0 : torture_comment(tctx, "pipe_number test - we can open %d %s pipes.\n", num_pipes, pipe_name );
178 0 : return true;
179 : }
180 :
181 :
182 :
183 :
184 : /*
185 : open N connections to the server and just hold them open
186 : used for testing performance when there are N idle users
187 : already connected
188 : */
189 0 : bool torture_holdcon(struct torture_context *tctx)
190 : {
191 0 : int i;
192 0 : struct smbcli_state **cli;
193 0 : int num_dead = 0;
194 :
195 0 : torture_comment(tctx, "Opening %d connections\n", torture_numops);
196 :
197 0 : cli = malloc_array_p(struct smbcli_state *, torture_numops);
198 :
199 0 : for (i=0;i<torture_numops;i++) {
200 0 : if (!torture_open_connection(&cli[i], tctx, i)) {
201 0 : return false;
202 : }
203 0 : if (torture_setting_bool(tctx, "progress", true)) {
204 0 : torture_comment(tctx, "opened %d connections\r", i+1);
205 0 : fflush(stdout);
206 : }
207 : }
208 :
209 0 : torture_comment(tctx, "\nStarting pings\n");
210 :
211 0 : while (1) {
212 0 : for (i=0;i<torture_numops;i++) {
213 0 : NTSTATUS status;
214 0 : if (cli[i]) {
215 0 : status = smbcli_chkpath(cli[i]->tree, "\\");
216 0 : if (!NT_STATUS_IS_OK(status)) {
217 0 : torture_comment(tctx, "Connection %d is dead\n", i);
218 0 : cli[i] = NULL;
219 0 : num_dead++;
220 : }
221 0 : usleep(100);
222 : }
223 : }
224 :
225 0 : if (num_dead == torture_numops) {
226 0 : torture_comment(tctx, "All connections dead - finishing\n");
227 0 : break;
228 : }
229 :
230 0 : torture_comment(tctx, ".");
231 0 : fflush(stdout);
232 : }
233 :
234 0 : return true;
235 : }
236 :
237 : /*
238 : open a file N times on the server and just hold them open
239 : used for testing performance when there are N file handles
240 : open
241 : */
242 0 : bool torture_holdopen(struct torture_context *tctx,
243 : struct smbcli_state *cli)
244 : {
245 0 : int i, fnum;
246 0 : const char *fname = "\\holdopen.dat";
247 0 : NTSTATUS status;
248 :
249 0 : smbcli_unlink(cli->tree, fname);
250 :
251 0 : fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
252 0 : if (fnum == -1) {
253 0 : torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
254 0 : return false;
255 : }
256 :
257 0 : smbcli_close(cli->tree, fnum);
258 :
259 0 : for (i=0;i<torture_numops;i++) {
260 0 : union smb_open op;
261 :
262 0 : op.generic.level = RAW_OPEN_NTCREATEX;
263 0 : op.ntcreatex.in.root_fid.fnum = 0;
264 0 : op.ntcreatex.in.flags = 0;
265 0 : op.ntcreatex.in.access_mask = SEC_FILE_WRITE_DATA;
266 0 : op.ntcreatex.in.create_options = 0;
267 0 : op.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
268 0 : op.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_MASK;
269 0 : op.ntcreatex.in.alloc_size = 0;
270 0 : op.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
271 0 : op.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
272 0 : op.ntcreatex.in.security_flags = 0;
273 0 : op.ntcreatex.in.fname = fname;
274 0 : status = smb_raw_open(cli->tree, tctx, &op);
275 0 : if (!NT_STATUS_IS_OK(status)) {
276 0 : torture_warning(tctx, "open %d failed\n", i);
277 0 : continue;
278 : }
279 :
280 0 : if (torture_setting_bool(tctx, "progress", true)) {
281 0 : torture_comment(tctx, "opened %d file\r", i);
282 0 : fflush(stdout);
283 : }
284 : }
285 :
286 0 : torture_comment(tctx, "\nStarting pings\n");
287 :
288 0 : while (1) {
289 0 : struct smb_echo ec;
290 0 : ZERO_STRUCT(ec);
291 0 : status = smb_raw_echo(cli->transport, &ec);
292 0 : torture_comment(tctx, ".");
293 0 : fflush(stdout);
294 0 : sleep(15);
295 : }
296 : }
297 :
298 : /*
299 : test how many open files this server supports on the one socket
300 : */
301 0 : bool torture_maxfid_test(struct torture_context *tctx, struct smbcli_state *cli)
302 : {
303 : #define MAXFID_TEMPLATE "\\maxfid\\fid%d\\maxfid.%d.%d"
304 0 : char *fname;
305 0 : int fnums[0x11000], i;
306 0 : int retries=4, maxfid;
307 0 : bool correct = true;
308 0 : int ret;
309 :
310 0 : if (retries <= 0) {
311 0 : torture_comment(tctx, "failed to connect\n");
312 0 : return false;
313 : }
314 :
315 0 : if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
316 0 : torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
317 : smbcli_errstr(cli->tree));
318 0 : return false;
319 : }
320 0 : if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, "\\maxfid"))) {
321 0 : torture_comment(tctx, "Failed to mkdir \\maxfid, error=%s\n",
322 : smbcli_errstr(cli->tree));
323 0 : return false;
324 : }
325 :
326 0 : torture_comment(tctx, "Testing maximum number of open files\n");
327 :
328 0 : for (i=0; i<0x11000; i++) {
329 0 : if (i % 1000 == 0) {
330 0 : ret = asprintf(&fname, "\\maxfid\\fid%d", i/1000);
331 0 : torture_assert(tctx, ret != -1, "asprintf failed");
332 0 : if (NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, fname))) {
333 0 : torture_comment(tctx, "Failed to mkdir %s, error=%s\n",
334 : fname, smbcli_errstr(cli->tree));
335 0 : return false;
336 : }
337 0 : free(fname);
338 : }
339 0 : ret = asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
340 0 : torture_assert(tctx, ret != -1, "asprintf failed");
341 0 : if ((fnums[i] = smbcli_open(cli->tree, fname,
342 : O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) ==
343 : -1) {
344 0 : torture_comment(tctx, "open of %s failed (%s)\n",
345 : fname, smbcli_errstr(cli->tree));
346 0 : torture_comment(tctx, "maximum fnum is %d\n", i);
347 0 : break;
348 : }
349 0 : free(fname);
350 0 : if (torture_setting_bool(tctx, "progress", true)) {
351 0 : torture_comment(tctx, "%6d\r", i);
352 0 : fflush(stdout);
353 : }
354 : }
355 0 : torture_comment(tctx, "%6d\n", i);
356 :
357 0 : maxfid = i;
358 :
359 0 : torture_comment(tctx, "cleaning up\n");
360 0 : for (i=0;i<maxfid;i++) {
361 0 : ret = asprintf(&fname, MAXFID_TEMPLATE, i/1000, i,(int)getpid());
362 0 : torture_assert(tctx, ret != -1, "asprintf failed");
363 0 : if (NT_STATUS_IS_ERR(smbcli_close(cli->tree, fnums[i]))) {
364 0 : torture_comment(tctx, "Close of fnum %d failed - %s\n", fnums[i], smbcli_errstr(cli->tree));
365 : }
366 0 : if (NT_STATUS_IS_ERR(smbcli_unlink(cli->tree, fname))) {
367 0 : torture_comment(tctx, "unlink of %s failed (%s)\n",
368 : fname, smbcli_errstr(cli->tree));
369 0 : correct = false;
370 : }
371 0 : free(fname);
372 :
373 0 : if (torture_setting_bool(tctx, "progress", true)) {
374 0 : torture_comment(tctx, "%6d\r", i);
375 0 : fflush(stdout);
376 : }
377 : }
378 0 : torture_comment(tctx, "%6d\n", 0);
379 :
380 0 : if (smbcli_deltree(cli->tree, "\\maxfid") == -1) {
381 0 : torture_comment(tctx, "Failed to deltree \\maxfid - %s\n",
382 : smbcli_errstr(cli->tree));
383 0 : return false;
384 : }
385 :
386 0 : torture_comment(tctx, "maxfid test finished\n");
387 :
388 0 : return correct;
389 : #undef MAXFID_TEMPLATE
390 : }
391 :
392 :
393 :
394 : /*
395 : sees what IOCTLs are supported
396 : */
397 0 : bool torture_ioctl_test(struct torture_context *tctx,
398 : struct smbcli_state *cli)
399 : {
400 0 : uint16_t device, function;
401 0 : int fnum;
402 0 : const char *fname = "\\ioctl.dat";
403 0 : NTSTATUS status;
404 0 : union smb_ioctl parms;
405 0 : TALLOC_CTX *mem_ctx;
406 :
407 0 : mem_ctx = talloc_named_const(tctx, 0, "ioctl_test");
408 :
409 0 : smbcli_unlink(cli->tree, fname);
410 :
411 0 : fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT|O_EXCL, DENY_NONE);
412 0 : if (fnum == -1) {
413 0 : torture_comment(tctx, "open of %s failed (%s)\n", fname, smbcli_errstr(cli->tree));
414 0 : return false;
415 : }
416 :
417 0 : parms.ioctl.level = RAW_IOCTL_IOCTL;
418 0 : parms.ioctl.in.file.fnum = fnum;
419 0 : parms.ioctl.in.request = IOCTL_QUERY_JOB_INFO;
420 0 : status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
421 0 : torture_comment(tctx, "ioctl job info: %s\n", smbcli_errstr(cli->tree));
422 :
423 0 : for (device=0;device<0x100;device++) {
424 0 : torture_comment(tctx, "Testing device=0x%x\n", device);
425 0 : for (function=0;function<0x100;function++) {
426 0 : parms.ioctl.in.request = (device << 16) | function;
427 0 : status = smb_raw_ioctl(cli->tree, mem_ctx, &parms);
428 :
429 0 : if (NT_STATUS_IS_OK(status)) {
430 0 : torture_comment(tctx, "ioctl device=0x%x function=0x%x OK : %d bytes\n",
431 0 : device, function, (int)parms.ioctl.out.blob.length);
432 : }
433 : }
434 : }
435 :
436 0 : return true;
437 : }
438 :
439 : static void benchrw_callback(struct smbcli_request *req);
440 : enum benchrw_stage {
441 : START,
442 : OPEN_CONNECTION,
443 : CLEANUP_TESTDIR,
444 : MK_TESTDIR,
445 : OPEN_FILE,
446 : INITIAL_WRITE,
447 : READ_WRITE_DATA,
448 : MAX_OPS_REACHED,
449 : ERROR,
450 : CLOSE_FILE,
451 : CLEANUP,
452 : FINISHED
453 : };
454 :
455 : struct bench_params {
456 : struct unclist{
457 : const char *host;
458 : const char *share;
459 : } **unc;
460 : const char *workgroup;
461 : int retry;
462 : unsigned int writeblocks;
463 : unsigned int blocksize;
464 : unsigned int writeratio;
465 : int num_parallel_requests;
466 : };
467 :
468 : struct benchrw_state {
469 : struct torture_context *tctx;
470 : char *dname;
471 : char *fname;
472 : uint16_t fnum;
473 : int nr;
474 : struct smbcli_tree *cli;
475 : uint8_t *buffer;
476 : int writecnt;
477 : int readcnt;
478 : int completed;
479 : int num_parallel_requests;
480 : void *req_params;
481 : enum benchrw_stage mode;
482 : struct bench_params *lpcfg_params;
483 : };
484 :
485 : /*
486 : init params using lpcfg_parm_xxx
487 : return number of unclist entries
488 : */
489 0 : static int init_benchrw_params(struct torture_context *tctx,
490 : struct bench_params *lpar)
491 : {
492 0 : char **unc_list = NULL;
493 0 : int num_unc_names = 0, conn_index=0, empty_lines=0;
494 0 : const char *p;
495 0 : lpar->retry = torture_setting_int(tctx, "retry",3);
496 0 : lpar->blocksize = torture_setting_int(tctx, "blocksize",65535);
497 0 : lpar->writeblocks = torture_setting_int(tctx, "writeblocks",15);
498 0 : lpar->writeratio = torture_setting_int(tctx, "writeratio",5);
499 0 : lpar->num_parallel_requests = torture_setting_int(
500 : tctx, "parallel_requests", 5);
501 0 : lpar->workgroup = lpcfg_workgroup(tctx->lp_ctx);
502 :
503 0 : p = torture_setting_string(tctx, "unclist", NULL);
504 0 : if (p) {
505 0 : char *h, *s;
506 0 : unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
507 0 : if (!unc_list || num_unc_names <= 0) {
508 0 : torture_comment(tctx, "Failed to load unc names list "
509 : "from '%s'\n", p);
510 0 : exit(1);
511 : }
512 :
513 0 : lpar->unc = talloc_array(tctx, struct unclist *,
514 : (num_unc_names-empty_lines));
515 0 : for(conn_index = 0; conn_index < num_unc_names; conn_index++) {
516 : /* ignore empty lines */
517 0 : if(strlen(unc_list[conn_index % num_unc_names])==0){
518 0 : empty_lines++;
519 0 : continue;
520 : }
521 0 : if (!smbcli_parse_unc(
522 0 : unc_list[conn_index % num_unc_names],
523 : NULL, &h, &s)) {
524 0 : torture_comment(
525 : tctx, "Failed to parse UNC "
526 : "name %s\n",
527 0 : unc_list[conn_index % num_unc_names]);
528 0 : exit(1);
529 : }
530 0 : lpar->unc[conn_index-empty_lines] =
531 0 : talloc(tctx, struct unclist);
532 0 : lpar->unc[conn_index-empty_lines]->host = h;
533 0 : lpar->unc[conn_index-empty_lines]->share = s;
534 : }
535 0 : return num_unc_names-empty_lines;
536 : }else{
537 0 : lpar->unc = talloc_array(tctx, struct unclist *, 1);
538 0 : lpar->unc[0] = talloc(tctx,struct unclist);
539 0 : lpar->unc[0]->host = torture_setting_string(tctx, "host",
540 : NULL);
541 0 : lpar->unc[0]->share = torture_setting_string(tctx, "share",
542 : NULL);
543 0 : return 1;
544 : }
545 : }
546 :
547 : /*
548 : Called when the reads & writes are finished. closes the file.
549 : */
550 0 : static NTSTATUS benchrw_close(struct torture_context *tctx,
551 : struct smbcli_request *req,
552 : struct benchrw_state *state)
553 : {
554 0 : union smb_close close_parms;
555 :
556 0 : NT_STATUS_NOT_OK_RETURN(req->status);
557 :
558 0 : torture_comment(tctx, "Close file %d (%d)\n",state->nr,state->fnum);
559 0 : close_parms.close.level = RAW_CLOSE_CLOSE;
560 0 : close_parms.close.in.file.fnum = state->fnum ;
561 0 : close_parms.close.in.write_time = 0;
562 0 : state->mode=CLOSE_FILE;
563 :
564 0 : req = smb_raw_close_send(state->cli, &close_parms);
565 0 : NT_STATUS_HAVE_NO_MEMORY(req);
566 : /*register the callback function!*/
567 0 : req->async.fn = benchrw_callback;
568 0 : req->async.private_data = state;
569 :
570 0 : return NT_STATUS_OK;
571 : }
572 :
573 : static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
574 : struct benchrw_state *state);
575 : static void benchrw_callback(struct smbcli_request *req);
576 :
577 0 : static void benchrw_rw_callback(struct smbcli_request *req)
578 : {
579 0 : struct benchrw_state *state = req->async.private_data;
580 0 : struct torture_context *tctx = state->tctx;
581 :
582 0 : if (!NT_STATUS_IS_OK(req->status)) {
583 0 : state->mode = ERROR;
584 0 : return;
585 : }
586 :
587 0 : state->completed++;
588 0 : state->num_parallel_requests--;
589 :
590 0 : if ((state->completed >= torture_numops)
591 0 : && (state->num_parallel_requests == 0)) {
592 0 : benchrw_callback(req);
593 0 : talloc_free(req);
594 0 : return;
595 : }
596 :
597 0 : talloc_free(req);
598 :
599 0 : if (state->completed + state->num_parallel_requests
600 0 : < torture_numops) {
601 0 : benchrw_readwrite(tctx, state);
602 : }
603 : }
604 :
605 : /*
606 : Called when the initial write is completed is done. write or read a file.
607 : */
608 0 : static NTSTATUS benchrw_readwrite(struct torture_context *tctx,
609 : struct benchrw_state *state)
610 : {
611 0 : struct smbcli_request *req;
612 0 : union smb_read rd;
613 0 : union smb_write wr;
614 :
615 : /* randomize between writes and reads*/
616 0 : if (random() % state->lpcfg_params->writeratio == 0) {
617 0 : torture_comment(tctx, "Callback WRITE file:%d (%d/%d)\n",
618 : state->nr,state->completed,torture_numops);
619 0 : wr.generic.level = RAW_WRITE_WRITEX ;
620 0 : wr.writex.in.file.fnum = state->fnum ;
621 0 : wr.writex.in.offset = 0;
622 0 : wr.writex.in.wmode = 0 ;
623 0 : wr.writex.in.remaining = 0;
624 0 : wr.writex.in.count = state->lpcfg_params->blocksize;
625 0 : wr.writex.in.data = state->buffer;
626 0 : state->readcnt=0;
627 0 : req = smb_raw_write_send(state->cli,&wr);
628 : }
629 : else {
630 0 : torture_comment(tctx,
631 : "Callback READ file:%d (%d/%d) Offset:%d\n",
632 : state->nr,state->completed,torture_numops,
633 0 : (state->readcnt*state->lpcfg_params->blocksize));
634 0 : rd.generic.level = RAW_READ_READX;
635 0 : rd.readx.in.file.fnum = state->fnum ;
636 0 : rd.readx.in.offset = state->readcnt*state->lpcfg_params->blocksize;
637 0 : rd.readx.in.mincnt = state->lpcfg_params->blocksize;
638 0 : rd.readx.in.maxcnt = rd.readx.in.mincnt;
639 0 : rd.readx.in.remaining = 0 ;
640 0 : rd.readx.out.data = state->buffer;
641 0 : rd.readx.in.read_for_execute = false;
642 0 : if(state->readcnt < state->lpcfg_params->writeblocks){
643 0 : state->readcnt++;
644 : }else{
645 : /*start reading from beginning of file*/
646 0 : state->readcnt=0;
647 : }
648 0 : req = smb_raw_read_send(state->cli,&rd);
649 : }
650 0 : state->num_parallel_requests += 1;
651 0 : NT_STATUS_HAVE_NO_MEMORY(req);
652 : /*register the callback function!*/
653 0 : req->async.fn = benchrw_rw_callback;
654 0 : req->async.private_data = state;
655 :
656 0 : return NT_STATUS_OK;
657 : }
658 :
659 : /*
660 : Called when the open is done. writes to the file.
661 : */
662 0 : static NTSTATUS benchrw_open(struct torture_context *tctx,
663 : struct smbcli_request *req,
664 : struct benchrw_state *state)
665 : {
666 0 : union smb_write wr;
667 0 : if(state->mode == OPEN_FILE){
668 0 : NTSTATUS status;
669 0 : status = smb_raw_open_recv(req,tctx,(
670 0 : union smb_open*)state->req_params);
671 0 : NT_STATUS_NOT_OK_RETURN(status);
672 :
673 0 : state->fnum = ((union smb_open*)state->req_params)
674 0 : ->openx.out.file.fnum;
675 0 : torture_comment(tctx, "File opened (%d)\n",state->fnum);
676 0 : state->mode=INITIAL_WRITE;
677 : }
678 :
679 0 : torture_comment(tctx, "Write initial test file:%d (%d/%d)\n",state->nr,
680 0 : (state->writecnt+1)*state->lpcfg_params->blocksize,
681 0 : (state->lpcfg_params->writeblocks*state->lpcfg_params->blocksize));
682 0 : wr.generic.level = RAW_WRITE_WRITEX ;
683 0 : wr.writex.in.file.fnum = state->fnum ;
684 0 : wr.writex.in.offset = state->writecnt *
685 0 : state->lpcfg_params->blocksize;
686 0 : wr.writex.in.wmode = 0 ;
687 0 : wr.writex.in.remaining = (state->lpcfg_params->writeblocks *
688 0 : state->lpcfg_params->blocksize)-
689 0 : ((state->writecnt+1)*state->
690 : lpcfg_params->blocksize);
691 0 : wr.writex.in.count = state->lpcfg_params->blocksize;
692 0 : wr.writex.in.data = state->buffer;
693 0 : state->writecnt++;
694 0 : if(state->writecnt == state->lpcfg_params->writeblocks){
695 0 : state->mode=READ_WRITE_DATA;
696 : }
697 0 : req = smb_raw_write_send(state->cli,&wr);
698 0 : NT_STATUS_HAVE_NO_MEMORY(req);
699 :
700 : /*register the callback function!*/
701 0 : req->async.fn = benchrw_callback;
702 0 : req->async.private_data = state;
703 0 : return NT_STATUS_OK;
704 : }
705 :
706 : /*
707 : Called when the mkdir is done. Opens a file.
708 : */
709 0 : static NTSTATUS benchrw_mkdir(struct torture_context *tctx,
710 : struct smbcli_request *req,
711 : struct benchrw_state *state)
712 : {
713 0 : union smb_open *open_parms;
714 0 : uint8_t *writedata;
715 :
716 0 : NT_STATUS_NOT_OK_RETURN(req->status);
717 :
718 : /* open/create the files */
719 0 : torture_comment(tctx, "Open File %d/%d\n",state->nr+1,
720 : torture_setting_int(tctx, "nprocs", 4));
721 0 : open_parms=talloc_zero(tctx, union smb_open);
722 0 : NT_STATUS_HAVE_NO_MEMORY(open_parms);
723 0 : open_parms->openx.level = RAW_OPEN_OPENX;
724 0 : open_parms->openx.in.flags = 0;
725 0 : open_parms->openx.in.open_mode = OPENX_MODE_ACCESS_RDWR;
726 0 : open_parms->openx.in.search_attrs =
727 : FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
728 0 : open_parms->openx.in.file_attrs = 0;
729 0 : open_parms->openx.in.write_time = 0;
730 0 : open_parms->openx.in.open_func = OPENX_OPEN_FUNC_CREATE;
731 0 : open_parms->openx.in.size = 0;
732 0 : open_parms->openx.in.timeout = 0;
733 0 : open_parms->openx.in.fname = state->fname;
734 :
735 0 : writedata = talloc_size(tctx,state->lpcfg_params->blocksize);
736 0 : NT_STATUS_HAVE_NO_MEMORY(writedata);
737 0 : generate_random_buffer(writedata,state->lpcfg_params->blocksize);
738 0 : state->buffer=writedata;
739 0 : state->writecnt=1;
740 0 : state->readcnt=0;
741 0 : state->req_params=open_parms;
742 0 : state->mode=OPEN_FILE;
743 :
744 0 : req = smb_raw_open_send(state->cli,open_parms);
745 0 : NT_STATUS_HAVE_NO_MEMORY(req);
746 :
747 : /*register the callback function!*/
748 0 : req->async.fn = benchrw_callback;
749 0 : req->async.private_data = state;
750 :
751 0 : return NT_STATUS_OK;
752 : }
753 :
754 : /*
755 : handler for completion of a sub-request of the bench-rw test
756 : */
757 0 : static void benchrw_callback(struct smbcli_request *req)
758 : {
759 0 : struct benchrw_state *state = req->async.private_data;
760 0 : struct torture_context *tctx = state->tctx;
761 :
762 : /*don't send new requests when torture_numops is reached*/
763 0 : if ((state->mode == READ_WRITE_DATA)
764 0 : && (state->completed >= torture_numops)) {
765 0 : state->mode=MAX_OPS_REACHED;
766 : }
767 :
768 0 : switch (state->mode) {
769 :
770 0 : case MK_TESTDIR:
771 0 : if (!NT_STATUS_IS_OK(benchrw_mkdir(tctx, req,state))) {
772 0 : torture_comment(tctx, "Failed to create the test "
773 : "directory - %s\n",
774 : nt_errstr(req->status));
775 0 : state->mode=ERROR;
776 0 : return;
777 : }
778 0 : break;
779 0 : case OPEN_FILE:
780 : case INITIAL_WRITE:
781 0 : if (!NT_STATUS_IS_OK(benchrw_open(tctx, req,state))){
782 0 : torture_comment(tctx, "Failed to open/write the "
783 : "file - %s\n",
784 : nt_errstr(req->status));
785 0 : state->mode=ERROR;
786 0 : state->readcnt=0;
787 0 : return;
788 : }
789 0 : break;
790 0 : case READ_WRITE_DATA:
791 0 : while (state->num_parallel_requests
792 0 : < state->lpcfg_params->num_parallel_requests) {
793 0 : NTSTATUS status;
794 0 : status = benchrw_readwrite(tctx,state);
795 0 : if (!NT_STATUS_IS_OK(status)){
796 0 : torture_comment(tctx, "Failed to read/write "
797 : "the file - %s\n",
798 : nt_errstr(req->status));
799 0 : state->mode=ERROR;
800 0 : return;
801 : }
802 : }
803 0 : break;
804 0 : case MAX_OPS_REACHED:
805 0 : if (!NT_STATUS_IS_OK(benchrw_close(tctx,req,state))){
806 0 : torture_comment(tctx, "Failed to read/write/close "
807 : "the file - %s\n",
808 : nt_errstr(req->status));
809 0 : state->mode=ERROR;
810 0 : return;
811 : }
812 0 : break;
813 0 : case CLOSE_FILE:
814 0 : torture_comment(tctx, "File %d closed\n",state->nr);
815 0 : if (!NT_STATUS_IS_OK(req->status)) {
816 0 : torture_comment(tctx, "Failed to close the "
817 : "file - %s\n",
818 : nt_errstr(req->status));
819 0 : state->mode=ERROR;
820 0 : return;
821 : }
822 0 : state->mode=CLEANUP;
823 0 : return;
824 0 : default:
825 0 : break;
826 : }
827 :
828 : }
829 :
830 : /* open connection async callback function*/
831 0 : static void async_open_callback(struct composite_context *con)
832 : {
833 0 : struct benchrw_state *state = con->async.private_data;
834 0 : struct torture_context *tctx = state->tctx;
835 0 : int retry = state->lpcfg_params->retry;
836 :
837 0 : if (NT_STATUS_IS_OK(con->status)) {
838 0 : state->cli=((struct smb_composite_connect*)
839 0 : state->req_params)->out.tree;
840 0 : state->mode=CLEANUP_TESTDIR;
841 : }else{
842 0 : if(state->writecnt < retry){
843 0 : torture_comment(tctx, "Failed to open connection: "
844 : "%d, Retry (%d/%d)\n",
845 : state->nr,state->writecnt,retry);
846 0 : state->writecnt++;
847 0 : state->mode=START;
848 0 : usleep(1000);
849 : }else{
850 0 : torture_comment(tctx, "Failed to open connection "
851 : "(%d) - %s\n",
852 : state->nr, nt_errstr(con->status));
853 0 : state->mode=ERROR;
854 : }
855 0 : return;
856 : }
857 : }
858 :
859 : /*
860 : establishes a smbcli_tree from scratch (async)
861 : */
862 0 : static struct composite_context *torture_connect_async(
863 : struct torture_context *tctx,
864 : struct smb_composite_connect *smb,
865 : TALLOC_CTX *mem_ctx,
866 : struct tevent_context *ev,
867 : const char *host,
868 : const char *share,
869 : const char *workgroup)
870 : {
871 0 : torture_comment(tctx, "Open Connection to %s/%s\n",host,share);
872 0 : smb->in.dest_host=talloc_strdup(mem_ctx,host);
873 0 : smb->in.service=talloc_strdup(mem_ctx,share);
874 0 : smb->in.dest_ports=lpcfg_smb_ports(tctx->lp_ctx);
875 0 : smb->in.socket_options = lpcfg_socket_options(tctx->lp_ctx);
876 0 : smb->in.called_name = strupper_talloc(mem_ctx, host);
877 0 : smb->in.service_type=NULL;
878 0 : smb->in.credentials = samba_cmdline_get_creds();
879 0 : smb->in.fallback_to_anonymous=false;
880 0 : smb->in.gensec_settings = lpcfg_gensec_settings(mem_ctx, tctx->lp_ctx);
881 0 : smb->in.workgroup=workgroup;
882 0 : lpcfg_smbcli_options(tctx->lp_ctx, &smb->in.options);
883 0 : lpcfg_smbcli_session_options(tctx->lp_ctx, &smb->in.session_options);
884 :
885 0 : return smb_composite_connect_send(smb,mem_ctx,
886 : lpcfg_resolve_context(tctx->lp_ctx),ev);
887 : }
888 :
889 0 : bool run_benchrw(struct torture_context *tctx)
890 : {
891 0 : struct smb_composite_connect *smb_con;
892 0 : const char *fname = "\\rwtest.dat";
893 0 : struct smbcli_request *req;
894 0 : struct benchrw_state **state;
895 0 : int i , num_unc_names;
896 0 : struct tevent_context *ev ;
897 0 : struct composite_context *req1;
898 0 : struct bench_params lpparams;
899 0 : union smb_mkdir parms;
900 0 : int finished = 0;
901 0 : bool success=true;
902 0 : int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
903 :
904 0 : torture_comment(tctx, "Start BENCH-READWRITE num_ops=%d "
905 : "num_nprocs=%d\n",
906 : torture_numops, torture_nprocs);
907 :
908 : /*init talloc context*/
909 0 : ev = tctx->ev;
910 0 : state = talloc_array(tctx, struct benchrw_state *, torture_nprocs);
911 :
912 : /* init params using lpcfg_parm_xxx */
913 0 : num_unc_names = init_benchrw_params(tctx,&lpparams);
914 :
915 : /* init private data structs*/
916 0 : for(i = 0; i<torture_nprocs;i++){
917 0 : state[i]=talloc(tctx,struct benchrw_state);
918 0 : state[i]->tctx = tctx;
919 0 : state[i]->completed=0;
920 0 : state[i]->num_parallel_requests=0;
921 0 : state[i]->lpcfg_params=&lpparams;
922 0 : state[i]->nr=i;
923 0 : state[i]->dname=talloc_asprintf(tctx,"benchrw%d",i);
924 0 : state[i]->fname=talloc_asprintf(tctx,"%s%s",
925 0 : state[i]->dname,fname);
926 0 : state[i]->mode=START;
927 0 : state[i]->writecnt=0;
928 : }
929 :
930 0 : torture_comment(tctx, "Starting async requests\n");
931 0 : while(finished != torture_nprocs){
932 0 : finished=0;
933 0 : for(i = 0; i<torture_nprocs;i++){
934 0 : switch (state[i]->mode){
935 : /*open multiple connections with the same userid */
936 0 : case START:
937 0 : smb_con = talloc_zero(
938 : tctx,struct smb_composite_connect);
939 0 : state[i]->req_params=smb_con;
940 0 : state[i]->mode=OPEN_CONNECTION;
941 0 : req1 = torture_connect_async(
942 : tctx, smb_con, tctx,ev,
943 0 : lpparams.unc[i % num_unc_names]->host,
944 0 : lpparams.unc[i % num_unc_names]->share,
945 : lpparams.workgroup);
946 : /* register callback fn + private data */
947 0 : req1->async.fn = async_open_callback;
948 0 : req1->async.private_data=state[i];
949 0 : break;
950 : /*setup test dirs (sync)*/
951 0 : case CLEANUP_TESTDIR:
952 0 : torture_comment(tctx, "Setup test dir %d\n",i);
953 0 : smb_raw_exit(state[i]->cli->session);
954 0 : if (smbcli_deltree(state[i]->cli,
955 0 : state[i]->dname) == -1) {
956 0 : torture_comment(
957 : tctx,
958 : "Unable to delete %s - %s\n",
959 0 : state[i]->dname,
960 0 : smbcli_errstr(state[i]->cli));
961 0 : state[i]->mode=ERROR;
962 0 : break;
963 : }
964 0 : state[i]->mode=MK_TESTDIR;
965 0 : parms.mkdir.level = RAW_MKDIR_MKDIR;
966 0 : parms.mkdir.in.path = state[i]->dname;
967 0 : req = smb_raw_mkdir_send(state[i]->cli,&parms);
968 : /* register callback fn + private data */
969 0 : req->async.fn = benchrw_callback;
970 0 : req->async.private_data=state[i];
971 0 : break;
972 : /* error occurred , finish */
973 0 : case ERROR:
974 0 : finished++;
975 0 : success=false;
976 0 : break;
977 : /* cleanup , close connection */
978 0 : case CLEANUP:
979 0 : torture_comment(tctx, "Deleting test dir %s "
980 0 : "%d/%d\n",state[i]->dname,
981 : i+1,torture_nprocs);
982 0 : smbcli_deltree(state[i]->cli,state[i]->dname);
983 0 : if (NT_STATUS_IS_ERR(smb_tree_disconnect(
984 : state[i]->cli))) {
985 0 : torture_comment(tctx, "ERROR: Tree "
986 : "disconnect failed");
987 0 : state[i]->mode=ERROR;
988 0 : break;
989 : }
990 0 : state[i]->mode=FINISHED;
991 :
992 0 : FALL_THROUGH;
993 0 : case FINISHED:
994 0 : finished++;
995 0 : break;
996 0 : default:
997 0 : tevent_loop_once(ev);
998 : }
999 : }
1000 : }
1001 :
1002 0 : return success;
1003 : }
1004 :
|