2 * Unix SMB/CIFS implementation.
3 * Copyright (C) Volker Lendecke 2020
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "torture/proto.h"
21 #include "libsmb/libsmb.h"
22 #include "libsmb/clirap.h"
23 #include "lib/util/tevent_ntstatus.h"
24 #include "lib/util/smb_strtox.h"
26 extern int torture_nprocs
;
27 extern int torture_numops
;
29 struct create_ts_state
{
30 struct tevent_context
*ev
;
31 struct cli_state
*cli
;
32 unsigned timestamp_idx
;
36 static void create_ts_opened(struct tevent_req
*subreq
);
37 static void create_ts_setinfo_done(struct tevent_req
*subreq
);
38 static void create_ts_waited(struct tevent_req
*subreq
);
39 static void create_ts_written(struct tevent_req
*subreq
);
40 static void create_ts_doc_done(struct tevent_req
*subreq
);
42 static struct tevent_req
*create_ts_send(
44 struct tevent_context
*ev
,
45 struct cli_state
*cli
,
47 unsigned timestamp_idx
)
49 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
50 struct create_ts_state
*state
= NULL
;
52 req
= tevent_req_create(mem_ctx
, &state
, struct create_ts_state
);
58 state
->timestamp_idx
= timestamp_idx
;
60 subreq
= cli_ntcreate_send(
66 SEC_FILE_WRITE_ATTRIBUTE
|
68 SEC_STD_DELETE
, /* DesiredAccess */
69 FILE_ATTRIBUTE_NORMAL
, /* FileAttributes */
70 FILE_SHARE_WRITE
|FILE_SHARE_READ
, /* ShareAccess */
71 FILE_OPEN_IF
, /* CreateDisposition */
72 FILE_NON_DIRECTORY_FILE
, /* CreateOptions */
73 0, /* Impersonation */
74 0); /* SecurityFlags */
75 if (tevent_req_nomem(subreq
, req
)) {
76 return tevent_req_post(req
, ev
);
78 tevent_req_set_callback(subreq
, create_ts_opened
, req
);
82 static void create_ts_opened(struct tevent_req
*subreq
)
84 struct tevent_req
*req
= tevent_req_callback_data(
85 subreq
, struct tevent_req
);
86 struct create_ts_state
*state
= tevent_req_data(
87 req
, struct create_ts_state
);
88 struct smb_create_returns cr
;
89 struct timespec mtime
;
92 status
= cli_ntcreate_recv(subreq
, &state
->fnum
, &cr
);
94 if (tevent_req_nterror(req
, status
)) {
98 mtime
= nt_time_to_unix_timespec(cr
.last_write_time
);
100 mtime
.tv_sec
&= ~(0xFFFFULL
);
101 mtime
.tv_sec
|= (state
->timestamp_idx
& 0xFFFF);
103 subreq
= cli_setfileinfo_ext_send(
108 (struct timespec
) { .tv_nsec
= SAMBA_UTIME_OMIT
}, /* create */
109 (struct timespec
) { .tv_nsec
= SAMBA_UTIME_OMIT
}, /* access */
111 (struct timespec
) { .tv_nsec
= SAMBA_UTIME_OMIT
}, /* change */
112 UINT32_MAX
); /* attr */
113 if (tevent_req_nomem(subreq
, req
)) {
116 tevent_req_set_callback(subreq
, create_ts_setinfo_done
, req
);
119 static void create_ts_setinfo_done(struct tevent_req
*subreq
)
121 struct tevent_req
*req
= tevent_req_callback_data(
122 subreq
, struct tevent_req
);
123 struct create_ts_state
*state
= tevent_req_data(
124 req
, struct create_ts_state
);
127 status
= cli_setfileinfo_ext_recv(subreq
);
129 if (tevent_req_nterror(req
, status
)) {
133 subreq
= tevent_wakeup_send(
134 state
, state
->ev
, timeval_current_ofs_msec(100));
135 if (tevent_req_nomem(subreq
, req
)) {
138 tevent_req_set_callback(subreq
, create_ts_waited
, req
);
141 static void create_ts_waited(struct tevent_req
*subreq
)
143 struct tevent_req
*req
= tevent_req_callback_data(
144 subreq
, struct tevent_req
);
145 struct create_ts_state
*state
= tevent_req_data(
146 req
, struct create_ts_state
);
149 ok
= tevent_wakeup_recv(subreq
);
152 tevent_req_oom(subreq
);
156 subreq
= cli_write_send(
162 (uint8_t *)&state
->fnum
,
164 sizeof(state
->fnum
));
165 if (tevent_req_nomem(subreq
, req
)) {
168 tevent_req_set_callback(subreq
, create_ts_written
, req
);
171 static void create_ts_written(struct tevent_req
*subreq
)
173 struct tevent_req
*req
= tevent_req_callback_data(
174 subreq
, struct tevent_req
);
175 struct create_ts_state
*state
= tevent_req_data(
176 req
, struct create_ts_state
);
180 status
= cli_write_recv(subreq
, &written
);
182 if (tevent_req_nterror(subreq
, status
)) {
186 subreq
= cli_nt_delete_on_close_send(
187 state
, state
->ev
, state
->cli
, state
->fnum
, true);
188 if (tevent_req_nomem(subreq
, req
)) {
191 tevent_req_set_callback(subreq
, create_ts_doc_done
, req
);
194 static void create_ts_doc_done(struct tevent_req
*subreq
)
196 NTSTATUS status
= cli_nt_delete_on_close_recv(subreq
);
197 tevent_req_simple_finish_ntstatus(subreq
, status
);
200 static NTSTATUS
create_ts_recv(struct tevent_req
*req
, uint16_t *fnum
)
202 struct create_ts_state
*state
= tevent_req_data(
203 req
, struct create_ts_state
);
206 if (tevent_req_is_nterror(req
, &status
)) {
210 tevent_req_received(req
);
214 struct create_ts_files_state
{
220 static void create_ts_files_done(struct tevent_req
*subreq
);
222 static struct tevent_req
*create_ts_files_send(
224 struct tevent_context
*ev
,
225 struct cli_state
*cli
,
230 struct tevent_req
*req
= NULL
;
231 struct create_ts_files_state
*state
= NULL
;
234 req
= tevent_req_create(mem_ctx
, &state
, struct create_ts_files_state
);
238 state
->num_files
= num_files
;
240 state
->fnums
= talloc_array(state
, uint16_t, num_files
);
241 if (tevent_req_nomem(state
->fnums
, req
)) {
242 return tevent_req_post(req
, ev
);
245 for (i
=0; i
<num_files
; i
++) {
246 struct tevent_req
*subreq
= NULL
;
247 const char *fname
= NULL
;
249 fname
= talloc_asprintf(state
, "%s%zu_%zu", prefix
, idx
, i
);
250 if (tevent_req_nomem(fname
, req
)) {
251 return tevent_req_post(req
, ev
);
254 subreq
= create_ts_send(state
, ev
, cli
, fname
, i
);
255 if (tevent_req_nomem(subreq
, req
)) {
256 return tevent_req_post(req
, ev
);
258 talloc_steal(subreq
, fname
);
260 tevent_req_set_callback(subreq
, create_ts_files_done
, req
);
265 static void create_ts_files_done(struct tevent_req
*subreq
)
267 struct tevent_req
*req
= tevent_req_callback_data(
268 subreq
, struct tevent_req
);
269 struct create_ts_files_state
*state
= tevent_req_data(
270 req
, struct create_ts_files_state
);
273 status
= create_ts_recv(subreq
, &state
->fnums
[state
->num_received
]);
275 if (tevent_req_nterror(req
, status
)) {
279 state
->num_received
+= 1;
280 if (state
->num_received
== state
->num_files
) {
281 tevent_req_done(req
);
285 static NTSTATUS
create_ts_files_recv(
286 struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
, uint16_t **fnums
)
288 struct create_ts_files_state
*state
= tevent_req_data(
289 req
, struct create_ts_files_state
);
292 if (tevent_req_is_nterror(req
, &status
)) {
295 *fnums
= talloc_move(mem_ctx
, &state
->fnums
);
296 tevent_req_received(req
);
300 struct create_files_state
{
303 struct tevent_req
**reqs
;
307 static void create_files_done(struct tevent_req
*subreq
);
309 static struct tevent_req
*create_files_send(
311 struct tevent_context
*ev
,
312 struct cli_state
**cli
,
317 struct tevent_req
*req
= NULL
;
318 struct create_files_state
*state
= NULL
;
321 req
= tevent_req_create(mem_ctx
, &state
, struct create_files_state
);
325 state
->num_reqs
= num_cli
;
327 state
->reqs
= talloc_array(state
, struct tevent_req
*, num_cli
);
328 if (tevent_req_nomem(state
->reqs
, req
)) {
329 return tevent_req_post(req
, ev
);
331 state
->fnums
= talloc_array(state
, uint16_t *, num_cli
);
332 if (tevent_req_nomem(state
->fnums
, req
)) {
333 return tevent_req_post(req
, ev
);
336 for (i
=0; i
<num_cli
; i
++) {
337 state
->reqs
[i
] = create_ts_files_send(
338 state
, ev
, cli
[i
], prefix
, i
, num_files
);
339 if (tevent_req_nomem(state
->reqs
[i
], req
)) {
340 return tevent_req_post(req
, ev
);
342 tevent_req_set_callback(
343 state
->reqs
[i
], create_files_done
, req
);
348 static void create_files_done(struct tevent_req
*subreq
)
350 struct tevent_req
*req
= tevent_req_callback_data(
351 subreq
, struct tevent_req
);
352 struct create_files_state
*state
= tevent_req_data(
353 req
, struct create_files_state
);
354 uint16_t *fnums
= NULL
;
358 status
= create_ts_files_recv(subreq
, state
->fnums
, &fnums
);
359 if (tevent_req_nterror(req
, status
)) {
363 for (i
=0; i
<state
->num_reqs
; i
++) {
364 if (state
->reqs
[i
] == subreq
) {
368 if (i
== state
->num_reqs
) {
369 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
374 state
->reqs
[i
] = NULL
;
375 state
->fnums
[i
] = fnums
;
377 state
->num_received
+= 1;
379 if (state
->num_reqs
== state
->num_received
) {
380 tevent_req_done(req
);
384 static NTSTATUS
create_files_recv(
385 struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
, uint16_t ***fnums
)
387 struct create_files_state
*state
= tevent_req_data(
388 req
, struct create_files_state
);
391 if (tevent_req_is_nterror(req
, &status
)) {
395 *fnums
= talloc_move(mem_ctx
, &state
->fnums
);
396 tevent_req_received(req
);
400 struct list_cb_state
{
405 static NTSTATUS
list_cb(
410 struct list_cb_state
*state
= private_data
;
411 char *underbar
= NULL
;
412 unsigned long long int name_idx
;
415 underbar
= strchr(f
->name
, '_');
416 if (underbar
== NULL
) {
417 /* alien filename, . or ..? */
421 name_idx
= smb_strtoull(underbar
+1, NULL
, 10, &err
, SMB_STR_STANDARD
);
427 if ((name_idx
& 0xFFFF) != (f
->mtime_ts
.tv_sec
& 0xFFFF)) {
428 d_printf("idx=%llu, nsec=%ld\n",
430 f
->mtime_ts
.tv_nsec
);
438 bool run_readdir_timestamp(int dummy
)
440 struct cli_state
**cli
= NULL
;
444 const char prefix
[] = "readdir_ts/";
445 struct list_cb_state state
= { .ok
= true };
446 struct tevent_context
*ev
= NULL
;
447 struct tevent_req
*req
= NULL
;
448 uint16_t **fnums
= NULL
;
452 cli
= talloc_array(talloc_tos(), struct cli_state
*, torture_nprocs
);
454 d_printf("talloc_array failed\n");
458 for (i
=0; i
<torture_nprocs
; i
++) {
459 ok
= torture_open_connection_flags(&cli
[i
], i
, 0);
461 d_printf("torture_open_connection_flags(%d) failed\n",
467 status
= cli_mkdir(cli
[0], "readdir_ts");
468 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_COLLISION
)) {
469 status
= NT_STATUS_OK
;
471 if (!NT_STATUS_IS_OK(status
)) {
472 d_printf("cli_mkdir failed: %s\n", nt_errstr(status
));
476 ev
= samba_tevent_context_init(cli
);
478 d_printf("samba_tevent_context_init() failed\n");
482 req
= create_files_send(
483 cli
, ev
, cli
, torture_nprocs
, prefix
, torture_numops
);
485 d_printf("create_files_send() failed\n");
489 ok
= tevent_req_poll_ntstatus(req
, ev
, &status
);
491 d_printf("tevent_req_poll_ntstatus failed: %s\n",
496 status
= create_files_recv(req
, talloc_tos(), &fnums
);
498 if (!NT_STATUS_IS_OK(status
)) {
499 d_printf("create_files_recv failed: %s\n",
504 status
= cli_list(cli
[0],
506 FILE_ATTRIBUTE_DIRECTORY
|
507 FILE_ATTRIBUTE_SYSTEM
|
508 FILE_ATTRIBUTE_HIDDEN
,
511 if (!NT_STATUS_IS_OK(status
)) {
512 d_printf("cli_list failed: %s\n",
517 expected
= torture_nprocs
* torture_numops
;
518 if (state
.found
!= expected
) {
519 d_printf("Expected %zu, got %zu files\n",
525 d_printf("timestamp mismatch\n");