Line data Source code
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 0 : 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 0 : struct tevent_req *req = NULL, *subreq = NULL;
50 0 : struct create_ts_state *state = NULL;
51 :
52 0 : req = tevent_req_create(mem_ctx, &state, struct create_ts_state);
53 0 : if (req == NULL) {
54 0 : return NULL;
55 : }
56 0 : state->ev = ev;
57 0 : state->cli = cli;
58 0 : state->timestamp_idx = timestamp_idx;
59 :
60 0 : 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 0 : if (tevent_req_nomem(subreq, req)) {
76 0 : return tevent_req_post(req, ev);
77 : }
78 0 : tevent_req_set_callback(subreq, create_ts_opened, req);
79 0 : return req;
80 : }
81 :
82 0 : static void create_ts_opened(struct tevent_req *subreq)
83 : {
84 0 : struct tevent_req *req = tevent_req_callback_data(
85 : subreq, struct tevent_req);
86 0 : struct create_ts_state *state = tevent_req_data(
87 : req, struct create_ts_state);
88 0 : struct smb_create_returns cr;
89 0 : struct timespec mtime;
90 0 : NTSTATUS status;
91 :
92 0 : status = cli_ntcreate_recv(subreq, &state->fnum, &cr);
93 0 : TALLOC_FREE(subreq);
94 0 : if (tevent_req_nterror(req, status)) {
95 0 : return;
96 : }
97 :
98 0 : mtime = nt_time_to_unix_timespec(cr.last_write_time);
99 :
100 0 : mtime.tv_sec &= ~(0xFFFFULL);
101 0 : mtime.tv_sec |= (state->timestamp_idx & 0xFFFF);
102 :
103 0 : subreq = cli_setfileinfo_ext_send(
104 : state,
105 : state->ev,
106 : state->cli,
107 0 : state->fnum,
108 0 : (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* create */
109 0 : (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* access */
110 : mtime,
111 0 : (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, /* change */
112 : UINT32_MAX); /* attr */
113 0 : if (tevent_req_nomem(subreq, req)) {
114 0 : return;
115 : }
116 0 : tevent_req_set_callback(subreq, create_ts_setinfo_done, req);
117 : }
118 :
119 0 : static void create_ts_setinfo_done(struct tevent_req *subreq)
120 : {
121 0 : struct tevent_req *req = tevent_req_callback_data(
122 : subreq, struct tevent_req);
123 0 : struct create_ts_state *state = tevent_req_data(
124 : req, struct create_ts_state);
125 0 : NTSTATUS status;
126 :
127 0 : status = cli_setfileinfo_ext_recv(subreq);
128 0 : TALLOC_FREE(subreq);
129 0 : if (tevent_req_nterror(req, status)) {
130 0 : return;
131 : }
132 :
133 0 : subreq = tevent_wakeup_send(
134 : state, state->ev, timeval_current_ofs_msec(100));
135 0 : if (tevent_req_nomem(subreq, req)) {
136 0 : return;
137 : }
138 0 : tevent_req_set_callback(subreq, create_ts_waited, req);
139 : }
140 :
141 0 : static void create_ts_waited(struct tevent_req *subreq)
142 : {
143 0 : struct tevent_req *req = tevent_req_callback_data(
144 : subreq, struct tevent_req);
145 0 : struct create_ts_state *state = tevent_req_data(
146 : req, struct create_ts_state);
147 0 : bool ok;
148 :
149 0 : ok = tevent_wakeup_recv(subreq);
150 0 : TALLOC_FREE(subreq);
151 0 : if (!ok) {
152 0 : tevent_req_oom(subreq);
153 0 : return;
154 : }
155 :
156 0 : subreq = cli_write_send(
157 : state,
158 : state->ev,
159 : state->cli,
160 0 : state->fnum,
161 : 0,
162 0 : (uint8_t *)&state->fnum,
163 : 0,
164 : sizeof(state->fnum));
165 0 : if (tevent_req_nomem(subreq, req)) {
166 0 : return;
167 : }
168 0 : tevent_req_set_callback(subreq, create_ts_written, req);
169 : }
170 :
171 0 : static void create_ts_written(struct tevent_req *subreq)
172 : {
173 0 : struct tevent_req *req = tevent_req_callback_data(
174 : subreq, struct tevent_req);
175 0 : struct create_ts_state *state = tevent_req_data(
176 : req, struct create_ts_state);
177 0 : size_t written;
178 0 : NTSTATUS status;
179 :
180 0 : status = cli_write_recv(subreq, &written);
181 0 : TALLOC_FREE(subreq);
182 0 : if (tevent_req_nterror(subreq, status)) {
183 0 : return;
184 : }
185 :
186 0 : subreq = cli_nt_delete_on_close_send(
187 0 : state, state->ev, state->cli, state->fnum, true);
188 0 : if (tevent_req_nomem(subreq, req)) {
189 0 : return;
190 : }
191 0 : tevent_req_set_callback(subreq, create_ts_doc_done, req);
192 : }
193 :
194 0 : static void create_ts_doc_done(struct tevent_req *subreq)
195 : {
196 0 : NTSTATUS status = cli_nt_delete_on_close_recv(subreq);
197 0 : tevent_req_simple_finish_ntstatus(subreq, status);
198 0 : }
199 :
200 0 : static NTSTATUS create_ts_recv(struct tevent_req *req, uint16_t *fnum)
201 : {
202 0 : struct create_ts_state *state = tevent_req_data(
203 : req, struct create_ts_state);
204 0 : NTSTATUS status;
205 :
206 0 : if (tevent_req_is_nterror(req, &status)) {
207 0 : return status;
208 : }
209 0 : *fnum = state->fnum;
210 0 : tevent_req_received(req);
211 0 : 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 0 : 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 0 : struct tevent_req *req = NULL;
231 0 : struct create_ts_files_state *state = NULL;
232 0 : size_t i;
233 :
234 0 : req = tevent_req_create(mem_ctx, &state, struct create_ts_files_state);
235 0 : if (req == NULL) {
236 0 : return NULL;
237 : }
238 0 : state->num_files = num_files;
239 :
240 0 : state->fnums = talloc_array(state, uint16_t, num_files);
241 0 : if (tevent_req_nomem(state->fnums, req)) {
242 0 : return tevent_req_post(req, ev);
243 : }
244 :
245 0 : for (i=0; i<num_files; i++) {
246 0 : struct tevent_req *subreq = NULL;
247 0 : const char *fname = NULL;
248 :
249 0 : fname = talloc_asprintf(state, "%s%zu_%zu", prefix, idx, i);
250 0 : if (tevent_req_nomem(fname, req)) {
251 0 : return tevent_req_post(req, ev);
252 : }
253 :
254 0 : subreq = create_ts_send(state, ev, cli, fname, i);
255 0 : if (tevent_req_nomem(subreq, req)) {
256 0 : return tevent_req_post(req, ev);
257 : }
258 0 : talloc_steal(subreq, fname);
259 :
260 0 : tevent_req_set_callback(subreq, create_ts_files_done, req);
261 : }
262 0 : return req;
263 : }
264 :
265 0 : static void create_ts_files_done(struct tevent_req *subreq)
266 : {
267 0 : struct tevent_req *req = tevent_req_callback_data(
268 : subreq, struct tevent_req);
269 0 : struct create_ts_files_state *state = tevent_req_data(
270 : req, struct create_ts_files_state);
271 0 : NTSTATUS status;
272 :
273 0 : status = create_ts_recv(subreq, &state->fnums[state->num_received]);
274 0 : TALLOC_FREE(subreq);
275 0 : if (tevent_req_nterror(req, status)) {
276 0 : return;
277 : }
278 :
279 0 : state->num_received += 1;
280 0 : if (state->num_received == state->num_files) {
281 0 : tevent_req_done(req);
282 : }
283 : }
284 :
285 0 : static NTSTATUS create_ts_files_recv(
286 : struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t **fnums)
287 : {
288 0 : struct create_ts_files_state *state = tevent_req_data(
289 : req, struct create_ts_files_state);
290 0 : NTSTATUS status;
291 :
292 0 : if (tevent_req_is_nterror(req, &status)) {
293 0 : return status;
294 : }
295 0 : *fnums = talloc_move(mem_ctx, &state->fnums);
296 0 : tevent_req_received(req);
297 0 : 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 0 : 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 0 : struct tevent_req *req = NULL;
318 0 : struct create_files_state *state = NULL;
319 0 : size_t i;
320 :
321 0 : req = tevent_req_create(mem_ctx, &state, struct create_files_state);
322 0 : if (req == NULL) {
323 0 : return NULL;
324 : }
325 0 : state->num_reqs = num_cli;
326 :
327 0 : state->reqs = talloc_array(state, struct tevent_req *, num_cli);
328 0 : if (tevent_req_nomem(state->reqs, req)) {
329 0 : return tevent_req_post(req, ev);
330 : }
331 0 : state->fnums = talloc_array(state, uint16_t *, num_cli);
332 0 : if (tevent_req_nomem(state->fnums, req)) {
333 0 : return tevent_req_post(req, ev);
334 : }
335 :
336 0 : for (i=0; i<num_cli; i++) {
337 0 : state->reqs[i] = create_ts_files_send(
338 0 : state, ev, cli[i], prefix, i, num_files);
339 0 : if (tevent_req_nomem(state->reqs[i], req)) {
340 0 : return tevent_req_post(req, ev);
341 : }
342 0 : tevent_req_set_callback(
343 : state->reqs[i], create_files_done, req);
344 : }
345 0 : return req;
346 : }
347 :
348 0 : static void create_files_done(struct tevent_req *subreq)
349 : {
350 0 : struct tevent_req *req = tevent_req_callback_data(
351 : subreq, struct tevent_req);
352 0 : struct create_files_state *state = tevent_req_data(
353 : req, struct create_files_state);
354 0 : uint16_t *fnums = NULL;
355 0 : NTSTATUS status;
356 0 : size_t i;
357 :
358 0 : status = create_ts_files_recv(subreq, state->fnums, &fnums);
359 0 : if (tevent_req_nterror(req, status)) {
360 0 : return;
361 : }
362 :
363 0 : for (i=0; i<state->num_reqs; i++) {
364 0 : if (state->reqs[i] == subreq) {
365 0 : break;
366 : }
367 : }
368 0 : if (i == state->num_reqs) {
369 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
370 0 : return;
371 : }
372 :
373 0 : TALLOC_FREE(subreq);
374 0 : state->reqs[i] = NULL;
375 0 : state->fnums[i] = fnums;
376 :
377 0 : state->num_received += 1;
378 :
379 0 : if (state->num_reqs == state->num_received) {
380 0 : tevent_req_done(req);
381 : }
382 : }
383 :
384 0 : static NTSTATUS create_files_recv(
385 : struct tevent_req *req, TALLOC_CTX *mem_ctx, uint16_t ***fnums)
386 : {
387 0 : struct create_files_state *state = tevent_req_data(
388 : req, struct create_files_state);
389 0 : NTSTATUS status;
390 :
391 0 : if (tevent_req_is_nterror(req, &status)) {
392 0 : return status;
393 : }
394 :
395 0 : *fnums = talloc_move(mem_ctx, &state->fnums);
396 0 : tevent_req_received(req);
397 0 : return NT_STATUS_OK;
398 : }
399 :
400 : struct list_cb_state {
401 : size_t found;
402 : bool ok;
403 : };
404 :
405 0 : static NTSTATUS list_cb(
406 : struct file_info *f,
407 : const char *mask,
408 : void *private_data)
409 : {
410 0 : struct list_cb_state *state = private_data;
411 0 : char *underbar = NULL;
412 0 : unsigned long long int name_idx;
413 0 : int err;
414 :
415 0 : underbar = strchr(f->name, '_');
416 0 : if (underbar == NULL) {
417 : /* alien filename, . or ..? */
418 0 : return NT_STATUS_OK;
419 : }
420 :
421 0 : name_idx = smb_strtoull(underbar+1, NULL, 10, &err, SMB_STR_STANDARD);
422 0 : if (err != 0) {
423 : /* non-numeric? */
424 0 : return NT_STATUS_OK;
425 : }
426 :
427 0 : if ((name_idx & 0xFFFF) != (f->mtime_ts.tv_sec & 0xFFFF)) {
428 0 : d_printf("idx=%llu, nsec=%ld\n",
429 : name_idx,
430 : f->mtime_ts.tv_nsec);
431 0 : state->ok = false;
432 : }
433 0 : state->found += 1;
434 :
435 0 : return NT_STATUS_OK;
436 : }
437 :
438 0 : bool run_readdir_timestamp(int dummy)
439 : {
440 0 : struct cli_state **cli = NULL;
441 0 : int i;
442 0 : bool ret = false;
443 0 : bool ok;
444 0 : const char prefix[] = "readdir_ts/";
445 0 : struct list_cb_state state = { .ok = true };
446 0 : struct tevent_context *ev = NULL;
447 0 : struct tevent_req *req = NULL;
448 0 : uint16_t **fnums = NULL;
449 0 : NTSTATUS status;
450 0 : size_t expected;
451 :
452 0 : cli = talloc_array(talloc_tos(), struct cli_state *, torture_nprocs);
453 0 : if (cli == NULL) {
454 0 : d_printf("talloc_array failed\n");
455 0 : goto fail;
456 : }
457 :
458 0 : for (i=0; i<torture_nprocs; i++) {
459 0 : ok = torture_open_connection_flags(&cli[i], i, 0);
460 0 : if (!ok) {
461 0 : d_printf("torture_open_connection_flags(%d) failed\n",
462 : i);
463 0 : goto fail;
464 : }
465 : }
466 :
467 0 : status = cli_mkdir(cli[0], "readdir_ts");
468 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
469 0 : status = NT_STATUS_OK;
470 : }
471 0 : if (!NT_STATUS_IS_OK(status)) {
472 0 : d_printf("cli_mkdir failed: %s\n", nt_errstr(status));
473 0 : goto fail;
474 : }
475 :
476 0 : ev = samba_tevent_context_init(cli);
477 0 : if (ev == NULL) {
478 0 : d_printf("samba_tevent_context_init() failed\n");
479 0 : goto fail;
480 : }
481 :
482 0 : req = create_files_send(
483 : cli, ev, cli, torture_nprocs, prefix, torture_numops);
484 0 : if (req == NULL) {
485 0 : d_printf("create_files_send() failed\n");
486 0 : goto fail;
487 : }
488 :
489 0 : ok = tevent_req_poll_ntstatus(req, ev, &status);
490 0 : if (!ok) {
491 0 : d_printf("tevent_req_poll_ntstatus failed: %s\n",
492 : nt_errstr(status));
493 0 : goto fail;
494 : }
495 :
496 0 : status = create_files_recv(req, talloc_tos(), &fnums);
497 0 : TALLOC_FREE(req);
498 0 : if (!NT_STATUS_IS_OK(status)) {
499 0 : d_printf("create_files_recv failed: %s\n",
500 : nt_errstr(status));
501 0 : goto fail;
502 : }
503 :
504 0 : 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 0 : if (!NT_STATUS_IS_OK(status)) {
512 0 : d_printf("cli_list failed: %s\n",
513 : nt_errstr(status));
514 0 : goto fail;
515 : }
516 :
517 0 : expected = torture_nprocs * torture_numops;
518 0 : if (state.found != expected) {
519 0 : d_printf("Expected %zu, got %zu files\n",
520 : expected,
521 : state.found);
522 0 : goto fail;
523 : }
524 0 : if (!state.ok) {
525 0 : d_printf("timestamp mismatch\n");
526 0 : goto fail;
527 : }
528 :
529 0 : ret = true;
530 0 : fail:
531 0 : TALLOC_FREE(cli);
532 0 : return ret;
533 : }
|