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 "torture/util.h"
31 #include "torture/nbench/proto.h"
33 extern int nbench_line_count
;
34 static int nbio_id
= -1;
36 static bool bypass_io
;
37 static struct timeval tv_start
, tv_end
;
38 static int warmup
, timelimit
;
39 static int in_cleanup
;
42 struct lock_info
*next
, *prev
;
47 struct createx_params
{
49 unsigned int create_options
;
50 unsigned int create_disposition
;
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
;
62 static struct ftable
*ftable
;
65 double bytes
, warmup_bytes
;
70 struct timeval starttime
;
73 static bool nb_do_createx(struct ftable
*f
,
75 unsigned int create_options
,
76 unsigned int create_disposition
,
81 static bool nb_do_lockx(bool relock
, int handle
, off_t offset
, int size
, NTSTATUS status
);
83 static void nb_set_createx_params(struct ftable
*f
,
85 unsigned int create_options
,
86 unsigned int create_disposition
,
89 struct createx_params
*cp
= &f
->cp
;
92 cp
->fname
= talloc_strdup(f
, fname
);
93 if (cp
->fname
== NULL
) {
94 perror("nb_set_createx_params: strdup");
101 cp
->create_options
= create_options
;
102 cp
->create_disposition
= create_disposition
;
106 static bool nb_reestablish_locks(struct ftable
*f
)
108 struct lock_info
*linfo
= f
->locks
;
110 while (linfo
!= NULL
) {
111 DEBUG(1,("nb_reestablish_locks: lock for file %d at %lu\n",
112 f
->handle
, (unsigned long) linfo
->offset
));
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
);
126 static bool nb_reopen_all_files(void)
128 struct ftable
*f
= ftable
;
131 DEBUG(1,("-- nb_reopen_all_files: opening %s (handle %d)\n",
132 f
->cp
.fname
, f
->cp
.handle
));
134 if (!nb_do_createx(f
,
136 f
->cp
.create_options
,
137 f
->cp
.create_disposition
,
142 printf("-- nb_reopen_all_files: failed to open file %s\n", f
->cp
.fname
);
146 if (!nb_reestablish_locks(f
)) {
147 printf("--nb_reopen_all_files: failed to reestablish locks\n");
157 bool nb_reconnect(struct smbcli_state
**cli
, struct torture_context
*tctx
, int client
)
159 children
[client
].connected
= false;
165 if (!torture_open_connection(cli
, tctx
, client
)) {
166 printf("nb_reconnect: failed to connect\n");
171 nb_setup(*cli
, client
);
173 if (!nb_reopen_all_files()) {
174 printf("nb_reconnect: failed to reopen files in client %d\n", client
);
181 void nbio_target_rate(double rate
)
183 static double last_bytes
;
184 static struct timeval last_time
;
187 if (last_bytes
== 0) {
188 last_bytes
= children
[nbio_id
].bytes
;
189 last_time
= timeval_current();
193 tdelay
= (children
[nbio_id
].bytes
- last_bytes
)/(1.0e6
*rate
) - timeval_elapsed(&last_time
);
195 smb_msleep(tdelay
*1000);
197 children
[nbio_id
].max_latency
= MAX(children
[nbio_id
].max_latency
, -tdelay
);
200 last_time
= timeval_current();
201 last_bytes
= children
[nbio_id
].bytes
;
204 void nbio_time_reset(void)
206 children
[nbio_id
].starttime
= timeval_current();
209 void nbio_time_delay(double targett
)
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
);
219 double nbio_result(void)
223 for (i
=0;i
<nprocs
;i
++) {
224 total
+= children
[i
].bytes
- children
[i
].warmup_bytes
;
226 return 1.0e-6 * total
/ timeval_elapsed2(&tv_start
, &tv_end
);
229 double nbio_latency(void)
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;
244 return children
[nbio_id
].done
;
248 void nb_alarm(int sig
)
254 int num_connected
= 0;
256 if (nbio_id
!= -1) return;
258 for (i
=0;i
<nprocs
;i
++) {
259 if (children
[i
].connected
) {
262 if (children
[i
].bytes
== 0) {
265 lines
+= children
[i
].line
;
268 t
= timeval_elapsed(&tv_start
);
270 if (!in_warmup
&& warmup
>0 && t
> warmup
) {
271 tv_start
= timeval_current();
273 for (i
=0;i
<nprocs
;i
++) {
274 children
[i
].warmup_bytes
= children
[i
].bytes
;
280 } else if (!in_warmup
&& !in_cleanup
&& t
> timelimit
) {
281 for (i
=0;i
<nprocs
;i
++) {
282 children
[i
].done
= 1;
284 tv_end
= timeval_current();
291 tv_end
= timeval_current();
295 printf("%4d %8d %.2f MB/sec warmup %.0f sec \n",
296 num_connected
, lines
/nprocs
,
298 } else if (in_cleanup
) {
299 printf("%4d %8d %.2f MB/sec cleanup %.0f sec \n",
300 num_connected
, lines
/nprocs
,
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
);
310 signal(SIGALRM
, nb_alarm
);
314 void nbio_shmem(int n
, int t_timelimit
, int t_warmup
)
317 children
= anonymous_shared_allocate(sizeof(*children
) * nprocs
);
319 printf("Failed to setup shared memory!\n");
322 memset(children
, 0, sizeof(*children
) * nprocs
);
323 timelimit
= t_timelimit
;
326 tv_start
= timeval_current();
329 static struct lock_info
* find_lock(struct lock_info
*linfo
, off_t offset
, int size
)
331 while (linfo
!= NULL
) {
332 if (linfo
->offset
== offset
&&
344 static struct ftable
*find_ftable(int handle
)
348 for (f
=ftable
;f
;f
=f
->next
) {
349 if (f
->handle
== handle
) return f
;
354 static int find_handle(int handle
, struct ftable
**f_ret
)
361 children
[nbio_id
].line
= nbench_line_count
;
363 f
= find_ftable(handle
);
369 printf("(%d) ERROR: handle %d was not found\n",
370 nbench_line_count
, handle
);
373 return -1; /* Not reached */
378 static struct smbcli_state
*c
;
381 a handler function for oplock break requests
383 static bool oplock_handler(struct smbcli_transport
*transport
, uint16_t tid
,
384 uint16_t fnum
, uint8_t level
, void *private_data
)
386 struct smbcli_tree
*tree
= (struct smbcli_tree
*)private_data
;
387 return smbcli_oplock_ack(tree
, fnum
, OPLOCK_BREAK_TO_NONE
);
390 void nb_setup(struct smbcli_state
*cli
, int id
)
395 printf("skipping I/O\n");
398 smbcli_oplock_handler(cli
->transport
, oplock_handler
, cli
->tree
);
401 children
[id
].connected
= true;
405 static bool check_status(const char *op
, NTSTATUS status
, NTSTATUS ret
)
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
))
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
));
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
));
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
));
436 bool nb_unlink(const char *fname
, int attr
, NTSTATUS status
, bool retry
)
441 io
.unlink
.in
.pattern
= fname
;
443 io
.unlink
.in
.attrib
= FILE_ATTRIBUTE_SYSTEM
| FILE_ATTRIBUTE_HIDDEN
;
444 if (strchr(fname
, '*') == 0) {
445 io
.unlink
.in
.attrib
|= FILE_ATTRIBUTE_DIRECTORY
;
448 ret
= smb_raw_unlink(c
->tree
, &io
);
451 return check_status("Unlink", status
, ret
);
456 static bool nb_do_createx(struct ftable
*f
,
458 unsigned int create_options
,
459 unsigned int create_disposition
,
465 uint32_t desired_access
;
468 unsigned int flags
= 0;
470 mem_ctx
= talloc_init("raw_open");
472 if (create_options
& NTCREATEX_OPTIONS_DIRECTORY
) {
473 desired_access
= 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
;
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
;
499 /* Reopening after a disconnect. */
500 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
503 f
->cp
.create_disposition
== NTCREATEX_DISP_CREATE
&&
504 NT_STATUS_IS_OK(status
))
506 /* Reopening after nb_createx() error. */
507 io
.ntcreatex
.in
.open_disposition
= NTCREATEX_DISP_OPEN_IF
;
510 ret
= smb_raw_open(c
->tree
, mem_ctx
, &io
);
512 talloc_free(mem_ctx
);
514 if (!check_status("NTCreateX", status
, ret
))
517 if (!NT_STATUS_IS_OK(ret
))
521 f
= talloc (NULL
, struct ftable
);
523 nb_set_createx_params(f
, fname
, create_options
, create_disposition
, handle
);
524 DLIST_ADD_END(ftable
, f
, struct ftable
*);
528 f
->fd
= io
.ntcreatex
.out
.file
.fnum
;
533 bool nb_createx(const char *fname
,
534 unsigned int create_options
, unsigned int create_disposition
, int handle
,
537 return nb_do_createx(NULL
, fname
, create_options
, create_disposition
, handle
, status
, false);
540 bool nb_writex(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
547 i
= find_handle(handle
, NULL
);
553 memset(buf
, 0xab, size
);
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
;
563 ret
= smb_raw_write(c
->tree
, &io
);
567 if (!check_status("WriteX", status
, ret
))
570 if (NT_STATUS_IS_OK(ret
) && io
.writex
.out
.nwritten
!= ret_size
) {
571 printf("[%d] Warning: WriteX got count %d expected %d\n",
573 io
.writex
.out
.nwritten
, ret_size
);
576 children
[nbio_id
].bytes
+= ret_size
;
581 bool nb_write(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
588 i
= find_handle(handle
, NULL
);
595 memset(buf
, 0x12, size
);
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
;
604 ret
= smb_raw_write(c
->tree
, &io
);
608 if (!check_status("Write", status
, ret
))
611 if (NT_STATUS_IS_OK(ret
) && io
.write
.out
.nwritten
!= ret_size
) {
612 printf("[%d] Warning: Write got count %d expected %d\n",
614 io
.write
.out
.nwritten
, ret_size
);
617 children
[nbio_id
].bytes
+= ret_size
;
622 static bool nb_do_lockx(bool relock
, int handle
, off_t offset
, int size
, NTSTATUS status
)
627 struct smb_lock_entry lck
;
630 i
= find_handle(handle
, &f
);
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
;
644 ret
= smb_raw_lock(c
->tree
, &io
);
646 if (!check_status("LockX", status
, ret
))
652 struct lock_info
*linfo
;
653 linfo
= talloc (f
, struct lock_info
);
654 linfo
->offset
= offset
;
656 DLIST_ADD_END(f
->locks
, linfo
, struct lock_info
*);
662 bool nb_lockx(int handle
, off_t offset
, int size
, NTSTATUS status
)
664 return nb_do_lockx(false, handle
, offset
, size
, status
);
667 bool nb_unlockx(int handle
, unsigned int offset
, int size
, NTSTATUS status
)
672 struct smb_lock_entry lck
;
675 i
= find_handle(handle
, &f
);
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
;
689 ret
= smb_raw_lock(c
->tree
, &io
);
691 if (!check_status("UnlockX", status
, ret
))
695 struct lock_info
*linfo
;
696 linfo
= find_lock(f
->locks
, offset
, size
);
698 DLIST_REMOVE(f
->locks
, linfo
);
700 printf("nb_unlockx: unknown lock (%d)\n", handle
);
706 bool nb_readx(int handle
, off_t offset
, int size
, int ret_size
, NTSTATUS status
)
713 i
= find_handle(handle
, NULL
);
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
;
729 ret
= smb_raw_read(c
->tree
, &io
);
733 if (!check_status("ReadX", status
, ret
))
736 if (NT_STATUS_IS_OK(ret
) && io
.readx
.out
.nread
!= ret_size
) {
737 printf("[%d] ERROR: ReadX got count %d expected %d\n",
739 io
.readx
.out
.nread
, ret_size
);
743 children
[nbio_id
].bytes
+= ret_size
;
748 bool nb_close(int handle
, NTSTATUS status
)
754 i
= find_handle(handle
, NULL
);
756 io
.close
.level
= RAW_CLOSE_CLOSE
;
757 io
.close
.in
.file
.fnum
= i
;
758 io
.close
.in
.write_time
= 0;
760 ret
= smb_raw_close(c
->tree
, &io
);
762 if (!check_status("Close", status
, ret
))
765 if (NT_STATUS_IS_OK(ret
)) {
766 struct ftable
*f
= find_ftable(handle
);
767 DLIST_REMOVE(ftable
, f
);
774 bool nb_rmdir(const char *dname
, NTSTATUS status
, bool retry
)
781 ret
= smb_raw_rmdir(c
->tree
, &io
);
784 return check_status("Rmdir", status
, ret
);
789 bool nb_mkdir(const char *dname
, NTSTATUS status
, bool retry
)
793 io
.mkdir
.level
= RAW_MKDIR_MKDIR
;
794 io
.mkdir
.in
.path
= dname
;
796 /* NOTE! no error checking. Used for base fileset creation */
797 smb_raw_mkdir(c
->tree
, &io
);
802 bool nb_rename(const char *o
, const char *n
, NTSTATUS status
, bool retry
)
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
;
812 ret
= smb_raw_rename(c
->tree
, &io
);
815 return check_status("Rename", status
, ret
);
821 bool nb_qpathinfo(const char *fname
, int level
, NTSTATUS status
)
823 union smb_fileinfo io
;
827 mem_ctx
= talloc_init("nb_qpathinfo");
829 io
.generic
.level
= level
;
830 io
.generic
.in
.file
.path
= fname
;
832 ret
= smb_raw_pathinfo(c
->tree
, mem_ctx
, &io
);
834 talloc_free(mem_ctx
);
836 return check_status("Pathinfo", status
, ret
);
840 bool nb_qfileinfo(int fnum
, int level
, NTSTATUS status
)
842 union smb_fileinfo io
;
847 i
= find_handle(fnum
, NULL
);
849 mem_ctx
= talloc_init("nb_qfileinfo");
851 io
.generic
.level
= level
;
852 io
.generic
.in
.file
.fnum
= i
;
854 ret
= smb_raw_fileinfo(c
->tree
, mem_ctx
, &io
);
856 talloc_free(mem_ctx
);
858 return check_status("Fileinfo", status
, ret
);
861 bool nb_sfileinfo(int fnum
, int level
, NTSTATUS status
)
863 union smb_setfileinfo io
;
867 if (level
!= RAW_SFILEINFO_BASIC_INFORMATION
) {
868 printf("[%d] Warning: setfileinfo level %d not handled\n", nbench_line_count
, level
);
874 i
= find_handle(fnum
, NULL
);
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;
884 ret
= smb_raw_setfileinfo(c
->tree
, &io
);
886 return check_status("Setfileinfo", status
, ret
);
889 bool nb_qfsinfo(int level
, NTSTATUS status
)
895 mem_ctx
= talloc_init("smbcli_dskattr");
897 io
.generic
.level
= level
;
898 ret
= smb_raw_fsinfo(c
->tree
, mem_ctx
, &io
);
900 talloc_free(mem_ctx
);
902 return check_status("Fsinfo", status
, ret
);
905 /* callback function used for trans2 search */
906 static bool findfirst_callback(void *private_data
, const union smb_search_data
*file
)
911 bool nb_findfirst(const char *mask
, int level
, int maxcnt
, int count
, NTSTATUS status
)
913 union smb_search_first io
;
917 mem_ctx
= talloc_init("smbcli_dskattr");
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;
927 ret
= smb_raw_search_first(c
->tree
, mem_ctx
, &io
, NULL
, findfirst_callback
);
929 talloc_free(mem_ctx
);
931 if (!check_status("Search", status
, ret
))
934 if (NT_STATUS_IS_OK(ret
) && io
.t2ffirst
.out
.count
!= count
) {
935 printf("[%d] Warning: got count %d expected %d\n",
937 io
.t2ffirst
.out
.count
, count
);
943 bool nb_flush(int fnum
, NTSTATUS status
)
948 i
= find_handle(fnum
, NULL
);
950 io
.flush
.level
= RAW_FLUSH_FLUSH
;
951 io
.flush
.in
.file
.fnum
= i
;
953 ret
= smb_raw_flush(c
->tree
, &io
);
955 return check_status("Flush", status
, ret
);
958 void nb_sleep(int usec
, NTSTATUS status
)
963 bool nb_deltree(const char *dname
, bool retry
)
967 smb_raw_exit(c
->session
);
970 struct ftable
*f
= ftable
;
971 DLIST_REMOVE(ftable
, f
);
975 total_deleted
= smbcli_deltree(c
->tree
, dname
);
977 if (total_deleted
== -1) {
978 printf("Failed to cleanup tree %s - exiting\n", dname
);
982 smbcli_rmdir(c
->tree
, dname
);
988 void nb_exit(int status
)
990 children
[nbio_id
].connected
= false;
991 printf("[%d] client %d exiting with status %d\n",
992 nbench_line_count
, nbio_id
, status
);