s3: smbtorture3: Add test_smb1_ctemp() DFS test to run_smb1_dfs_operations().
[samba.git] / source3 / torture / test_readdir_timestamp.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * Copyright (C) Volker Lendecke 2020
4  *
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.
9  *
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.
14  *
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/>.
17  */
18
19 #include "includes.h"
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"
25
26 extern int torture_nprocs;
27 extern int torture_numops;
28
29 struct create_ts_state {
30         struct tevent_context *ev;
31         struct cli_state *cli;
32         unsigned timestamp_idx;
33         uint16_t fnum;
34 };
35
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);
41
42 static struct tevent_req *create_ts_send(
43         TALLOC_CTX *mem_ctx,
44         struct tevent_context *ev,
45         struct cli_state *cli,
46         const char *fname,
47         unsigned timestamp_idx)
48 {
49         struct tevent_req *req = NULL, *subreq = NULL;
50         struct create_ts_state *state = NULL;
51
52         req = tevent_req_create(mem_ctx, &state, struct create_ts_state);
53         if (req == NULL) {
54                 return NULL;
55         }
56         state->ev = ev;
57         state->cli = cli;
58         state->timestamp_idx = timestamp_idx;
59
60         subreq = cli_ntcreate_send(
61                 state,
62                 ev,
63                 cli,
64                 fname,
65                 0,                      /* CreatFlags */
66                 SEC_FILE_WRITE_ATTRIBUTE|
67                 SEC_FILE_WRITE_DATA|
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);
77         }
78         tevent_req_set_callback(subreq, create_ts_opened, req);
79         return req;
80 }
81
82 static void create_ts_opened(struct tevent_req *subreq)
83 {
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;
90         NTSTATUS status;
91
92         status = cli_ntcreate_recv(subreq, &state->fnum, &cr);
93         TALLOC_FREE(subreq);
94         if (tevent_req_nterror(req, status)) {
95                 return;
96         }
97
98         mtime = nt_time_to_unix_timespec(cr.last_write_time);
99
100         mtime.tv_sec &= ~(0xFFFFULL);
101         mtime.tv_sec |= (state->timestamp_idx & 0xFFFF);
102
103         subreq = cli_setfileinfo_ext_send(
104                 state,
105                 state->ev,
106                 state->cli,
107                 state->fnum,
108                 (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
109                 (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
110                 mtime,
111                 (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
112                 UINT32_MAX);    /* attr */
113         if (tevent_req_nomem(subreq, req)) {
114                 return;
115         }
116         tevent_req_set_callback(subreq, create_ts_setinfo_done, req);
117 }
118
119 static void create_ts_setinfo_done(struct tevent_req *subreq)
120 {
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);
125         NTSTATUS status;
126
127         status = cli_setfileinfo_ext_recv(subreq);
128         TALLOC_FREE(subreq);
129         if (tevent_req_nterror(req, status)) {
130                 return;
131         }
132
133         subreq = tevent_wakeup_send(
134                 state, state->ev, timeval_current_ofs_msec(100));
135         if (tevent_req_nomem(subreq, req)) {
136                 return;
137         }
138         tevent_req_set_callback(subreq, create_ts_waited, req);
139 }
140
141 static void create_ts_waited(struct tevent_req *subreq)
142 {
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);
147         bool ok;
148
149         ok = tevent_wakeup_recv(subreq);
150         TALLOC_FREE(subreq);
151         if (!ok) {
152                 tevent_req_oom(subreq);
153                 return;
154         }
155
156         subreq = cli_write_send(
157                 state,
158                 state->ev,
159                 state->cli,
160                 state->fnum,
161                 0,
162                 (uint8_t *)&state->fnum,
163                 0,
164                 sizeof(state->fnum));
165         if (tevent_req_nomem(subreq, req)) {
166                 return;
167         }
168         tevent_req_set_callback(subreq, create_ts_written, req);
169 }
170
171 static void create_ts_written(struct tevent_req *subreq)
172 {
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);
177         size_t written;
178         NTSTATUS status;
179
180         status = cli_write_recv(subreq, &written);
181         TALLOC_FREE(subreq);
182         if (tevent_req_nterror(subreq, status)) {
183                 return;
184         }
185
186         subreq = cli_nt_delete_on_close_send(
187                 state, state->ev, state->cli, state->fnum, true);
188         if (tevent_req_nomem(subreq, req)) {
189                 return;
190         }
191         tevent_req_set_callback(subreq, create_ts_doc_done, req);
192 }
193
194 static void create_ts_doc_done(struct tevent_req *subreq)
195 {
196         NTSTATUS status = cli_nt_delete_on_close_recv(subreq);
197         tevent_req_simple_finish_ntstatus(subreq, status);
198 }
199
200 static NTSTATUS create_ts_recv(struct tevent_req *req, uint16_t *fnum)
201 {
202         struct create_ts_state *state = tevent_req_data(
203                 req, struct create_ts_state);
204         NTSTATUS status;
205
206         if (tevent_req_is_nterror(req, &status)) {
207                 return status;
208         }
209         *fnum = state->fnum;
210         tevent_req_received(req);
211         return NT_STATUS_OK;
212 }
213
214 struct create_ts_files_state {
215         size_t num_files;
216         size_t num_received;
217         uint16_t *fnums;
218 };
219
220 static void create_ts_files_done(struct tevent_req *subreq);
221
222 static struct tevent_req *create_ts_files_send(
223         TALLOC_CTX *mem_ctx,
224         struct tevent_context *ev,
225         struct cli_state *cli,
226         const char *prefix,
227         size_t idx,
228         size_t num_files)
229 {
230         struct tevent_req *req = NULL;
231         struct create_ts_files_state *state = NULL;
232         size_t i;
233
234         req = tevent_req_create(mem_ctx, &state, struct create_ts_files_state);
235         if (req == NULL) {
236                 return NULL;
237         }
238         state->num_files = num_files;
239
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);
243         }
244
245         for (i=0; i<num_files; i++) {
246                 struct tevent_req *subreq = NULL;
247                 const char *fname = NULL;
248
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);
252                 }
253
254                 subreq = create_ts_send(state, ev, cli, fname, i);
255                 if (tevent_req_nomem(subreq, req)) {
256                         return tevent_req_post(req, ev);
257                 }
258                 talloc_steal(subreq, fname);
259
260                 tevent_req_set_callback(subreq, create_ts_files_done, req);
261         }
262         return req;
263 }
264
265 static void create_ts_files_done(struct tevent_req *subreq)
266 {
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);
271         NTSTATUS status;
272
273         status = create_ts_recv(subreq, &state->fnums[state->num_received]);
274         TALLOC_FREE(subreq);
275         if (tevent_req_nterror(req, status)) {
276                 return;
277         }
278
279         state->num_received += 1;
280         if (state->num_received == state->num_files) {
281                 tevent_req_done(req);
282         }
283 }
284
285 static NTSTATUS create_ts_files_recv(
286         struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t **fnums)
287 {
288         struct create_ts_files_state *state = tevent_req_data(
289                 req, struct create_ts_files_state);
290         NTSTATUS status;
291
292         if (tevent_req_is_nterror(req, &status)) {
293                 return status;
294         }
295         *fnums = talloc_move(mem_ctx, &state->fnums);
296         tevent_req_received(req);
297         return NT_STATUS_OK;
298 }
299
300 struct create_files_state {
301         size_t num_reqs;
302         size_t num_received;
303         struct tevent_req **reqs;
304         uint16_t **fnums;
305 };
306
307 static void create_files_done(struct tevent_req *subreq);
308
309 static struct tevent_req *create_files_send(
310         TALLOC_CTX *mem_ctx,
311         struct tevent_context *ev,
312         struct cli_state **cli,
313         size_t num_cli,
314         const char *prefix,
315         size_t num_files)
316 {
317         struct tevent_req *req = NULL;
318         struct create_files_state *state = NULL;
319         size_t i;
320
321         req = tevent_req_create(mem_ctx, &state, struct create_files_state);
322         if (req == NULL) {
323                 return NULL;
324         }
325         state->num_reqs = num_cli;
326
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);
330         }
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);
334         }
335
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);
341                 }
342                 tevent_req_set_callback(
343                         state->reqs[i], create_files_done, req);
344         }
345         return req;
346 }
347
348 static void create_files_done(struct tevent_req *subreq)
349 {
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;
355         NTSTATUS status;
356         size_t i;
357
358         status = create_ts_files_recv(subreq, state->fnums, &fnums);
359         if (tevent_req_nterror(req, status)) {
360                 return;
361         }
362
363         for (i=0; i<state->num_reqs; i++) {
364                 if (state->reqs[i] == subreq) {
365                         break;
366                 }
367         }
368         if (i == state->num_reqs) {
369                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
370                 return;
371         }
372
373         TALLOC_FREE(subreq);
374         state->reqs[i] = NULL;
375         state->fnums[i] = fnums;
376
377         state->num_received += 1;
378
379         if (state->num_reqs == state->num_received) {
380                 tevent_req_done(req);
381         }
382 }
383
384 static NTSTATUS create_files_recv(
385         struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t ***fnums)
386 {
387         struct create_files_state *state = tevent_req_data(
388                 req, struct create_files_state);
389         NTSTATUS status;
390
391         if (tevent_req_is_nterror(req, &status)) {
392                 return status;
393         }
394
395         *fnums = talloc_move(mem_ctx, &state->fnums);
396         tevent_req_received(req);
397         return NT_STATUS_OK;
398 }
399
400 struct list_cb_state {
401         size_t found;
402         bool ok;
403 };
404
405 static NTSTATUS list_cb(
406         struct file_info *f,
407         const char *mask,
408         void *private_data)
409 {
410         struct list_cb_state *state = private_data;
411         char *underbar = NULL;
412         unsigned long long int name_idx;
413         int err;
414
415         underbar = strchr(f->name, '_');
416         if (underbar == NULL) {
417                 /* alien filename, . or ..? */
418                 return NT_STATUS_OK;
419         }
420
421         name_idx = smb_strtoull(underbar+1, NULL, 10, &err, SMB_STR_STANDARD);
422         if (err != 0) {
423                 /* non-numeric? */
424                 return NT_STATUS_OK;
425         }
426
427         if ((name_idx & 0xFFFF) != (f->mtime_ts.tv_sec & 0xFFFF)) {
428                 d_printf("idx=%llu, nsec=%ld\n",
429                          name_idx,
430                          f->mtime_ts.tv_nsec);
431                 state->ok = false;
432         }
433         state->found += 1;
434
435         return NT_STATUS_OK;
436 }
437
438 bool run_readdir_timestamp(int dummy)
439 {
440         struct cli_state **cli = NULL;
441         int i;
442         bool ret = false;
443         bool ok;
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;
449         NTSTATUS status;
450         size_t expected;
451
452         cli = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
453         if (cli == NULL) {
454                 d_printf("talloc_array failed\n");
455                 goto fail;
456         }
457
458         for (i=0; i<torture_nprocs; i++) {
459                 ok = torture_open_connection_flags(&cli[i], i, 0);
460                 if (!ok) {
461                         d_printf("torture_open_connection_flags(%d) failed\n",
462                                  i);
463                         goto fail;
464                 }
465         }
466
467         status = cli_mkdir(cli[0], "readdir_ts");
468         if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
469                 status = NT_STATUS_OK;
470         }
471         if (!NT_STATUS_IS_OK(status)) {
472                 d_printf("cli_mkdir failed: %s\n", nt_errstr(status));
473                 goto fail;
474         }
475
476         ev = samba_tevent_context_init(cli);
477         if (ev == NULL) {
478                 d_printf("samba_tevent_context_init() failed\n");
479                 goto fail;
480         }
481
482         req = create_files_send(
483                 cli, ev, cli, torture_nprocs, prefix, torture_numops);
484         if (req == NULL) {
485                 d_printf("create_files_send() failed\n");
486                 goto fail;
487         }
488
489         ok = tevent_req_poll_ntstatus(req, ev, &status);
490         if (!ok) {
491                 d_printf("tevent_req_poll_ntstatus failed: %s\n",
492                          nt_errstr(status));
493                 goto fail;
494         }
495
496         status = create_files_recv(req, talloc_tos(), &fnums);
497         TALLOC_FREE(req);
498         if (!NT_STATUS_IS_OK(status)) {
499                 d_printf("create_files_recv failed: %s\n",
500                          nt_errstr(status));
501                 goto fail;
502         }
503
504         status = cli_list(cli[0],
505                           "readdir_ts\\*",
506                           FILE_ATTRIBUTE_DIRECTORY |
507                           FILE_ATTRIBUTE_SYSTEM |
508                           FILE_ATTRIBUTE_HIDDEN,
509                           list_cb,
510                           &state);
511         if (!NT_STATUS_IS_OK(status)) {
512                 d_printf("cli_list failed: %s\n",
513                          nt_errstr(status));
514                 goto fail;
515         }
516
517         expected = torture_nprocs * torture_numops;
518         if (state.found != expected) {
519                 d_printf("Expected %zu, got %zu files\n",
520                          expected,
521                          state.found);
522                 goto fail;
523         }
524         if (!state.ok) {
525                 d_printf("timestamp mismatch\n");
526                 goto fail;
527         }
528
529         ret = true;
530 fail:
531         TALLOC_FREE(cli);
532         return ret;
533 }