2 TODO: add splitting of writes for servers with signing
7 Unix SMB/CIFS implementation.
9 Copyright (C) Andrew Tridgell 1997-1998
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.
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.
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/>.
26 #include "system/time.h"
27 #include "system/filesys.h"
28 #include "../lib/util/dlinklist.h"
29 #include "libcli/libcli.h"
30 #include "libcli/raw/libcliraw.h"
31 #include "torture/torture.h"
32 #include "libcli/libcli.h"
33 #include "torture/util.h"
34 #include "torture/nbench/proto.h"
36 extern int nbench_line_count
;
37 static int nbio_id
= -1;
39 static bool bypass_io
;
40 static struct timeval tv_start
, tv_end
;
41 static int warmup
, timelimit
;
42 static int in_cleanup
;
45 struct lock_info
*next
, *prev
;
50 struct createx_params
{
52 uint_t create_options
;
53 uint_t create_disposition
;
58 struct ftable
*next
, *prev
;
59 int fd
; /* the fd that we got back from the server */
60 int handle
; /* the handle in the load file */
61 struct createx_params cp
;
62 struct lock_info
*locks
;
65 static struct ftable
*ftable
;
68 double bytes
, warmup_bytes
;
73 struct timeval starttime
;
76 static bool nb_do_createx(struct ftable
*f
,
78 uint_t create_options
,
79 uint_t create_disposition
,
84 static bool nb_do_lockx(bool relock
, int handle
, off_t offset
, int size
, NTSTATUS status
);
86 static void nb_set_createx_params(struct ftable
*f
,
88 uint_t create_options
,
89 uint_t create_disposition
,
92 struct createx_params
*cp
= &f
->cp
;
95 cp
->fname
= talloc_strdup(f
, fname
);
96 if (cp
->fname
== NULL
) {
97 perror("nb_set_createx_params: strdup");
104 cp
->create_options
= create_options
;
105 cp
->create_disposition
= create_disposition
;
109 static bool nb_reestablish_locks(struct ftable
*f
)
111 struct lock_info
*linfo
= f
->locks
;
113 while (linfo
!= NULL
) {
114 DEBUG(1,("nb_reestablish_locks: lock for file %d at %lu\n",
115 f
->handle
, (unsigned long) linfo
->offset
));
117 if (!nb_do_lockx(true, f
->handle
, linfo
->offset
, linfo
->size
, NT_STATUS_OK
)) {
118 printf("nb_reestablish_locks: failed to get lock for file %s at %lu\n",
119 f
->cp
.fname
, (unsigned long) linfo
->offset
);
129 static bool nb_reopen_all_files(void)
131 struct ftable
*f
= ftable
;
134 DEBUG(1,("-- nb_reopen_all_files: opening %s (handle %d)\n",
135 f
->cp
.fname
, f
->cp
.handle
));
137 if (!nb_do_createx(f
,
139 f
->cp
.create_options
,
140 f
->cp
.create_disposition
,
145 printf("-- nb_reopen_all_files: failed to open file %s\n", f
->cp
.fname
);
149 if (!nb_reestablish_locks(f
)) {
150 printf("--nb_reopen_all_files: failed to reestablish locks\n");
160 bool nb_reconnect(struct smbcli_state
**cli
, struct torture_context
*tctx
, int client
)
162 children
[client
].connected
= false;
168 if (!torture_open_connection(cli
, tctx
, client
)) {
169 printf("nb_reconnect: failed to connect\n");
174 nb_setup(*cli
, client
);
176 if (!nb_reopen_all_files()) {
177 printf("nb_reconnect: failed to reopen files in client %d\n", client
);
184 void nbio_target_rate(double rate
)
186 static double last_bytes
;
187 static struct timeval last_time
;
190 if (last_bytes
== 0) {
191 last_bytes
= children
[nbio_id
].bytes
;
192 last_time
= timeval_current();
196 tdelay
= (children
[nbio_id
].bytes
- last_bytes
)/(1.0e6
*rate
) - timeval_elapsed(&last_time
);
200 children
[nbio_id
].max_latency
= MAX(children
[nbio_id
].max_latency
, -tdelay
);
203 last_time
= timeval_current();
204 last_bytes
= children
[nbio_id
].bytes
;
207 void nbio_time_reset(void)
209 children
[nbio_id
].starttime
= timeval_current();
212 void nbio_time_delay(double targett
)
214 double elapsed
= timeval_elapsed(&children
[nbio_id
].starttime
);
215 if (targett
> elapsed
) {
216 msleep(1000*(targett
- elapsed
));
217 } else if (elapsed
- targett
> children
[nbio_id
].max_latency
) {
218 children
[nbio_id
].max_latency
= MAX(elapsed
- targett
, children
[nbio_id
].max_latency
);
222 double nbio_result(void)
226 for (i
=0;i
<nprocs
;i
++) {
227 total
+= children
[i
].bytes
- children
[i
].warmup_bytes
;
229 return 1.0e-6 * total
/ timeval_elapsed2(&tv_start
, &tv_end
);
232 double nbio_latency(void)
235 double max_latency
= 0;
236 for (i
=0;i
<nprocs
;i
++) {
237 if (children
[i
].max_latency
> max_latency
) {
238 max_latency
= children
[i
].max_latency
;
239 children
[i
].max_latency
= 0;
247 return children
[nbio_id
].done
;
251 void nb_alarm(int sig
)
257 int num_connected
= 0;
259 if (nbio_id
!= -1) return;
261 for (i
=0;i
<nprocs
;i
++) {
262 if (children
[i
].connected
) {
265 if (children
[i
].bytes
== 0) {
268 lines
+= children
[i
].line
;
271 t
= timeval_elapsed(&tv_start
);
273 if (!in_warmup
&& warmup
>0 && t
> warmup
) {
274 tv_start
= timeval_current();
276 for (i
=0;i
<nprocs
;i
++) {
277 children
[i
].warmup_bytes
= children
[i
].bytes
;
283 } else if (!in_warmup
&& !in_cleanup
&& t
> timelimit
) {
284 for (i
=0;i
<nprocs
;i
++) {
285 children
[i
].done
= 1;
287 tv_end
= timeval_current();
294 tv_end
= timeval_current();
298 printf("%4d %8d %.2f MB/sec warmup %.0f sec \n",
299 num_connected
, lines
/nprocs
,
301 } else if (in_cleanup
) {
302 printf("%4d %8d %.2f MB/sec cleanup %.0f sec \n",
303 num_connected
, lines
/nprocs
,
306 printf("%4d %8d %.2f MB/sec execute %.0f sec latency %.2f msec \n",
307 num_connected
, lines
/nprocs
,
308 nbio_result(), t
, nbio_latency() * 1.0e3
);
313 signal(SIGALRM
, nb_alarm
);
317 void nbio_shmem(int n
, int t_timelimit
, int t_warmup
)
320 children
= shm_setup(sizeof(*children
) * nprocs
);
322 printf("Failed to setup shared memory!\n");
325 memset(children
, 0, sizeof(*children
) * nprocs
);
326 timelimit
= t_timelimit
;
329 tv_start
= timeval_current();
332 static struct lock_info
* find_lock(struct lock_info
*linfo
, off_t offset
, int size
)
334 while (linfo
!= NULL
) {
335 if (linfo
->offset
== offset
&&
347 static struct ftable
*find_ftable(int handle
)
351 for (f
=ftable
;f
;f
=f
->next
) {
352 if (f
->handle
== handle
) return f
;
357 static int find_handle(int handle
, struct ftable
**f_ret
)
364 children
[nbio_id
].line
= nbench_line_count
;
366 f
= find_ftable(handle
);
372 printf("(%d) ERROR: handle %d was not found\n",
373 nbench_line_count
, handle
);
376 return -1; /* Not reached */
381 static struct smbcli_state
*c
;
384 a handler function for oplock break requests
386 static bool oplock_handler(struct smbcli_transport
*transport
, uint16_t tid
,
387 uint16_t fnum
, uint8_t level
, void *private)
389 struct smbcli_tree
*tree
= (struct smbcli_tree
*)private;
390 return smbcli_oplock_ack(tree
, fnum
, OPLOCK_BREAK_TO_NONE
);
393 void nb_setup(struct smbcli_state
*cli
, int id
)
398 printf("skipping I/O\n");
401 smbcli_oplock_handler(cli
->transport
, oplock_handler
, cli
->tree
);
404 children
[id
].connected
= true;
408 static bool check_status(const char *op
, NTSTATUS status
, NTSTATUS ret
)
410 if ((NT_STATUS_EQUAL(ret
, NT_STATUS_END_OF_FILE
) ||
411 NT_STATUS_EQUAL(ret
, NT_STATUS_NET_WRITE_FAULT
) ||
412 NT_STATUS_EQUAL(ret
, NT_STATUS_CONNECTION_RESET
))
413 && !NT_STATUS_EQUAL (status
, ret
))
418 if (!NT_STATUS_IS_OK(status
) && NT_STATUS_IS_OK(ret
)) {
419 printf("[%d] Error: %s should have failed with %s\n",
420 nbench_line_count
, op
, nt_errstr(status
));
424 if (NT_STATUS_IS_OK(status
) && !NT_STATUS_IS_OK(ret
)) {
425 printf("[%d] Error: %s should have succeeded - %s\n",
426 nbench_line_count
, op
, nt_errstr(ret
));
430 if (!NT_STATUS_EQUAL(status
, ret
)) {
431 printf("[%d] Warning: got status %s but expected %s\n",
432 nbench_line_count
, nt_errstr(ret
), nt_errstr(status
));
439 bool nb_unlink(const char *fname
, int attr
, NTSTATUS status
, bool retry
)
444 io
.unlink
.in
.pattern
= fname
;
446 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
;
447 if (strchr(fname
, '*') == 0) {
448 io
.unlink
.in
.attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
451 ret
= smb_raw_unlink(c
->tree
, &io
);
454 return check_status("Unlink", status
, ret
);
459 static bool nb_do_createx(struct ftable
*f
,
461 uint_t create_options
,
462 uint_t create_disposition
,
468 uint32_t desired_access
;
473 mem_ctx
= talloc_init("raw_open");
475 if (create_options
& NTCREATEX_OPTIONS_DIRECTORY
) {
476 desired_access
= SEC_FILE_READ_DATA
;
480 SEC_FILE_WRITE_DATA
|
481 SEC_FILE_READ_ATTRIBUTE
|
482 SEC_FILE_WRITE_ATTRIBUTE
;
483 flags
= NTCREATEX_FLAGS_EXTENDED
|
484 NTCREATEX_FLAGS_REQUEST_OPLOCK
|
485 NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK
;
488 io
.ntcreatex
.level
= RAW_OPEN_NTCREATEX
;
489 io
.ntcreatex
.in
.flags
= flags
;
490 io
.ntcreatex
.in
.root_fid
= 0;
491 io
.ntcreatex
.in
.access_mask
= desired_access
;
492 io
.ntcreatex
.in
.file_attr
= 0;
493 io
.ntcreatex
.in
.alloc_size
= 0;
494 io
.ntcreatex
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|NTCREATEX_SHARE_ACCESS_WRITE
;
495 io
.ntcreatex
.in
.open_disposition
= create_disposition
;
496 io
.ntcreatex
.in
.create_options
= create_options
;
497 io
.ntcreatex
.in
.impersonation
= 0;
498 io
.ntcreatex
.in
.security_flags
= 0;
499 io
.ntcreatex
.in
.fname
= fname
;
502 /* Reopening after a disconnect. */
503 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
506 f
->cp
.create_disposition
== NTCREATEX_DISP_CREATE
&&
507 NT_STATUS_IS_OK(status
))
509 /* Reopening after nb_createx() error. */
510 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
513 ret
= smb_raw_open(c
->tree
, mem_ctx
, &io
);
515 talloc_free(mem_ctx
);
517 if (!check_status("NTCreateX", status
, ret
))
520 if (!NT_STATUS_IS_OK(ret
))
524 f
= talloc (NULL
, struct ftable
);
526 nb_set_createx_params(f
, fname
, create_options
, create_disposition
, handle
);
527 DLIST_ADD_END(ftable
, f
, struct ftable
*);
531 f
->fd
= io
.ntcreatex
.out
.file
.fnum
;
536 bool nb_createx(const char *fname
,
537 uint_t create_options
, uint_t create_disposition
, int handle
,
540 return nb_do_createx(NULL
, fname
, create_options
, create_disposition
, handle
, status
, false);
543 bool nb_writex(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
550 i
= find_handle(handle
, NULL
);
556 memset(buf
, 0xab, size
);
558 io
.writex
.level
= RAW_WRITE_WRITEX
;
559 io
.writex
.in
.file
.fnum
= i
;
560 io
.writex
.in
.wmode
= 0;
561 io
.writex
.in
.remaining
= 0;
562 io
.writex
.in
.offset
= offset
;
563 io
.writex
.in
.count
= size
;
564 io
.writex
.in
.data
= buf
;
566 ret
= smb_raw_write(c
->tree
, &io
);
570 if (!check_status("WriteX", status
, ret
))
573 if (NT_STATUS_IS_OK(ret
) && io
.writex
.out
.nwritten
!= ret_size
) {
574 printf("[%d] Warning: WriteX got count %d expected %d\n",
576 io
.writex
.out
.nwritten
, ret_size
);
579 children
[nbio_id
].bytes
+= ret_size
;
584 bool nb_write(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
591 i
= find_handle(handle
, NULL
);
598 memset(buf
, 0x12, size
);
600 io
.write
.level
= RAW_WRITE_WRITE
;
601 io
.write
.in
.file
.fnum
= i
;
602 io
.write
.in
.remaining
= 0;
603 io
.write
.in
.offset
= offset
;
604 io
.write
.in
.count
= size
;
605 io
.write
.in
.data
= buf
;
607 ret
= smb_raw_write(c
->tree
, &io
);
611 if (!check_status("Write", status
, ret
))
614 if (NT_STATUS_IS_OK(ret
) && io
.write
.out
.nwritten
!= ret_size
) {
615 printf("[%d] Warning: Write got count %d expected %d\n",
617 io
.write
.out
.nwritten
, ret_size
);
620 children
[nbio_id
].bytes
+= ret_size
;
625 static bool nb_do_lockx(bool relock
, int handle
, off_t offset
, int size
, NTSTATUS status
)
630 struct smb_lock_entry lck
;
633 i
= find_handle(handle
, &f
);
639 io
.lockx
.level
= RAW_LOCK_LOCKX
;
640 io
.lockx
.in
.file
.fnum
= i
;
641 io
.lockx
.in
.mode
= 0;
642 io
.lockx
.in
.timeout
= 0;
643 io
.lockx
.in
.ulock_cnt
= 0;
644 io
.lockx
.in
.lock_cnt
= 1;
645 io
.lockx
.in
.locks
= &lck
;
647 ret
= smb_raw_lock(c
->tree
, &io
);
649 if (!check_status("LockX", status
, ret
))
655 struct lock_info
*linfo
;
656 linfo
= talloc (f
, struct lock_info
);
657 linfo
->offset
= offset
;
659 DLIST_ADD_END(f
->locks
, linfo
, struct lock_info
*);
665 bool nb_lockx(int handle
, off_t offset
, int size
, NTSTATUS status
)
667 return nb_do_lockx(false, handle
, offset
, size
, status
);
670 bool nb_unlockx(int handle
, uint_t offset
, int size
, NTSTATUS status
)
675 struct smb_lock_entry lck
;
678 i
= find_handle(handle
, &f
);
684 io
.lockx
.level
= RAW_LOCK_LOCKX
;
685 io
.lockx
.in
.file
.fnum
= i
;
686 io
.lockx
.in
.mode
= 0;
687 io
.lockx
.in
.timeout
= 0;
688 io
.lockx
.in
.ulock_cnt
= 1;
689 io
.lockx
.in
.lock_cnt
= 0;
690 io
.lockx
.in
.locks
= &lck
;
692 ret
= smb_raw_lock(c
->tree
, &io
);
694 if (!check_status("UnlockX", status
, ret
))
698 struct lock_info
*linfo
;
699 linfo
= find_lock(f
->locks
, offset
, size
);
701 DLIST_REMOVE(f
->locks
, linfo
);
703 printf("nb_unlockx: unknown lock (%d)\n", handle
);
709 bool nb_readx(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
716 i
= find_handle(handle
, NULL
);
723 io
.readx
.level
= RAW_READ_READX
;
724 io
.readx
.in
.file
.fnum
= i
;
725 io
.readx
.in
.offset
= offset
;
726 io
.readx
.in
.mincnt
= size
;
727 io
.readx
.in
.maxcnt
= size
;
728 io
.readx
.in
.remaining
= 0;
729 io
.readx
.in
.read_for_execute
= false;
730 io
.readx
.out
.data
= buf
;
732 ret
= smb_raw_read(c
->tree
, &io
);
736 if (!check_status("ReadX", status
, ret
))
739 if (NT_STATUS_IS_OK(ret
) && io
.readx
.out
.nread
!= ret_size
) {
740 printf("[%d] ERROR: ReadX got count %d expected %d\n",
742 io
.readx
.out
.nread
, ret_size
);
746 children
[nbio_id
].bytes
+= ret_size
;
751 bool nb_close(int handle
, NTSTATUS status
)
757 i
= find_handle(handle
, NULL
);
759 io
.close
.level
= RAW_CLOSE_CLOSE
;
760 io
.close
.in
.file
.fnum
= i
;
761 io
.close
.in
.write_time
= 0;
763 ret
= smb_raw_close(c
->tree
, &io
);
765 if (!check_status("Close", status
, ret
))
768 if (NT_STATUS_IS_OK(ret
)) {
769 struct ftable
*f
= find_ftable(handle
);
770 DLIST_REMOVE(ftable
, f
);
777 bool nb_rmdir(const char *dname
, NTSTATUS status
, bool retry
)
784 ret
= smb_raw_rmdir(c
->tree
, &io
);
787 return check_status("Rmdir", status
, ret
);
792 bool nb_mkdir(const char *dname
, NTSTATUS status
, bool retry
)
796 io
.mkdir
.level
= RAW_MKDIR_MKDIR
;
797 io
.mkdir
.in
.path
= dname
;
799 /* NOTE! no error checking. Used for base fileset creation */
800 smb_raw_mkdir(c
->tree
, &io
);
805 bool nb_rename(const char *old
, const char *new, NTSTATUS status
, bool retry
)
810 io
.generic
.level
= RAW_RENAME_RENAME
;
811 io
.rename
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_DIRECTORY
;
812 io
.rename
.in
.pattern1
= old
;
813 io
.rename
.in
.pattern2
= new;
815 ret
= smb_raw_rename(c
->tree
, &io
);
818 return check_status("Rename", status
, ret
);
824 bool nb_qpathinfo(const char *fname
, int level
, NTSTATUS status
)
826 union smb_fileinfo io
;
830 mem_ctx
= talloc_init("nb_qpathinfo");
832 io
.generic
.level
= level
;
833 io
.generic
.in
.file
.path
= fname
;
835 ret
= smb_raw_pathinfo(c
->tree
, mem_ctx
, &io
);
837 talloc_free(mem_ctx
);
839 return check_status("Pathinfo", status
, ret
);
843 bool nb_qfileinfo(int fnum
, int level
, NTSTATUS status
)
845 union smb_fileinfo io
;
850 i
= find_handle(fnum
, NULL
);
852 mem_ctx
= talloc_init("nb_qfileinfo");
854 io
.generic
.level
= level
;
855 io
.generic
.in
.file
.fnum
= i
;
857 ret
= smb_raw_fileinfo(c
->tree
, mem_ctx
, &io
);
859 talloc_free(mem_ctx
);
861 return check_status("Fileinfo", status
, ret
);
864 bool nb_sfileinfo(int fnum
, int level
, NTSTATUS status
)
866 union smb_setfileinfo io
;
870 if (level
!= RAW_SFILEINFO_BASIC_INFORMATION
) {
871 printf("[%d] Warning: setfileinfo level %d not handled\n", nbench_line_count
, level
);
877 i
= find_handle(fnum
, NULL
);
879 io
.generic
.level
= level
;
880 io
.generic
.in
.file
.fnum
= i
;
881 unix_to_nt_time(&io
.basic_info
.in
.create_time
, time(NULL
));
882 unix_to_nt_time(&io
.basic_info
.in
.access_time
, 0);
883 unix_to_nt_time(&io
.basic_info
.in
.write_time
, 0);
884 unix_to_nt_time(&io
.basic_info
.in
.change_time
, 0);
885 io
.basic_info
.in
.attrib
= 0;
887 ret
= smb_raw_setfileinfo(c
->tree
, &io
);
889 return check_status("Setfileinfo", status
, ret
);
892 bool nb_qfsinfo(int level
, NTSTATUS status
)
898 mem_ctx
= talloc_init("smbcli_dskattr");
900 io
.generic
.level
= level
;
901 ret
= smb_raw_fsinfo(c
->tree
, mem_ctx
, &io
);
903 talloc_free(mem_ctx
);
905 return check_status("Fsinfo", status
, ret
);
908 /* callback function used for trans2 search */
909 static bool findfirst_callback(void *private, const union smb_search_data
*file
)
914 bool nb_findfirst(const char *mask
, int level
, int maxcnt
, int count
, NTSTATUS status
)
916 union smb_search_first io
;
920 mem_ctx
= talloc_init("smbcli_dskattr");
922 io
.t2ffirst
.level
= RAW_SEARCH_TRANS2
;
923 io
.t2ffirst
.data_level
= level
;
924 io
.t2ffirst
.in
.max_count
= maxcnt
;
925 io
.t2ffirst
.in
.search_attrib
= FILE_ATTRIBUTE_DIRECTORY
;
926 io
.t2ffirst
.in
.pattern
= mask
;
927 io
.t2ffirst
.in
.flags
= FLAG_TRANS2_FIND_CLOSE
;
928 io
.t2ffirst
.in
.storage_type
= 0;
930 ret
= smb_raw_search_first(c
->tree
, mem_ctx
, &io
, NULL
, findfirst_callback
);
932 talloc_free(mem_ctx
);
934 if (!check_status("Search", status
, ret
))
937 if (NT_STATUS_IS_OK(ret
) && io
.t2ffirst
.out
.count
!= count
) {
938 printf("[%d] Warning: got count %d expected %d\n",
940 io
.t2ffirst
.out
.count
, count
);
946 bool nb_flush(int fnum
, NTSTATUS status
)
951 i
= find_handle(fnum
, NULL
);
953 io
.flush
.level
= RAW_FLUSH_FLUSH
;
954 io
.flush
.in
.file
.fnum
= i
;
956 ret
= smb_raw_flush(c
->tree
, &io
);
958 return check_status("Flush", status
, ret
);
961 void nb_sleep(int usec
, NTSTATUS status
)
966 bool nb_deltree(const char *dname
, bool retry
)
970 smb_raw_exit(c
->session
);
973 struct ftable
*f
= ftable
;
974 DLIST_REMOVE(ftable
, f
);
978 total_deleted
= smbcli_deltree(c
->tree
, dname
);
980 if (total_deleted
== -1) {
981 printf("Failed to cleanup tree %s - exiting\n", dname
);
985 smbcli_rmdir(c
->tree
, dname
);
991 void nb_exit(int status
)
993 children
[nbio_id
].connected
= false;
994 printf("[%d] client %d exiting with status %d\n",
995 nbench_line_count
, nbio_id
, status
);