1 /*
2   TODO: add splitting of writes for servers with signing
3 */
4 
5 
6 /*
7    Unix SMB/CIFS implementation.
8    SMB torture tester
9    Copyright (C) Andrew Tridgell 1997-1998
10 
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24 
25 #include "includes.h"
26 #include "system/time.h"
27 #include "system/filesys.h"
28 #include "../lib/util/dlinklist.h"
29 #include "libcli/libcli.h"
30 #include "torture/util.h"
31 #include "torture/nbench/proto.h"
32 
33 extern int nbench_line_count;
34 static int nbio_id = -1;
35 static int nprocs;
36 static bool bypass_io;
37 static struct timeval tv_start, tv_end;
38 static int warmup, timelimit;
39 static int in_cleanup;
40 
41 struct lock_info {
42 	struct lock_info *next, *prev;
43 	off_t offset;
44 	int size;
45 };
46 
47 struct createx_params {
48 	char *fname;
49 	unsigned int create_options;
50 	unsigned int create_disposition;
51 	int handle;
52 };
53 
54 struct ftable {
55 	struct ftable *next, *prev;
56 	int fd;     /* the fd that we got back from the server */
57 	int handle; /* the handle in the load file */
58 	struct createx_params cp;
59 	struct lock_info *locks;
60 };
61 
62 static struct ftable *ftable;
63 
64 static struct {
65 	double bytes, warmup_bytes;
66 	int line;
67 	int done;
68 	bool connected;
69 	double max_latency;
70 	struct timeval starttime;
71 } *children;
72 
73 static bool nb_do_createx(struct ftable *f,
74 			  const char *fname,
75 			  unsigned int create_options,
76 			  unsigned int create_disposition,
77 			  int handle,
78 			  NTSTATUS status,
79 			  bool retry);
80 
81 static bool nb_do_lockx(bool relock, int handle, off_t offset, int size, NTSTATUS status);
82 
nb_set_createx_params(struct ftable * f,const char * fname,unsigned int create_options,unsigned int create_disposition,int handle)83 static void nb_set_createx_params(struct ftable *f,
84 				  const char *fname,
85 				  unsigned int create_options,
86 				  unsigned int create_disposition,
87 				  int handle)
88 {
89 	struct createx_params *cp = &f->cp;
90 
91 	if (fname != NULL) {
92 		cp->fname = talloc_strdup(f, fname);
93 		if (cp->fname == NULL) {
94 			perror("nb_set_createx_params: strdup");
95 			nb_exit(1);
96 		}
97 	} else {
98 		cp->fname = NULL;
99 	}
100 
101 	cp->create_options = create_options;
102 	cp->create_disposition = create_disposition;
103 	cp->handle = handle;
104 }
105 
nb_reestablish_locks(struct ftable * f)106 static bool nb_reestablish_locks(struct ftable *f)
107 {
108 	struct lock_info *linfo = f->locks;
109 
110 	while (linfo != NULL) {
111 		DEBUG(1,("nb_reestablish_locks: lock for file %d at %lu\n",
112 			 f->handle, (unsigned long) linfo->offset));
113 
114 		if (!nb_do_lockx(true, f->handle, linfo->offset, linfo->size, NT_STATUS_OK)) {
115 			printf("nb_reestablish_locks: failed to get lock for file %s at %lu\n",
116 			       f->cp.fname, (unsigned long) linfo->offset);
117 			return false;
118 		}
119 
120 		linfo = linfo->next;
121 	}
122 
123 	return true;
124 }
125 
nb_reopen_all_files(void)126 static bool nb_reopen_all_files(void)
127 {
128 	struct ftable *f = ftable;
129 
130 	while (f != NULL) {
131 		DEBUG(1,("-- nb_reopen_all_files: opening %s (handle %d)\n",
132 			 f->cp.fname, f->cp.handle));
133 
134 		if (!nb_do_createx(f,
135 				   f->cp.fname,
136 				   f->cp.create_options,
137 				   f->cp.create_disposition,
138 				   f->cp.handle,
139 				   NT_STATUS_OK,
140 				   true))
141 		{
142 			printf("-- nb_reopen_all_files: failed to open file %s\n", f->cp.fname);
143 			return false;
144 		}
145 
146 		if (!nb_reestablish_locks(f)) {
147 			printf("--nb_reopen_all_files: failed to reestablish locks\n");
148 			return false;
149 		}
150 
151 		f = f->next;
152 	}
153 
154 	return true;
155 }
156 
nb_reconnect(struct smbcli_state ** cli,struct torture_context * tctx,int client)157 bool nb_reconnect(struct smbcli_state **cli, struct torture_context *tctx, int client)
158 {
159 	children[client].connected = false;
160 
161 	if (*cli != NULL) {
162 		talloc_free(*cli);
163 	}
164 
165 	if (!torture_open_connection(cli, tctx, client)) {
166 		printf("nb_reconnect: failed to connect\n");
167 		*cli = NULL;
168 		return false;
169 	}
170 
171 	nb_setup(*cli, client);
172 
173 	if (!nb_reopen_all_files()) {
174 		printf("nb_reconnect: failed to reopen files in client %d\n", client);
175 		return false;
176 	}
177 
178 	return true;
179 }
180 
nbio_target_rate(double rate)181 void nbio_target_rate(double rate)
182 {
183 	static double last_bytes;
184 	static struct timeval last_time;
185 	double tdelay;
186 
187 	if (last_bytes == 0) {
188 		last_bytes = children[nbio_id].bytes;
189 		last_time = timeval_current();
190 		return;
191 	}
192 
193 	tdelay = (children[nbio_id].bytes - last_bytes)/(1.0e6*rate) - timeval_elapsed(&last_time);
194 	if (tdelay > 0) {
195 		smb_msleep(tdelay*1000);
196 	} else {
197 		children[nbio_id].max_latency = MAX(children[nbio_id].max_latency, -tdelay);
198 	}
199 
200 	last_time = timeval_current();
201 	last_bytes = children[nbio_id].bytes;
202 }
203 
nbio_time_reset(void)204 void nbio_time_reset(void)
205 {
206 	children[nbio_id].starttime = timeval_current();
207 }
208 
nbio_time_delay(double targett)209 void nbio_time_delay(double targett)
210 {
211 	double elapsed = timeval_elapsed(&children[nbio_id].starttime);
212 	if (targett > elapsed) {
213 		smb_msleep(1000*(targett - elapsed));
214 	} else if (elapsed - targett > children[nbio_id].max_latency) {
215 		children[nbio_id].max_latency = MAX(elapsed - targett, children[nbio_id].max_latency);
216 	}
217 }
218 
nbio_result(void)219 double nbio_result(void)
220 {
221 	int i;
222 	double total = 0;
223 	for (i=0;i<nprocs;i++) {
224 		total += children[i].bytes - children[i].warmup_bytes;
225 	}
226 	return 1.0e-6 * total / timeval_elapsed2(&tv_start, &tv_end);
227 }
228 
nbio_latency(void)229 double nbio_latency(void)
230 {
231 	int i;
232 	double max_latency = 0;
233 	for (i=0;i<nprocs;i++) {
234 		if (children[i].max_latency > max_latency) {
235 			max_latency = children[i].max_latency;
236 			children[i].max_latency = 0;
237 		}
238 	}
239 	return max_latency;
240 }
241 
nb_tick(void)242 bool nb_tick(void)
243 {
244 	return children[nbio_id].done;
245 }
246 
247 
nb_alarm(int sig)248 void nb_alarm(int sig)
249 {
250 	int i;
251 	int lines=0;
252 	double t;
253 	int in_warmup = 0;
254 	int num_connected = 0;
255 
256 	if (nbio_id != -1) return;
257 
258 	for (i=0;i<nprocs;i++) {
259 		if (children[i].connected) {
260 			num_connected++;
261 		}
262 		if (children[i].bytes == 0) {
263 			in_warmup = 1;
264 		}
265 		lines += children[i].line;
266 	}
267 
268 	t = timeval_elapsed(&tv_start);
269 
270 	if (!in_warmup && warmup>0 && t > warmup) {
271 		tv_start = timeval_current();
272 		warmup = 0;
273 		for (i=0;i<nprocs;i++) {
274 			children[i].warmup_bytes = children[i].bytes;
275 		}
276 		goto next;
277 	}
278 	if (t < warmup) {
279 		in_warmup = 1;
280 	} else if (!in_warmup && !in_cleanup && t > timelimit) {
281 		for (i=0;i<nprocs;i++) {
282 			children[i].done = 1;
283 		}
284 		tv_end = timeval_current();
285 		in_cleanup = 1;
286 	}
287 	if (t < 1) {
288 		goto next;
289 	}
290 	if (!in_cleanup) {
291 		tv_end = timeval_current();
292 	}
293 
294 	if (in_warmup) {
295 		printf("%4d  %8d  %.2f MB/sec  warmup %.0f sec   \n",
296 		       num_connected, lines/nprocs,
297 		       nbio_result(), t);
298 	} else if (in_cleanup) {
299 		printf("%4d  %8d  %.2f MB/sec  cleanup %.0f sec   \n",
300 		       num_connected, lines/nprocs,
301 		       nbio_result(), t);
302 	} else {
303 		printf("%4d  %8d  %.2f MB/sec  execute %.0f sec  latency %.2f msec \n",
304 		       num_connected, lines/nprocs,
305 		       nbio_result(), t, nbio_latency() * 1.0e3);
306 	}
307 
308 	fflush(stdout);
309 next:
310 	signal(SIGALRM, nb_alarm);
311 	alarm(1);
312 }
313 
nbio_shmem(int n,int t_timelimit,int t_warmup)314 void nbio_shmem(int n, int t_timelimit, int t_warmup)
315 {
316 	nprocs = n;
317 	children = anonymous_shared_allocate(sizeof(*children) * nprocs);
318 	if (!children) {
319 		printf("Failed to setup shared memory!\n");
320 		nb_exit(1);
321 	}
322 	memset(children, 0, sizeof(*children) * nprocs);
323 	timelimit = t_timelimit;
324 	warmup = t_warmup;
325 	in_cleanup = 0;
326 	tv_start = timeval_current();
327 }
328 
find_lock(struct lock_info * linfo,off_t offset,int size)329 static struct lock_info* find_lock(struct lock_info *linfo, off_t offset, int size)
330 {
331 	while (linfo != NULL) {
332 		if (linfo->offset == offset &&
333 		    linfo->size == size)
334 		{
335 			return linfo;
336 		}
337 
338 		linfo = linfo->next;
339 	}
340 
341 	return NULL;
342 }
343 
find_ftable(int handle)344 static struct ftable *find_ftable(int handle)
345 {
346 	struct ftable *f;
347 
348 	for (f=ftable;f;f=f->next) {
349 		if (f->handle == handle) return f;
350 	}
351 	return NULL;
352 }
353 
find_handle(int handle,struct ftable ** f_ret)354 static int find_handle(int handle, struct ftable **f_ret)
355 {
356 	struct ftable *f;
357 
358 	if (f_ret != NULL)
359 		*f_ret = NULL;
360 
361 	children[nbio_id].line = nbench_line_count;
362 
363 	f = find_ftable(handle);
364 	if (f) {
365 		if (f_ret != NULL)
366 			*f_ret = f;
367 		return f->fd;
368 	}
369 	printf("(%d) ERROR: handle %d was not found\n",
370 	       nbench_line_count, handle);
371 	nb_exit(1);
372 
373 	return -1;		/* Not reached */
374 }
375 
376 
377 
378 static struct smbcli_state *c;
379 
380 /*
381   a handler function for oplock break requests
382 */
oplock_handler(struct smbcli_transport * transport,uint16_t tid,uint16_t fnum,uint8_t level,void * private_data)383 static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid,
384 			   uint16_t fnum, uint8_t level, void *private_data)
385 {
386 	struct smbcli_tree *tree = (struct smbcli_tree *)private_data;
387 	return smbcli_oplock_ack(tree, fnum, OPLOCK_BREAK_TO_NONE);
388 }
389 
nb_setup(struct smbcli_state * cli,int id)390 void nb_setup(struct smbcli_state *cli, int id)
391 {
392 	nbio_id = id;
393 	c = cli;
394 	if (bypass_io)
395 		printf("skipping I/O\n");
396 
397 	if (cli) {
398 		smbcli_oplock_handler(cli->transport, oplock_handler, cli->tree);
399 	}
400 
401 	children[id].connected = true;
402 }
403 
404 
check_status(const char * op,NTSTATUS status,NTSTATUS ret)405 static bool check_status(const char *op, NTSTATUS status, NTSTATUS ret)
406 {
407 	if ((NT_STATUS_EQUAL(ret, NT_STATUS_END_OF_FILE) ||
408 	     NT_STATUS_EQUAL(ret, NT_STATUS_NET_WRITE_FAULT) ||
409 	     NT_STATUS_EQUAL(ret, NT_STATUS_CONNECTION_RESET))
410 		&& !NT_STATUS_EQUAL (status, ret))
411 	{
412 		return false;
413 	}
414 
415 	if (!NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(ret)) {
416 		printf("[%d] Error: %s should have failed with %s\n",
417 		       nbench_line_count, op, nt_errstr(status));
418 		nb_exit(1);
419 	}
420 
421 	if (NT_STATUS_IS_OK(status) && !NT_STATUS_IS_OK(ret)) {
422 		printf("[%d] Error: %s should have succeeded - %s\n",
423 		       nbench_line_count, op, nt_errstr(ret));
424 		nb_exit(1);
425 	}
426 
427 	if (!NT_STATUS_EQUAL(status, ret)) {
428 		printf("[%d] Warning: got status %s but expected %s\n",
429 		       nbench_line_count, nt_errstr(ret), nt_errstr(status));
430 	}
431 
432 	return true;
433 }
434 
435 
nb_unlink(const char * fname,int attr,NTSTATUS status,bool retry)436 bool nb_unlink(const char *fname, int attr, NTSTATUS status, bool retry)
437 {
438 	union smb_unlink io;
439 	NTSTATUS ret;
440 
441 	io.unlink.in.pattern = fname;
442 
443 	io.unlink.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
444 	if (strchr(fname, '*') == 0) {
445 		io.unlink.in.attrib |= FILE_ATTRIBUTE_DIRECTORY;
446 	}
447 
448 	ret = smb_raw_unlink(c->tree, &io);
449 
450 	if (!retry)
451 		return check_status("Unlink", status, ret);
452 
453 	return true;
454 }
455 
nb_do_createx(struct ftable * f,const char * fname,unsigned int create_options,unsigned int create_disposition,int handle,NTSTATUS status,bool retry)456 static bool nb_do_createx(struct ftable *f,
457 			  const char *fname,
458 			  unsigned int create_options,
459 			  unsigned int create_disposition,
460 			  int handle,
461 			  NTSTATUS status,
462 			  bool retry)
463 {
464 	union smb_open io;
465 	uint32_t desired_access;
466 	NTSTATUS ret;
467 	TALLOC_CTX *mem_ctx;
468 	unsigned int flags = 0;
469 
470 	mem_ctx = talloc_init("raw_open");
471 
472 	if (create_options & NTCREATEX_OPTIONS_DIRECTORY) {
473 		desired_access = SEC_FILE_READ_DATA;
474 	} else {
475 		desired_access =
476 			SEC_FILE_READ_DATA |
477 			SEC_FILE_WRITE_DATA |
478 			SEC_FILE_READ_ATTRIBUTE |
479 			SEC_FILE_WRITE_ATTRIBUTE;
480 		flags = NTCREATEX_FLAGS_EXTENDED |
481 			NTCREATEX_FLAGS_REQUEST_OPLOCK |
482 			NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
483 	}
484 
485 	io.ntcreatex.level = RAW_OPEN_NTCREATEX;
486 	io.ntcreatex.in.flags = flags;
487 	io.ntcreatex.in.root_fid.fnum = 0;
488 	io.ntcreatex.in.access_mask = desired_access;
489 	io.ntcreatex.in.file_attr = 0;
490 	io.ntcreatex.in.alloc_size = 0;
491 	io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ|NTCREATEX_SHARE_ACCESS_WRITE;
492 	io.ntcreatex.in.open_disposition = create_disposition;
493 	io.ntcreatex.in.create_options = create_options;
494 	io.ntcreatex.in.impersonation = 0;
495 	io.ntcreatex.in.security_flags = 0;
496 	io.ntcreatex.in.fname = fname;
497 
498 	if (retry) {
499 		/* Reopening after a disconnect. */
500 		io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
501 	} else
502 	if (f != NULL &&
503 	    f->cp.create_disposition == NTCREATEX_DISP_CREATE &&
504 	    NT_STATUS_IS_OK(status))
505 	{
506 		/* Reopening after nb_createx() error. */
507 		io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
508 	}
509 
510 	ret = smb_raw_open(c->tree, mem_ctx, &io);
511 
512 	talloc_free(mem_ctx);
513 
514 	if (!check_status("NTCreateX", status, ret))
515 		return false;
516 
517 	if (!NT_STATUS_IS_OK(ret))
518 		return true;
519 
520 	if (f == NULL) {
521 		f = talloc (NULL, struct ftable);
522 		f->locks = NULL;
523 		nb_set_createx_params(f, fname, create_options, create_disposition, handle);
524 		DLIST_ADD_END(ftable, f);
525 	}
526 
527 	f->handle = handle;
528 	f->fd = io.ntcreatex.out.file.fnum;
529 
530 	return true;
531 }
532 
nb_createx(const char * fname,unsigned int create_options,unsigned int create_disposition,int handle,NTSTATUS status)533 bool nb_createx(const char *fname,
534 	       unsigned int create_options, unsigned int create_disposition, int handle,
535 	       NTSTATUS status)
536 {
537 	return nb_do_createx(NULL, fname, create_options, create_disposition, handle, status, false);
538 }
539 
nb_writex(int handle,off_t offset,int size,int ret_size,NTSTATUS status)540 bool nb_writex(int handle, off_t offset, int size, int ret_size, NTSTATUS status)
541 {
542 	union smb_write io;
543 	int i;
544 	NTSTATUS ret;
545 	uint8_t *buf;
546 
547 	i = find_handle(handle, NULL);
548 
549 	if (bypass_io)
550 		return true;
551 
552 	buf = malloc(size);
553 	memset(buf, 0xab, size);
554 
555 	io.writex.level = RAW_WRITE_WRITEX;
556 	io.writex.in.file.fnum = i;
557 	io.writex.in.wmode = 0;
558 	io.writex.in.remaining = 0;
559 	io.writex.in.offset = offset;
560 	io.writex.in.count = size;
561 	io.writex.in.data = buf;
562 
563 	ret = smb_raw_write(c->tree, &io);
564 
565 	free(buf);
566 
567 	if (!check_status("WriteX", status, ret))
568 		return false;
569 
570 	if (NT_STATUS_IS_OK(ret) && io.writex.out.nwritten != ret_size) {
571 		printf("[%d] Warning: WriteX got count %d expected %d\n",
572 		       nbench_line_count,
573 		       io.writex.out.nwritten, ret_size);
574 	}
575 
576 	children[nbio_id].bytes += ret_size;
577 
578 	return true;
579 }
580 
nb_write(int handle,off_t offset,int size,int ret_size,NTSTATUS status)581 bool nb_write(int handle, off_t offset, int size, int ret_size, NTSTATUS status)
582 {
583 	union smb_write io;
584 	int i;
585 	NTSTATUS ret;
586 	uint8_t *buf;
587 
588 	i = find_handle(handle, NULL);
589 
590 	if (bypass_io)
591 		return true;
592 
593 	buf = malloc(size);
594 
595 	memset(buf, 0x12, size);
596 
597 	io.write.level = RAW_WRITE_WRITE;
598 	io.write.in.file.fnum = i;
599 	io.write.in.remaining = 0;
600 	io.write.in.offset = offset;
601 	io.write.in.count = size;
602 	io.write.in.data = buf;
603 
604 	ret = smb_raw_write(c->tree, &io);
605 
606 	free(buf);
607 
608 	if (!check_status("Write", status, ret))
609 		return false;
610 
611 	if (NT_STATUS_IS_OK(ret) && io.write.out.nwritten != ret_size) {
612 		printf("[%d] Warning: Write got count %d expected %d\n",
613 		       nbench_line_count,
614 		       io.write.out.nwritten, ret_size);
615 	}
616 
617 	children[nbio_id].bytes += ret_size;
618 
619 	return true;
620 }
621 
nb_do_lockx(bool relock,int handle,off_t offset,int size,NTSTATUS status)622 static bool nb_do_lockx(bool relock, int handle, off_t offset, int size, NTSTATUS status)
623 {
624 	union smb_lock io;
625 	int i;
626 	NTSTATUS ret;
627 	struct smb_lock_entry lck;
628 	struct ftable *f;
629 
630 	i = find_handle(handle, &f);
631 
632 	lck.pid = getpid();
633 	lck.offset = offset;
634 	lck.count = size;
635 
636 	io.lockx.level = RAW_LOCK_LOCKX;
637 	io.lockx.in.file.fnum = i;
638 	io.lockx.in.mode = 0;
639 	io.lockx.in.timeout = 0;
640 	io.lockx.in.ulock_cnt = 0;
641 	io.lockx.in.lock_cnt = 1;
642 	io.lockx.in.locks = &lck;
643 
644 	ret = smb_raw_lock(c->tree, &io);
645 
646 	if (!check_status("LockX", status, ret))
647 		return false;
648 
649 	if (f != NULL &&
650 	    !relock)
651 	{
652 		struct lock_info *linfo;
653 		linfo = talloc (f, struct lock_info);
654 		linfo->offset = offset;
655 		linfo->size = size;
656 		DLIST_ADD_END(f->locks, linfo);
657 	}
658 
659 	return true;
660 }
661 
nb_lockx(int handle,off_t offset,int size,NTSTATUS status)662 bool nb_lockx(int handle, off_t offset, int size, NTSTATUS status)
663 {
664 	return nb_do_lockx(false, handle, offset, size, status);
665 }
666 
nb_unlockx(int handle,unsigned int offset,int size,NTSTATUS status)667 bool nb_unlockx(int handle, unsigned int offset, int size, NTSTATUS status)
668 {
669 	union smb_lock io;
670 	int i;
671 	NTSTATUS ret;
672 	struct smb_lock_entry lck;
673 	struct ftable *f;
674 
675 	i = find_handle(handle, &f);
676 
677 	lck.pid = getpid();
678 	lck.offset = offset;
679 	lck.count = size;
680 
681 	io.lockx.level = RAW_LOCK_LOCKX;
682 	io.lockx.in.file.fnum = i;
683 	io.lockx.in.mode = 0;
684 	io.lockx.in.timeout = 0;
685 	io.lockx.in.ulock_cnt = 1;
686 	io.lockx.in.lock_cnt = 0;
687 	io.lockx.in.locks = &lck;
688 
689 	ret = smb_raw_lock(c->tree, &io);
690 
691 	if (!check_status("UnlockX", status, ret))
692 		return false;
693 
694 	if (f != NULL) {
695 		struct lock_info *linfo;
696 		linfo = find_lock(f->locks, offset, size);
697 		if (linfo != NULL)
698 			DLIST_REMOVE(f->locks, linfo);
699 		else
700 			printf("nb_unlockx: unknown lock (%d)\n", handle);
701 	}
702 
703 	return true;
704 }
705 
nb_readx(int handle,off_t offset,int size,int ret_size,NTSTATUS status)706 bool nb_readx(int handle, off_t offset, int size, int ret_size, NTSTATUS status)
707 {
708 	union smb_read io;
709 	int i;
710 	NTSTATUS ret;
711 	uint8_t *buf;
712 
713 	i = find_handle(handle, NULL);
714 
715 	if (bypass_io)
716 		return true;
717 
718 	buf = malloc(size);
719 
720 	io.readx.level = RAW_READ_READX;
721 	io.readx.in.file.fnum = i;
722 	io.readx.in.offset    = offset;
723 	io.readx.in.mincnt    = size;
724 	io.readx.in.maxcnt    = size;
725 	io.readx.in.remaining = 0;
726 	io.readx.in.read_for_execute = false;
727 	io.readx.out.data     = buf;
728 
729 	ret = smb_raw_read(c->tree, &io);
730 
731 	free(buf);
732 
733 	if (!check_status("ReadX", status, ret))
734 		return false;
735 
736 	if (NT_STATUS_IS_OK(ret) && io.readx.out.nread != ret_size) {
737 		printf("[%d] ERROR: ReadX got count %d expected %d\n",
738 		       nbench_line_count,
739 		       io.readx.out.nread, ret_size);
740 		nb_exit(1);
741 	}
742 
743 	children[nbio_id].bytes += ret_size;
744 
745 	return true;
746 }
747 
nb_close(int handle,NTSTATUS status)748 bool nb_close(int handle, NTSTATUS status)
749 {
750 	NTSTATUS ret;
751 	union smb_close io;
752 	int i;
753 
754 	i = find_handle(handle, NULL);
755 
756 	io.close.level = RAW_CLOSE_CLOSE;
757 	io.close.in.file.fnum = i;
758 	io.close.in.write_time = 0;
759 
760 	ret = smb_raw_close(c->tree, &io);
761 
762 	if (!check_status("Close", status, ret))
763 		return false;
764 
765 	if (NT_STATUS_IS_OK(ret)) {
766 		struct ftable *f = find_ftable(handle);
767 		DLIST_REMOVE(ftable, f);
768 		talloc_free(f);
769 	}
770 
771 	return true;
772 }
773 
nb_rmdir(const char * dname,NTSTATUS status,bool retry)774 bool nb_rmdir(const char *dname, NTSTATUS status, bool retry)
775 {
776 	NTSTATUS ret;
777 	struct smb_rmdir io;
778 
779 	io.in.path = dname;
780 
781 	ret = smb_raw_rmdir(c->tree, &io);
782 
783 	if (!retry)
784 		return check_status("Rmdir", status, ret);
785 
786 	return true;
787 }
788 
nb_mkdir(const char * dname,NTSTATUS status,bool retry)789 bool nb_mkdir(const char *dname, NTSTATUS status, bool retry)
790 {
791 	union smb_mkdir io;
792 
793 	io.mkdir.level = RAW_MKDIR_MKDIR;
794 	io.mkdir.in.path = dname;
795 
796 	/* NOTE! no error checking. Used for base fileset creation */
797 	smb_raw_mkdir(c->tree, &io);
798 
799 	return true;
800 }
801 
nb_rename(const char * o,const char * n,NTSTATUS status,bool retry)802 bool nb_rename(const char *o, const char *n, NTSTATUS status, bool retry)
803 {
804 	NTSTATUS ret;
805 	union smb_rename io;
806 
807 	io.generic.level = RAW_RENAME_RENAME;
808 	io.rename.in.attrib = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
809 	io.rename.in.pattern1 = o;
810 	io.rename.in.pattern2 = n;
811 
812 	ret = smb_raw_rename(c->tree, &io);
813 
814 	if (!retry)
815 		return check_status("Rename", status, ret);
816 
817 	return true;
818 }
819 
820 
nb_qpathinfo(const char * fname,int level,NTSTATUS status)821 bool nb_qpathinfo(const char *fname, int level, NTSTATUS status)
822 {
823 	union smb_fileinfo io;
824 	TALLOC_CTX *mem_ctx;
825 	NTSTATUS ret;
826 
827 	mem_ctx = talloc_init("nb_qpathinfo");
828 
829 	io.generic.level = level;
830 	io.generic.in.file.path = fname;
831 
832 	ret = smb_raw_pathinfo(c->tree, mem_ctx, &io);
833 
834 	talloc_free(mem_ctx);
835 
836 	return check_status("Pathinfo", status, ret);
837 }
838 
839 
nb_qfileinfo(int fnum,int level,NTSTATUS status)840 bool nb_qfileinfo(int fnum, int level, NTSTATUS status)
841 {
842 	union smb_fileinfo io;
843 	TALLOC_CTX *mem_ctx;
844 	NTSTATUS ret;
845 	int i;
846 
847 	i = find_handle(fnum, NULL);
848 
849 	mem_ctx = talloc_init("nb_qfileinfo");
850 
851 	io.generic.level = level;
852 	io.generic.in.file.fnum = i;
853 
854 	ret = smb_raw_fileinfo(c->tree, mem_ctx, &io);
855 
856 	talloc_free(mem_ctx);
857 
858 	return check_status("Fileinfo", status, ret);
859 }
860 
nb_sfileinfo(int fnum,int level,NTSTATUS status)861 bool nb_sfileinfo(int fnum, int level, NTSTATUS status)
862 {
863 	union smb_setfileinfo io;
864 	NTSTATUS ret;
865 	int i;
866 
867 	if (level != RAW_SFILEINFO_BASIC_INFORMATION) {
868 		printf("[%d] Warning: setfileinfo level %d not handled\n", nbench_line_count, level);
869 		return true;
870 	}
871 
872 	ZERO_STRUCT(io);
873 
874 	i = find_handle(fnum, NULL);
875 
876 	io.generic.level = level;
877 	io.generic.in.file.fnum = i;
878 	unix_to_nt_time(&io.basic_info.in.create_time, time(NULL));
879 	unix_to_nt_time(&io.basic_info.in.access_time, 0);
880 	unix_to_nt_time(&io.basic_info.in.write_time, 0);
881 	unix_to_nt_time(&io.basic_info.in.change_time, 0);
882 	io.basic_info.in.attrib = 0;
883 
884 	ret = smb_raw_setfileinfo(c->tree, &io);
885 
886 	return check_status("Setfileinfo", status, ret);
887 }
888 
nb_qfsinfo(int level,NTSTATUS status)889 bool nb_qfsinfo(int level, NTSTATUS status)
890 {
891 	union smb_fsinfo io;
892 	TALLOC_CTX *mem_ctx;
893 	NTSTATUS ret;
894 
895 	mem_ctx = talloc_init("smbcli_dskattr");
896 
897 	io.generic.level = level;
898 	ret = smb_raw_fsinfo(c->tree, mem_ctx, &io);
899 
900 	talloc_free(mem_ctx);
901 
902 	return check_status("Fsinfo", status, ret);
903 }
904 
905 /* callback function used for trans2 search */
findfirst_callback(void * private_data,const union smb_search_data * file)906 static bool findfirst_callback(void *private_data, const union smb_search_data *file)
907 {
908 	return true;
909 }
910 
nb_findfirst(const char * mask,int level,int maxcnt,int count,NTSTATUS status)911 bool nb_findfirst(const char *mask, int level, int maxcnt, int count, NTSTATUS status)
912 {
913 	union smb_search_first io;
914 	TALLOC_CTX *mem_ctx;
915 	NTSTATUS ret;
916 
917 	mem_ctx = talloc_init("smbcli_dskattr");
918 
919 	io.t2ffirst.level = RAW_SEARCH_TRANS2;
920 	io.t2ffirst.data_level = level;
921 	io.t2ffirst.in.max_count = maxcnt;
922 	io.t2ffirst.in.search_attrib = FILE_ATTRIBUTE_DIRECTORY;
923 	io.t2ffirst.in.pattern = mask;
924 	io.t2ffirst.in.flags = FLAG_TRANS2_FIND_CLOSE;
925 	io.t2ffirst.in.storage_type = 0;
926 
927 	ret = smb_raw_search_first(c->tree, mem_ctx, &io, NULL, findfirst_callback);
928 
929 	talloc_free(mem_ctx);
930 
931 	if (!check_status("Search", status, ret))
932 		return false;
933 
934 	if (NT_STATUS_IS_OK(ret) && io.t2ffirst.out.count != count) {
935 		printf("[%d] Warning: got count %d expected %d\n",
936 		       nbench_line_count,
937 		       io.t2ffirst.out.count, count);
938 	}
939 
940 	return true;
941 }
942 
nb_flush(int fnum,NTSTATUS status)943 bool nb_flush(int fnum, NTSTATUS status)
944 {
945 	union smb_flush io;
946 	NTSTATUS ret;
947 	int i;
948 	i = find_handle(fnum, NULL);
949 
950 	io.flush.level		= RAW_FLUSH_FLUSH;
951 	io.flush.in.file.fnum	= i;
952 
953 	ret = smb_raw_flush(c->tree, &io);
954 
955 	return check_status("Flush", status, ret);
956 }
957 
nb_sleep(int usec,NTSTATUS status)958 void nb_sleep(int usec, NTSTATUS status)
959 {
960 	usleep(usec);
961 }
962 
nb_deltree(const char * dname,bool retry)963 bool nb_deltree(const char *dname, bool retry)
964 {
965 	int total_deleted;
966 
967 	smb_raw_exit(c->session);
968 
969 	while (ftable) {
970 		struct ftable *f = ftable;
971 		DLIST_REMOVE(ftable, f);
972 		talloc_free (f);
973 	}
974 
975 	total_deleted = smbcli_deltree(c->tree, dname);
976 
977 	if (total_deleted == -1) {
978 		printf("Failed to cleanup tree %s - exiting\n", dname);
979 		nb_exit(1);
980 	}
981 
982 	smbcli_rmdir(c->tree, dname);
983 
984 	return true;
985 }
986 
987 
nb_exit(int status)988 void nb_exit(int status)
989 {
990 	children[nbio_id].connected = false;
991 	printf("[%d] client %d exiting with status %d\n",
992 	       nbench_line_count, nbio_id, status);
993 	exit(status);
994 }
995