2 Unix SMB/CIFS implementation.
3 SMB read/write torture tester
4 Copyright (C) Andrew Tridgell 1997-2003
5 Copyright (C) Jelmer Vernooij 2006
6 Copyright (C) David Mulder 2019
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "torture/smbtorture.h"
23 #include "libcli/smb2/smb2.h"
24 #include "libcli/smb2/smb2_calls.h"
25 #include "torture/torture.h"
26 #include "torture/util.h"
27 #include "torture/smb2/proto.h"
29 #define CHECK_STATUS(_status, _expected) \
30 torture_assert_ntstatus_equal_goto(torture, _status, _expected, \
31 ret, done, "Incorrect status")
33 #define CHECK_VALUE(v, correct) \
34 torture_assert_int_equal_goto(torture, v, correct, \
35 ret, done, "Incorrect value")
37 #define FNAME "smb2_writetest.dat"
39 static bool run_smb2_readwritetest(struct torture_context
*tctx
,
40 struct smb2_tree
*t1
, struct smb2_tree
*t2
)
42 const char *lockfname
= "torture2.lck";
43 struct smb2_create f1
= {0};
44 struct smb2_create f2
= {0};
45 struct smb2_handle h1
= {{0}};
46 struct smb2_handle h2
= {{0}};
53 ret
= smb2_deltree(t1
, lockfname
);
54 torture_assert(tctx
, ret
!= -1, "unlink failed");
56 f1
.in
.desired_access
= SEC_FILE_ALL
;
57 f1
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
58 NTCREATEX_SHARE_ACCESS_WRITE
;
59 f1
.in
.create_disposition
= FILE_CREATE
;
60 f1
.in
.fname
= lockfname
;
62 status
= smb2_create(t1
, tctx
, &f1
);
63 torture_assert_ntstatus_ok_goto(tctx
, status
, correct
, done
,
64 talloc_asprintf(tctx
, "first open read/write of %s failed (%s)",
65 lockfname
, nt_errstr(status
)));
66 h1
= f1
.out
.file
.handle
;
68 f2
.in
.desired_access
= SEC_FILE_READ_DATA
;
69 f2
.in
.share_access
= NTCREATEX_SHARE_ACCESS_READ
|
70 NTCREATEX_SHARE_ACCESS_WRITE
;
71 f2
.in
.create_disposition
= FILE_OPEN
;
72 f2
.in
.fname
= lockfname
;
74 status
= smb2_create(t2
, tctx
, &f2
);
75 torture_assert_ntstatus_ok_goto(tctx
, status
, correct
, done
,
76 talloc_asprintf(tctx
, "second open read-only of %s failed (%s)",
77 lockfname
, nt_errstr(status
)));
78 h2
= f2
.out
.file
.handle
;
80 torture_comment(tctx
, "Checking data integrity over %d ops\n",
83 for (i
= 0; i
< torture_numops
; i
++) {
84 struct smb2_write w
= {0};
85 struct smb2_read r
= {0};
86 size_t buf_size
= ((unsigned int)random()%(sizeof(buf
)-1))+ 1;
89 if (torture_setting_bool(tctx
, "progress", true)) {
90 torture_comment(tctx
, "%d\r", i
); fflush(stdout
);
94 generate_random_buffer(buf
, buf_size
);
96 w
.in
.file
.handle
= h1
;
99 w
.in
.data
.length
= buf_size
;
101 status
= smb2_write(t1
, &w
);
102 if (!NT_STATUS_IS_OK(status
) || w
.out
.nwritten
!= buf_size
) {
103 torture_comment(tctx
, "write failed (%s)\n",
105 torture_result(tctx
, TORTURE_FAIL
,
106 "wrote %d, expected %d\n",
107 (int)w
.out
.nwritten
, (int)buf_size
);
112 r
.in
.file
.handle
= h2
;
114 r
.in
.length
= buf_size
;
115 status
= smb2_read(t2
, tctx
, &r
);
116 if (!NT_STATUS_IS_OK(status
) || r
.out
.data
.length
!= buf_size
) {
117 torture_comment(tctx
, "read failed (%s)\n",
119 torture_result(tctx
, TORTURE_FAIL
,
120 "read %d, expected %d\n",
121 (int)r
.out
.data
.length
, (int)buf_size
);
126 torture_assert_mem_equal_goto(tctx
, r
.out
.data
.data
, buf
,
127 buf_size
, correct
, done
, "read/write compare failed\n");
130 status
= smb2_util_close(t2
, h2
);
131 torture_assert_ntstatus_ok_goto(tctx
, status
, correct
, done
,
132 talloc_asprintf(tctx
, "close failed (%s)", nt_errstr(status
)));
135 status
= smb2_util_close(t1
, h1
);
136 torture_assert_ntstatus_ok_goto(tctx
, status
, correct
, done
,
137 talloc_asprintf(tctx
, "close failed (%s)", nt_errstr(status
)));
141 if (!smb2_util_handle_empty(h2
)) {
142 smb2_util_close(t2
, h2
);
144 if (!smb2_util_handle_empty(h1
)) {
145 smb2_util_close(t1
, h1
);
148 status
= smb2_util_unlink(t1
, lockfname
);
149 if (!NT_STATUS_IS_OK(status
)) {
150 torture_comment(tctx
, "unlink failed (%s)", nt_errstr(status
));
157 static bool run_smb2_wrap_readwritetest(struct torture_context
*tctx
,
158 struct smb2_tree
*tree1
,
159 struct smb2_tree
*tree2
)
161 return run_smb2_readwritetest(tctx
, tree1
, tree1
);
164 static bool test_rw_invalid(struct torture_context
*torture
, struct smb2_tree
*tree
)
168 struct smb2_handle h
;
169 uint8_t buf
[64*1024];
171 struct smb2_write w
= {0};
172 union smb_setfileinfo sfinfo
;
173 TALLOC_CTX
*tmp_ctx
= talloc_new(tree
);
177 smb2_util_unlink(tree
, FNAME
);
179 status
= torture_smb2_testfile(tree
, FNAME
, &h
);
180 CHECK_STATUS(status
, NT_STATUS_OK
);
182 /* set delete-on-close */
184 sfinfo
.generic
.level
= RAW_SFILEINFO_DISPOSITION_INFORMATION
;
185 sfinfo
.disposition_info
.in
.delete_on_close
= 1;
186 sfinfo
.generic
.in
.file
.handle
= h
;
187 status
= smb2_setinfo_file(tree
, &sfinfo
);
188 CHECK_STATUS(status
, NT_STATUS_OK
);
190 status
= smb2_util_write(tree
, h
, buf
, 0, ARRAY_SIZE(buf
));
191 CHECK_STATUS(status
, NT_STATUS_OK
);
194 rd
.in
.file
.handle
= h
;
199 status
= smb2_read(tree
, tmp_ctx
, &rd
);
200 CHECK_STATUS(status
, NT_STATUS_OK
);
201 CHECK_VALUE(rd
.out
.data
.length
, 10);
205 rd
.in
.offset
= sizeof(buf
);
206 status
= smb2_read(tree
, tmp_ctx
, &rd
);
207 CHECK_STATUS(status
, NT_STATUS_END_OF_FILE
);
211 rd
.in
.offset
= sizeof(buf
);
212 status
= smb2_read(tree
, tmp_ctx
, &rd
);
213 CHECK_STATUS(status
, NT_STATUS_OK
);
214 CHECK_VALUE(rd
.out
.data
.length
, 0);
218 rd
.in
.offset
= INT64_MAX
- 1;
219 status
= smb2_read(tree
, tmp_ctx
, &rd
);
220 CHECK_STATUS(status
, NT_STATUS_END_OF_FILE
);
224 rd
.in
.offset
= INT64_MAX
;
225 status
= smb2_read(tree
, tmp_ctx
, &rd
);
226 CHECK_STATUS(status
, NT_STATUS_OK
);
227 CHECK_VALUE(rd
.out
.data
.length
, 0);
231 rd
.in
.offset
= INT64_MAX
;
232 status
= smb2_read(tree
, tmp_ctx
, &rd
);
233 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
237 rd
.in
.offset
= (uint64_t)INT64_MAX
+ 1;
238 status
= smb2_read(tree
, tmp_ctx
, &rd
);
239 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
243 rd
.in
.offset
= (uint64_t)INT64_MIN
;
244 status
= smb2_read(tree
, tmp_ctx
, &rd
);
245 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
249 rd
.in
.offset
= (uint64_t)(int64_t)-1;
250 status
= smb2_read(tree
, tmp_ctx
, &rd
);
251 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
255 rd
.in
.offset
= (uint64_t)(int64_t)-2;
256 status
= smb2_read(tree
, tmp_ctx
, &rd
);
257 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
261 rd
.in
.offset
= (uint64_t)(int64_t)-3;
262 status
= smb2_read(tree
, tmp_ctx
, &rd
);
263 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
265 w
.in
.file
.handle
= h
;
266 w
.in
.offset
= (int64_t)-1;
267 w
.in
.data
.data
= buf
;
268 w
.in
.data
.length
= ARRAY_SIZE(buf
);
270 status
= smb2_write(tree
, &w
);
271 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
273 w
.in
.file
.handle
= h
;
274 w
.in
.offset
= (int64_t)-2;
275 w
.in
.data
.data
= buf
;
276 w
.in
.data
.length
= ARRAY_SIZE(buf
);
278 status
= smb2_write(tree
, &w
);
279 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
281 w
.in
.file
.handle
= h
;
282 w
.in
.offset
= INT64_MIN
;
283 w
.in
.data
.data
= buf
;
284 w
.in
.data
.length
= 1;
286 status
= smb2_write(tree
, &w
);
287 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
289 w
.in
.file
.handle
= h
;
290 w
.in
.offset
= INT64_MIN
;
291 w
.in
.data
.data
= buf
;
292 w
.in
.data
.length
= 0;
293 status
= smb2_write(tree
, &w
);
294 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
296 w
.in
.file
.handle
= h
;
297 w
.in
.offset
= INT64_MAX
;
298 w
.in
.data
.data
= buf
;
299 w
.in
.data
.length
= 0;
300 status
= smb2_write(tree
, &w
);
301 CHECK_STATUS(status
, NT_STATUS_OK
);
302 CHECK_VALUE(w
.out
.nwritten
, 0);
304 w
.in
.file
.handle
= h
;
305 w
.in
.offset
= INT64_MAX
;
306 w
.in
.data
.data
= buf
;
307 w
.in
.data
.length
= 1;
308 status
= smb2_write(tree
, &w
);
309 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
311 w
.in
.file
.handle
= h
;
312 w
.in
.offset
= (uint64_t)INT64_MAX
+ 1;
313 w
.in
.data
.data
= buf
;
314 w
.in
.data
.length
= 0;
315 status
= smb2_write(tree
, &w
);
316 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
318 w
.in
.file
.handle
= h
;
319 w
.in
.offset
= 0xfffffff0000; /* MAXFILESIZE */
320 w
.in
.data
.data
= buf
;
321 w
.in
.data
.length
= 1;
322 status
= smb2_write(tree
, &w
);
323 CHECK_STATUS(status
, NT_STATUS_INVALID_PARAMETER
);
325 w
.in
.file
.handle
= h
;
326 w
.in
.offset
= 0xfffffff0000 - 1; /* MAXFILESIZE - 1 */
327 w
.in
.data
.data
= buf
;
328 w
.in
.data
.length
= 1;
329 status
= smb2_write(tree
, &w
);
330 if (TARGET_IS_SAMBA3(torture
) || TARGET_IS_SAMBA4(torture
)) {
331 CHECK_STATUS(status
, NT_STATUS_OK
);
332 CHECK_VALUE(w
.out
.nwritten
, 1);
334 CHECK_STATUS(status
, NT_STATUS_DISK_FULL
);
337 w
.in
.file
.handle
= h
;
338 w
.in
.offset
= 0xfffffff0000; /* MAXFILESIZE */
339 w
.in
.data
.data
= buf
;
340 w
.in
.data
.length
= 0;
341 status
= smb2_write(tree
, &w
);
342 CHECK_STATUS(status
, NT_STATUS_OK
);
343 CHECK_VALUE(w
.out
.nwritten
, 0);
346 talloc_free(tmp_ctx
);
350 struct torture_suite
*torture_smb2_readwrite_init(TALLOC_CTX
*ctx
)
352 struct torture_suite
*suite
= torture_suite_create(ctx
, "rw");
354 torture_suite_add_2smb2_test(suite
, "rw1", run_smb2_readwritetest
);
355 torture_suite_add_2smb2_test(suite
, "rw2", run_smb2_wrap_readwritetest
);
356 torture_suite_add_1smb2_test(suite
, "invalid", test_rw_invalid
);
358 suite
->description
= talloc_strdup(suite
, "SMB2 Samba4 Read/Write");