Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : smb2 lib
4 : Copyright (C) Jeremy Allison 2013
5 : Copyright (C) Volker Lendecke 2013
6 : Copyright (C) Stefan Metzmacher 2013
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : /*
23 : This code is a thin wrapper around the existing
24 : cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
25 : but allows the handles to be mapped to uint16_t fnums,
26 : which are easier for smbclient to use.
27 : */
28 :
29 : #include "includes.h"
30 : #include "client.h"
31 : #include "async_smb.h"
32 : #include "../libcli/smb/smbXcli_base.h"
33 : #include "cli_smb2_fnum.h"
34 : #include "trans2.h"
35 : #include "clirap.h"
36 : #include "../libcli/smb/smb2_create_blob.h"
37 : #include "libsmb/proto.h"
38 : #include "lib/util/tevent_ntstatus.h"
39 : #include "../libcli/security/security.h"
40 : #include "../librpc/gen_ndr/ndr_security.h"
41 : #include "lib/util_ea.h"
42 : #include "librpc/gen_ndr/ndr_ioctl.h"
43 : #include "ntioctl.h"
44 : #include "librpc/gen_ndr/ndr_quota.h"
45 : #include "librpc/gen_ndr/ndr_smb3posix.h"
46 : #include "lib/util/string_wrappers.h"
47 : #include "lib/util/idtree.h"
48 :
49 : struct smb2_hnd {
50 : uint64_t fid_persistent;
51 : uint64_t fid_volatile;
52 : };
53 :
54 : /*
55 : * Handle mapping code.
56 : */
57 :
58 : /***************************************************************
59 : Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
60 : Ensures handle is owned by cli struct.
61 : ***************************************************************/
62 :
63 56870 : static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
64 : const struct smb2_hnd *ph, /* In */
65 : uint16_t *pfnum) /* Out */
66 : {
67 0 : int ret;
68 56870 : struct idr_context *idp = cli->smb2.open_handles;
69 56870 : struct smb2_hnd *owned_h = talloc_memdup(cli,
70 : ph,
71 : sizeof(struct smb2_hnd));
72 :
73 56870 : if (owned_h == NULL) {
74 0 : return NT_STATUS_NO_MEMORY;
75 : }
76 :
77 56870 : if (idp == NULL) {
78 : /* Lazy init */
79 12055 : cli->smb2.open_handles = idr_init(cli);
80 12055 : if (cli->smb2.open_handles == NULL) {
81 0 : TALLOC_FREE(owned_h);
82 0 : return NT_STATUS_NO_MEMORY;
83 : }
84 12055 : idp = cli->smb2.open_handles;
85 : }
86 :
87 56870 : ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
88 56870 : if (ret == -1) {
89 0 : TALLOC_FREE(owned_h);
90 0 : return NT_STATUS_NO_MEMORY;
91 : }
92 :
93 56870 : *pfnum = (uint16_t)ret;
94 56870 : return NT_STATUS_OK;
95 : }
96 :
97 : /***************************************************************
98 : Return the smb2_hnd pointer associated with the given fnum.
99 : ***************************************************************/
100 :
101 108878 : static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
102 : uint16_t fnum, /* In */
103 : struct smb2_hnd **pph) /* Out */
104 : {
105 108878 : struct idr_context *idp = cli->smb2.open_handles;
106 :
107 108878 : if (idp == NULL) {
108 0 : return NT_STATUS_INVALID_PARAMETER;
109 : }
110 108878 : *pph = (struct smb2_hnd *)idr_find(idp, fnum);
111 108878 : if (*pph == NULL) {
112 12 : return NT_STATUS_INVALID_HANDLE;
113 : }
114 108866 : return NT_STATUS_OK;
115 : }
116 :
117 : /***************************************************************
118 : Delete the fnum to smb2_hnd mapping. Zeros out handle on
119 : successful return.
120 : ***************************************************************/
121 :
122 56843 : static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
123 : struct smb2_hnd **pph, /* In */
124 : uint16_t fnum) /* In */
125 : {
126 56843 : struct idr_context *idp = cli->smb2.open_handles;
127 0 : struct smb2_hnd *ph;
128 :
129 56843 : if (idp == NULL) {
130 0 : return NT_STATUS_INVALID_PARAMETER;
131 : }
132 :
133 56843 : ph = (struct smb2_hnd *)idr_find(idp, fnum);
134 56843 : if (ph != *pph) {
135 0 : return NT_STATUS_INVALID_PARAMETER;
136 : }
137 56843 : idr_remove(idp, fnum);
138 56843 : TALLOC_FREE(*pph);
139 56843 : return NT_STATUS_OK;
140 : }
141 :
142 : /***************************************************************
143 : Oplock mapping code.
144 : ***************************************************************/
145 :
146 69068 : static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
147 : {
148 69068 : if (create_flags.batch_oplock) {
149 25 : return SMB2_OPLOCK_LEVEL_BATCH;
150 69043 : } else if (create_flags.exclusive_oplock) {
151 0 : return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
152 : }
153 :
154 : /* create_flags doesn't do a level2 request. */
155 69043 : return SMB2_OPLOCK_LEVEL_NONE;
156 : }
157 :
158 : /***************************************************************
159 : If we're on a DFS share, ensure we convert to a full DFS path
160 : if this hasn't already been done.
161 : ***************************************************************/
162 :
163 69068 : static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
164 : struct cli_state *cli,
165 : char *path)
166 : {
167 135871 : bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
168 66803 : smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
169 69068 : bool is_already_dfs_path = false;
170 :
171 69068 : if (!is_dfs) {
172 52069 : return path;
173 : }
174 16999 : is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
175 16999 : if (is_already_dfs_path) {
176 16942 : return path;
177 : }
178 57 : if (path[0] == '\0') {
179 6 : return talloc_asprintf(ctx,
180 : "%s\\%s",
181 : smbXcli_conn_remote_name(cli->conn),
182 : cli->share);
183 : }
184 84 : while (*path == '\\') {
185 33 : path++;
186 : }
187 51 : return talloc_asprintf(ctx,
188 : "%s\\%s\\%s",
189 : smbXcli_conn_remote_name(cli->conn),
190 : cli->share,
191 : path);
192 : }
193 :
194 : /***************************************************************
195 : Small wrapper that allows SMB2 create to return a uint16_t fnum.
196 : ***************************************************************/
197 :
198 : struct cli_smb2_create_fnum_state {
199 : struct cli_state *cli;
200 : struct smb2_create_blobs in_cblobs;
201 : struct smb2_create_blobs out_cblobs;
202 : struct smb_create_returns cr;
203 : struct symlink_reparse_struct *symlink;
204 : uint16_t fnum;
205 : struct tevent_req *subreq;
206 : };
207 :
208 : static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
209 : static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
210 :
211 69068 : struct tevent_req *cli_smb2_create_fnum_send(
212 : TALLOC_CTX *mem_ctx,
213 : struct tevent_context *ev,
214 : struct cli_state *cli,
215 : const char *fname_in,
216 : struct cli_smb2_create_flags create_flags,
217 : uint32_t impersonation_level,
218 : uint32_t desired_access,
219 : uint32_t file_attributes,
220 : uint32_t share_access,
221 : uint32_t create_disposition,
222 : uint32_t create_options,
223 : const struct smb2_create_blobs *in_cblobs)
224 : {
225 0 : struct tevent_req *req, *subreq;
226 0 : struct cli_smb2_create_fnum_state *state;
227 69068 : char *fname = NULL;
228 69068 : size_t fname_len = 0;
229 0 : bool have_twrp;
230 0 : NTTIME ntt;
231 0 : NTSTATUS status;
232 :
233 69068 : req = tevent_req_create(mem_ctx, &state,
234 : struct cli_smb2_create_fnum_state);
235 69068 : if (req == NULL) {
236 0 : return NULL;
237 : }
238 69068 : state->cli = cli;
239 :
240 69068 : fname = talloc_strdup(state, fname_in);
241 69068 : if (tevent_req_nomem(fname, req)) {
242 0 : return tevent_req_post(req, ev);
243 : }
244 :
245 69068 : if (cli->backup_intent) {
246 28 : create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
247 : }
248 :
249 69068 : if (cli->smb2.client_smb311_posix) {
250 18 : uint8_t modebuf[4] = {
251 : 0,
252 : };
253 :
254 0 : status =
255 18 : smb2_create_blob_add(state,
256 18 : &state->in_cblobs,
257 : SMB2_CREATE_TAG_POSIX,
258 18 : (DATA_BLOB){
259 : .data = modebuf,
260 : .length = sizeof(modebuf),
261 : });
262 18 : if (tevent_req_nterror(req, status)) {
263 0 : return tevent_req_post(req, ev);
264 : }
265 : }
266 :
267 : /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
268 69068 : have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
269 69068 : if (have_twrp) {
270 3986 : status = smb2_create_blob_add(
271 : state,
272 3986 : &state->in_cblobs,
273 : SMB2_CREATE_TAG_TWRP,
274 3986 : (DATA_BLOB) {
275 : .data = (uint8_t *)&ntt,
276 : .length = sizeof(ntt),
277 : });
278 3986 : if (tevent_req_nterror(req, status)) {
279 0 : return tevent_req_post(req, ev);
280 : }
281 : }
282 :
283 69068 : if (in_cblobs != NULL) {
284 : uint32_t i;
285 4284 : for (i=0; i<in_cblobs->num_blobs; i++) {
286 2142 : struct smb2_create_blob *b = &in_cblobs->blobs[i];
287 2142 : status = smb2_create_blob_add(
288 2142 : state, &state->in_cblobs, b->tag, b->data);
289 2142 : if (!NT_STATUS_IS_OK(status)) {
290 0 : tevent_req_nterror(req, status);
291 0 : return tevent_req_post(req, ev);
292 : }
293 : }
294 : }
295 :
296 69068 : fname = smb2_dfs_share_path(state, cli, fname);
297 69068 : if (tevent_req_nomem(fname, req)) {
298 0 : return tevent_req_post(req, ev);
299 : }
300 69068 : fname_len = strlen(fname);
301 :
302 : /* SMB2 is pickier about pathnames. Ensure it doesn't
303 : start in a '\' */
304 69068 : if (*fname == '\\') {
305 49888 : fname++;
306 49888 : fname_len--;
307 : }
308 :
309 : /* Or end in a '\' */
310 69068 : if (fname_len > 0 && fname[fname_len-1] == '\\') {
311 1681 : fname[fname_len-1] = '\0';
312 : }
313 :
314 138136 : subreq = smb2cli_create_send(state, ev,
315 : cli->conn,
316 69068 : cli->timeout,
317 : cli->smb2.session,
318 : cli->smb2.tcon,
319 : fname,
320 69068 : flags_to_smb2_oplock(create_flags),
321 : impersonation_level,
322 : desired_access,
323 : file_attributes,
324 : share_access,
325 : create_disposition,
326 : create_options,
327 69068 : &state->in_cblobs);
328 69068 : if (tevent_req_nomem(subreq, req)) {
329 0 : return tevent_req_post(req, ev);
330 : }
331 69068 : tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
332 :
333 69068 : state->subreq = subreq;
334 69068 : tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
335 :
336 69068 : return req;
337 : }
338 :
339 69068 : static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
340 : {
341 69068 : struct tevent_req *req = tevent_req_callback_data(
342 : subreq, struct tevent_req);
343 69068 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
344 : req, struct cli_smb2_create_fnum_state);
345 0 : struct smb2_hnd h;
346 0 : NTSTATUS status;
347 :
348 69068 : status = smb2cli_create_recv(
349 : subreq,
350 : &h.fid_persistent,
351 : &h.fid_volatile, &state->cr,
352 : state,
353 : &state->out_cblobs,
354 : &state->symlink);
355 69068 : TALLOC_FREE(subreq);
356 69068 : if (tevent_req_nterror(req, status)) {
357 12198 : return;
358 : }
359 :
360 56870 : status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
361 56870 : if (tevent_req_nterror(req, status)) {
362 0 : return;
363 : }
364 56870 : tevent_req_done(req);
365 : }
366 :
367 2 : static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
368 : {
369 2 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
370 : req, struct cli_smb2_create_fnum_state);
371 2 : return tevent_req_cancel(state->subreq);
372 : }
373 :
374 69068 : NTSTATUS cli_smb2_create_fnum_recv(
375 : struct tevent_req *req,
376 : uint16_t *pfnum,
377 : struct smb_create_returns *cr,
378 : TALLOC_CTX *mem_ctx,
379 : struct smb2_create_blobs *out_cblobs,
380 : struct symlink_reparse_struct **symlink)
381 : {
382 69068 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
383 : req, struct cli_smb2_create_fnum_state);
384 0 : NTSTATUS status;
385 :
386 69068 : if (tevent_req_is_nterror(req, &status)) {
387 12198 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
388 : (symlink != NULL)) {
389 0 : *symlink = talloc_move(mem_ctx, &state->symlink);
390 : }
391 12198 : state->cli->raw_status = status;
392 12198 : return status;
393 : }
394 56870 : if (pfnum != NULL) {
395 56870 : *pfnum = state->fnum;
396 : }
397 56870 : if (cr != NULL) {
398 30143 : *cr = state->cr;
399 : }
400 56870 : if (out_cblobs != NULL) {
401 2132 : *out_cblobs = (struct smb2_create_blobs) {
402 2132 : .num_blobs = state->out_cblobs.num_blobs,
403 2132 : .blobs = talloc_move(
404 : mem_ctx, &state->out_cblobs.blobs),
405 : };
406 : }
407 56870 : state->cli->raw_status = NT_STATUS_OK;
408 56870 : return NT_STATUS_OK;
409 : }
410 :
411 13886 : NTSTATUS cli_smb2_create_fnum(
412 : struct cli_state *cli,
413 : const char *fname,
414 : struct cli_smb2_create_flags create_flags,
415 : uint32_t impersonation_level,
416 : uint32_t desired_access,
417 : uint32_t file_attributes,
418 : uint32_t share_access,
419 : uint32_t create_disposition,
420 : uint32_t create_options,
421 : const struct smb2_create_blobs *in_cblobs,
422 : uint16_t *pfid,
423 : struct smb_create_returns *cr,
424 : TALLOC_CTX *mem_ctx,
425 : struct smb2_create_blobs *out_cblobs)
426 : {
427 13886 : TALLOC_CTX *frame = talloc_stackframe();
428 0 : struct tevent_context *ev;
429 0 : struct tevent_req *req;
430 13886 : NTSTATUS status = NT_STATUS_NO_MEMORY;
431 :
432 13886 : if (smbXcli_conn_has_async_calls(cli->conn)) {
433 : /*
434 : * Can't use sync call while an async call is in flight
435 : */
436 0 : status = NT_STATUS_INVALID_PARAMETER;
437 0 : goto fail;
438 : }
439 13886 : ev = samba_tevent_context_init(frame);
440 13886 : if (ev == NULL) {
441 0 : goto fail;
442 : }
443 13886 : req = cli_smb2_create_fnum_send(
444 : frame,
445 : ev,
446 : cli,
447 : fname,
448 : create_flags,
449 : impersonation_level,
450 : desired_access,
451 : file_attributes,
452 : share_access,
453 : create_disposition,
454 : create_options,
455 : in_cblobs);
456 13886 : if (req == NULL) {
457 0 : goto fail;
458 : }
459 13886 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
460 0 : goto fail;
461 : }
462 13886 : status = cli_smb2_create_fnum_recv(
463 : req, pfid, cr, mem_ctx, out_cblobs, NULL);
464 13886 : fail:
465 13886 : TALLOC_FREE(frame);
466 13886 : return status;
467 : }
468 :
469 : /***************************************************************
470 : Small wrapper that allows SMB2 close to use a uint16_t fnum.
471 : ***************************************************************/
472 :
473 : struct cli_smb2_close_fnum_state {
474 : struct cli_state *cli;
475 : uint16_t fnum;
476 : struct smb2_hnd *ph;
477 : };
478 :
479 : static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
480 :
481 56860 : struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
482 : struct tevent_context *ev,
483 : struct cli_state *cli,
484 : uint16_t fnum,
485 : uint16_t flags)
486 : {
487 0 : struct tevent_req *req, *subreq;
488 0 : struct cli_smb2_close_fnum_state *state;
489 0 : NTSTATUS status;
490 :
491 56860 : req = tevent_req_create(mem_ctx, &state,
492 : struct cli_smb2_close_fnum_state);
493 56860 : if (req == NULL) {
494 0 : return NULL;
495 : }
496 56860 : state->cli = cli;
497 56860 : state->fnum = fnum;
498 :
499 56860 : status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
500 56860 : if (tevent_req_nterror(req, status)) {
501 6 : return tevent_req_post(req, ev);
502 : }
503 :
504 56854 : subreq = smb2cli_close_send(state,
505 : ev,
506 : cli->conn,
507 56854 : cli->timeout,
508 : cli->smb2.session,
509 : cli->smb2.tcon,
510 : flags,
511 56854 : state->ph->fid_persistent,
512 56854 : state->ph->fid_volatile);
513 56854 : if (tevent_req_nomem(subreq, req)) {
514 0 : return tevent_req_post(req, ev);
515 : }
516 56854 : tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
517 56854 : return req;
518 : }
519 :
520 56854 : static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
521 : {
522 56854 : struct tevent_req *req = tevent_req_callback_data(
523 : subreq, struct tevent_req);
524 56854 : struct cli_smb2_close_fnum_state *state = tevent_req_data(
525 : req, struct cli_smb2_close_fnum_state);
526 0 : NTSTATUS status;
527 :
528 56854 : status = smb2cli_close_recv(subreq);
529 56854 : if (tevent_req_nterror(req, status)) {
530 11 : return;
531 : }
532 :
533 : /* Delete the fnum -> handle mapping. */
534 56843 : status = delete_smb2_handle_mapping(state->cli, &state->ph,
535 56843 : state->fnum);
536 56843 : if (tevent_req_nterror(req, status)) {
537 0 : return;
538 : }
539 56843 : tevent_req_done(req);
540 : }
541 :
542 32250 : NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
543 : {
544 32250 : struct cli_smb2_close_fnum_state *state = tevent_req_data(
545 : req, struct cli_smb2_close_fnum_state);
546 32250 : NTSTATUS status = NT_STATUS_OK;
547 :
548 32250 : if (tevent_req_is_nterror(req, &status)) {
549 3 : state->cli->raw_status = status;
550 : }
551 32250 : tevent_req_received(req);
552 32250 : return status;
553 : }
554 :
555 8719 : NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
556 : {
557 8719 : TALLOC_CTX *frame = talloc_stackframe();
558 0 : struct tevent_context *ev;
559 0 : struct tevent_req *req;
560 8719 : NTSTATUS status = NT_STATUS_NO_MEMORY;
561 :
562 8719 : if (smbXcli_conn_has_async_calls(cli->conn)) {
563 : /*
564 : * Can't use sync call while an async call is in flight
565 : */
566 0 : status = NT_STATUS_INVALID_PARAMETER;
567 0 : goto fail;
568 : }
569 8719 : ev = samba_tevent_context_init(frame);
570 8719 : if (ev == NULL) {
571 0 : goto fail;
572 : }
573 8719 : req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
574 8719 : if (req == NULL) {
575 0 : goto fail;
576 : }
577 8719 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
578 0 : goto fail;
579 : }
580 8719 : status = cli_smb2_close_fnum_recv(req);
581 8719 : fail:
582 8719 : TALLOC_FREE(frame);
583 8719 : return status;
584 : }
585 :
586 : struct cli_smb2_set_info_fnum_state {
587 : uint8_t dummy;
588 : };
589 :
590 : static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
591 :
592 9416 : struct tevent_req *cli_smb2_set_info_fnum_send(
593 : TALLOC_CTX *mem_ctx,
594 : struct tevent_context *ev,
595 : struct cli_state *cli,
596 : uint16_t fnum,
597 : uint8_t in_info_type,
598 : uint8_t in_info_class,
599 : const DATA_BLOB *in_input_buffer,
600 : uint32_t in_additional_info)
601 : {
602 9416 : struct tevent_req *req = NULL, *subreq = NULL;
603 9416 : struct cli_smb2_set_info_fnum_state *state = NULL;
604 9416 : struct smb2_hnd *ph = NULL;
605 0 : NTSTATUS status;
606 :
607 9416 : req = tevent_req_create(
608 : mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
609 9416 : if (req == NULL) {
610 0 : return NULL;
611 : }
612 :
613 9416 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
614 9416 : if (tevent_req_nterror(req, status)) {
615 0 : return tevent_req_post(req, ev);
616 : }
617 :
618 9416 : subreq = smb2cli_set_info_send(
619 : state,
620 : ev,
621 : cli->conn,
622 9416 : cli->timeout,
623 : cli->smb2.session,
624 : cli->smb2.tcon,
625 : in_info_type,
626 : in_info_class,
627 : in_input_buffer,
628 : in_additional_info,
629 9416 : ph->fid_persistent,
630 9416 : ph->fid_volatile);
631 9416 : if (tevent_req_nomem(subreq, req)) {
632 0 : return tevent_req_post(req, ev);
633 : }
634 9416 : tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
635 9416 : return req;
636 : }
637 :
638 9416 : static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
639 : {
640 9416 : NTSTATUS status = smb2cli_set_info_recv(subreq);
641 9416 : tevent_req_simple_finish_ntstatus(subreq, status);
642 9416 : }
643 :
644 9416 : NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
645 : {
646 9416 : return tevent_req_simple_recv_ntstatus(req);
647 : }
648 :
649 1896 : NTSTATUS cli_smb2_set_info_fnum(
650 : struct cli_state *cli,
651 : uint16_t fnum,
652 : uint8_t in_info_type,
653 : uint8_t in_info_class,
654 : const DATA_BLOB *in_input_buffer,
655 : uint32_t in_additional_info)
656 : {
657 1896 : TALLOC_CTX *frame = talloc_stackframe();
658 1896 : struct tevent_context *ev = NULL;
659 1896 : struct tevent_req *req = NULL;
660 1896 : NTSTATUS status = NT_STATUS_NO_MEMORY;
661 0 : bool ok;
662 :
663 1896 : if (smbXcli_conn_has_async_calls(cli->conn)) {
664 : /*
665 : * Can't use sync call while an async call is in flight
666 : */
667 0 : status = NT_STATUS_INVALID_PARAMETER;
668 0 : goto fail;
669 : }
670 1896 : ev = samba_tevent_context_init(frame);
671 1896 : if (ev == NULL) {
672 0 : goto fail;
673 : }
674 1896 : req = cli_smb2_set_info_fnum_send(
675 : frame,
676 : ev,
677 : cli,
678 : fnum,
679 : in_info_type,
680 : in_info_class,
681 : in_input_buffer,
682 : in_additional_info);
683 1896 : if (req == NULL) {
684 0 : goto fail;
685 : }
686 1896 : ok = tevent_req_poll_ntstatus(req, ev, &status);
687 1896 : if (!ok) {
688 0 : goto fail;
689 : }
690 1896 : status = cli_smb2_set_info_fnum_recv(req);
691 1896 : fail:
692 1896 : TALLOC_FREE(frame);
693 1896 : return status;
694 : }
695 :
696 : struct cli_smb2_delete_on_close_state {
697 : struct cli_state *cli;
698 : uint8_t data[1];
699 : DATA_BLOB inbuf;
700 : };
701 :
702 : static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
703 :
704 3811 : struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
705 : struct tevent_context *ev,
706 : struct cli_state *cli,
707 : uint16_t fnum,
708 : bool flag)
709 : {
710 3811 : struct tevent_req *req = NULL;
711 3811 : struct cli_smb2_delete_on_close_state *state = NULL;
712 3811 : struct tevent_req *subreq = NULL;
713 0 : uint8_t in_info_type;
714 0 : uint8_t in_file_info_class;
715 :
716 3811 : req = tevent_req_create(mem_ctx, &state,
717 : struct cli_smb2_delete_on_close_state);
718 3811 : if (req == NULL) {
719 0 : return NULL;
720 : }
721 3811 : state->cli = cli;
722 :
723 : /*
724 : * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
725 : * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
726 : */
727 3811 : in_info_type = 1;
728 3811 : in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
729 : /* Setup data array. */
730 3811 : SCVAL(&state->data[0], 0, flag ? 1 : 0);
731 3811 : state->inbuf.data = &state->data[0];
732 3811 : state->inbuf.length = 1;
733 :
734 3811 : subreq = cli_smb2_set_info_fnum_send(
735 : state,
736 : ev,
737 : cli,
738 : fnum,
739 : in_info_type,
740 : in_file_info_class,
741 3811 : &state->inbuf,
742 : 0);
743 3811 : if (tevent_req_nomem(subreq, req)) {
744 0 : return tevent_req_post(req, ev);
745 : }
746 3811 : tevent_req_set_callback(subreq,
747 : cli_smb2_delete_on_close_done,
748 : req);
749 3811 : return req;
750 : }
751 :
752 3811 : static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
753 : {
754 3811 : NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
755 3811 : tevent_req_simple_finish_ntstatus(subreq, status);
756 3811 : }
757 :
758 3811 : NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
759 : {
760 0 : struct cli_smb2_delete_on_close_state *state =
761 3811 : tevent_req_data(req,
762 : struct cli_smb2_delete_on_close_state);
763 0 : NTSTATUS status;
764 :
765 3811 : if (tevent_req_is_nterror(req, &status)) {
766 80 : state->cli->raw_status = status;
767 80 : tevent_req_received(req);
768 80 : return status;
769 : }
770 :
771 3731 : state->cli->raw_status = NT_STATUS_OK;
772 3731 : tevent_req_received(req);
773 3731 : return NT_STATUS_OK;
774 : }
775 :
776 0 : NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
777 : {
778 0 : TALLOC_CTX *frame = talloc_stackframe();
779 0 : struct tevent_context *ev;
780 0 : struct tevent_req *req;
781 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
782 :
783 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
784 : /*
785 : * Can't use sync call while an async call is in flight
786 : */
787 0 : status = NT_STATUS_INVALID_PARAMETER;
788 0 : goto fail;
789 : }
790 0 : ev = samba_tevent_context_init(frame);
791 0 : if (ev == NULL) {
792 0 : goto fail;
793 : }
794 0 : req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
795 0 : if (req == NULL) {
796 0 : goto fail;
797 : }
798 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
799 0 : goto fail;
800 : }
801 0 : status = cli_smb2_delete_on_close_recv(req);
802 0 : fail:
803 0 : TALLOC_FREE(frame);
804 0 : return status;
805 : }
806 :
807 : struct cli_smb2_mkdir_state {
808 : struct tevent_context *ev;
809 : struct cli_state *cli;
810 : };
811 :
812 : static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
813 : static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
814 :
815 3869 : struct tevent_req *cli_smb2_mkdir_send(
816 : TALLOC_CTX *mem_ctx,
817 : struct tevent_context *ev,
818 : struct cli_state *cli,
819 : const char *dname)
820 : {
821 3869 : struct tevent_req *req = NULL, *subreq = NULL;
822 3869 : struct cli_smb2_mkdir_state *state = NULL;
823 :
824 3869 : req = tevent_req_create(
825 : mem_ctx, &state, struct cli_smb2_mkdir_state);
826 3869 : if (req == NULL) {
827 0 : return NULL;
828 : }
829 3869 : state->ev = ev;
830 3869 : state->cli = cli;
831 :
832 : /* Ensure this is a directory. */
833 3869 : subreq = cli_smb2_create_fnum_send(
834 : state, /* mem_ctx */
835 : ev, /* ev */
836 : cli, /* cli */
837 : dname, /* fname */
838 3869 : (struct cli_smb2_create_flags){0}, /* create_flags */
839 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
840 : FILE_READ_ATTRIBUTES, /* desired_access */
841 : FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
842 : FILE_SHARE_READ|
843 : FILE_SHARE_WRITE, /* share_access */
844 : FILE_CREATE, /* create_disposition */
845 : FILE_DIRECTORY_FILE, /* create_options */
846 : NULL); /* in_cblobs */
847 3869 : if (tevent_req_nomem(subreq, req)) {
848 0 : return tevent_req_post(req, ev);
849 : }
850 3869 : tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
851 3869 : return req;
852 : }
853 :
854 3869 : static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
855 : {
856 3869 : struct tevent_req *req = tevent_req_callback_data(
857 : subreq, struct tevent_req);
858 3869 : struct cli_smb2_mkdir_state *state = tevent_req_data(
859 : req, struct cli_smb2_mkdir_state);
860 0 : NTSTATUS status;
861 3869 : uint16_t fnum = 0xffff;
862 :
863 3869 : status = cli_smb2_create_fnum_recv(
864 : subreq, &fnum, NULL, NULL, NULL, NULL);
865 3869 : TALLOC_FREE(subreq);
866 3869 : if (tevent_req_nterror(req, status)) {
867 1353 : return;
868 : }
869 :
870 0 : subreq =
871 2516 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
872 2516 : if (tevent_req_nomem(subreq, req)) {
873 0 : return;
874 : }
875 2516 : tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
876 : }
877 :
878 2516 : static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
879 : {
880 2516 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
881 2516 : tevent_req_simple_finish_ntstatus(subreq, status);
882 2516 : }
883 :
884 3869 : NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
885 : {
886 3869 : return tevent_req_simple_recv_ntstatus(req);
887 : }
888 :
889 : struct cli_smb2_rmdir_state {
890 : struct tevent_context *ev;
891 : struct cli_state *cli;
892 : const char *dname;
893 : const struct smb2_create_blobs *in_cblobs;
894 : uint16_t fnum;
895 : NTSTATUS status;
896 : };
897 :
898 : static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
899 : static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
900 : static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
901 : static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
902 :
903 2742 : struct tevent_req *cli_smb2_rmdir_send(
904 : TALLOC_CTX *mem_ctx,
905 : struct tevent_context *ev,
906 : struct cli_state *cli,
907 : const char *dname,
908 : const struct smb2_create_blobs *in_cblobs)
909 : {
910 2742 : struct tevent_req *req = NULL, *subreq = NULL;
911 2742 : struct cli_smb2_rmdir_state *state = NULL;
912 :
913 2742 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
914 2742 : if (req == NULL) {
915 0 : return NULL;
916 : }
917 2742 : state->ev = ev;
918 2742 : state->cli = cli;
919 2742 : state->dname = dname;
920 2742 : state->in_cblobs = in_cblobs;
921 :
922 2742 : subreq = cli_smb2_create_fnum_send(
923 : state,
924 2742 : state->ev,
925 2742 : state->cli,
926 2742 : state->dname,
927 2742 : (struct cli_smb2_create_flags){0},
928 : SMB2_IMPERSONATION_IMPERSONATION,
929 : DELETE_ACCESS, /* desired_access */
930 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
931 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
932 : FILE_OPEN, /* create_disposition */
933 : FILE_DIRECTORY_FILE, /* create_options */
934 2742 : state->in_cblobs); /* in_cblobs */
935 2742 : if (tevent_req_nomem(subreq, req)) {
936 0 : return tevent_req_post(req, ev);
937 : }
938 2742 : tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
939 2742 : return req;
940 : }
941 :
942 2742 : static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
943 : {
944 2742 : struct tevent_req *req = tevent_req_callback_data(
945 : subreq, struct tevent_req);
946 2742 : struct cli_smb2_rmdir_state *state = tevent_req_data(
947 : req, struct cli_smb2_rmdir_state);
948 0 : NTSTATUS status;
949 :
950 2742 : status = cli_smb2_create_fnum_recv(
951 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
952 2742 : TALLOC_FREE(subreq);
953 :
954 2742 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
955 : /*
956 : * Naive option to match our SMB1 code. Assume the
957 : * symlink path that tripped us up was the last
958 : * component and try again. Eventually we will have to
959 : * deal with the returned path unprocessed component. JRA.
960 : */
961 0 : subreq = cli_smb2_create_fnum_send(
962 : state,
963 : state->ev,
964 : state->cli,
965 : state->dname,
966 0 : (struct cli_smb2_create_flags){0},
967 : SMB2_IMPERSONATION_IMPERSONATION,
968 : DELETE_ACCESS, /* desired_access */
969 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
970 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
971 : FILE_OPEN, /* create_disposition */
972 : FILE_DIRECTORY_FILE|
973 : FILE_DELETE_ON_CLOSE|
974 : FILE_OPEN_REPARSE_POINT, /* create_options */
975 : state->in_cblobs); /* in_cblobs */
976 0 : if (tevent_req_nomem(subreq, req)) {
977 0 : return;
978 : }
979 0 : tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
980 0 : return;
981 : }
982 :
983 2742 : if (tevent_req_nterror(req, status)) {
984 51 : return;
985 : }
986 :
987 2691 : subreq = cli_smb2_delete_on_close_send(
988 2691 : state, state->ev, state->cli, state->fnum, true);
989 2691 : if (tevent_req_nomem(subreq, req)) {
990 0 : return;
991 : }
992 2691 : tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
993 : }
994 :
995 0 : static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
996 : {
997 0 : struct tevent_req *req = tevent_req_callback_data(
998 : subreq, struct tevent_req);
999 0 : struct cli_smb2_rmdir_state *state = tevent_req_data(
1000 : req, struct cli_smb2_rmdir_state);
1001 0 : NTSTATUS status;
1002 :
1003 0 : status = cli_smb2_create_fnum_recv(
1004 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
1005 0 : TALLOC_FREE(subreq);
1006 0 : if (tevent_req_nterror(req, status)) {
1007 0 : return;
1008 : }
1009 :
1010 0 : subreq = cli_smb2_delete_on_close_send(
1011 0 : state, state->ev, state->cli, state->fnum, true);
1012 0 : if (tevent_req_nomem(subreq, req)) {
1013 0 : return;
1014 : }
1015 0 : tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1016 : }
1017 :
1018 2691 : static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1019 : {
1020 2691 : struct tevent_req *req = tevent_req_callback_data(
1021 : subreq, struct tevent_req);
1022 2691 : struct cli_smb2_rmdir_state *state = tevent_req_data(
1023 : req, struct cli_smb2_rmdir_state);
1024 :
1025 2691 : state->status = cli_smb2_delete_on_close_recv(subreq);
1026 2691 : TALLOC_FREE(subreq);
1027 :
1028 : /*
1029 : * Close the fd even if the set_disp failed
1030 : */
1031 :
1032 2691 : subreq = cli_smb2_close_fnum_send(state,
1033 : state->ev,
1034 : state->cli,
1035 2691 : state->fnum,
1036 : 0);
1037 2691 : if (tevent_req_nomem(subreq, req)) {
1038 0 : return;
1039 : }
1040 2691 : tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1041 : }
1042 :
1043 2691 : static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1044 : {
1045 2691 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1046 2691 : tevent_req_simple_finish_ntstatus(subreq, status);
1047 2691 : }
1048 :
1049 2742 : NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1050 : {
1051 2742 : struct cli_smb2_rmdir_state *state = tevent_req_data(
1052 : req, struct cli_smb2_rmdir_state);
1053 0 : NTSTATUS status;
1054 :
1055 2742 : if (tevent_req_is_nterror(req, &status)) {
1056 54 : return status;
1057 : }
1058 2688 : return state->status;
1059 : }
1060 :
1061 : /***************************************************************
1062 : Small wrapper that allows SMB2 to unlink a pathname.
1063 : ***************************************************************/
1064 :
1065 : struct cli_smb2_unlink_state {
1066 : struct tevent_context *ev;
1067 : struct cli_state *cli;
1068 : const char *fname;
1069 : const struct smb2_create_blobs *in_cblobs;
1070 : };
1071 :
1072 : static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1073 : static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1074 : static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1075 :
1076 2752 : struct tevent_req *cli_smb2_unlink_send(
1077 : TALLOC_CTX *mem_ctx,
1078 : struct tevent_context *ev,
1079 : struct cli_state *cli,
1080 : const char *fname,
1081 : const struct smb2_create_blobs *in_cblobs)
1082 : {
1083 2752 : struct tevent_req *req = NULL, *subreq = NULL;
1084 2752 : struct cli_smb2_unlink_state *state = NULL;
1085 :
1086 2752 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1087 2752 : if (req == NULL) {
1088 0 : return NULL;
1089 : }
1090 2752 : state->ev = ev;
1091 2752 : state->cli = cli;
1092 2752 : state->fname = fname;
1093 2752 : state->in_cblobs = in_cblobs;
1094 :
1095 2752 : subreq = cli_smb2_create_fnum_send(
1096 : state, /* mem_ctx */
1097 2752 : state->ev, /* tevent_context */
1098 2752 : state->cli, /* cli_struct */
1099 2752 : state->fname, /* filename */
1100 2752 : (struct cli_smb2_create_flags){0},
1101 : SMB2_IMPERSONATION_IMPERSONATION,
1102 : DELETE_ACCESS, /* desired_access */
1103 : FILE_ATTRIBUTE_NORMAL, /* file attributes */
1104 : FILE_SHARE_READ|
1105 : FILE_SHARE_WRITE|
1106 : FILE_SHARE_DELETE, /* share_access */
1107 : FILE_OPEN, /* create_disposition */
1108 : FILE_DELETE_ON_CLOSE, /* create_options */
1109 2752 : state->in_cblobs); /* in_cblobs */
1110 2752 : if (tevent_req_nomem(subreq, req)) {
1111 0 : return tevent_req_post(req, ev);
1112 : }
1113 2752 : tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1114 2752 : return req;
1115 : }
1116 :
1117 2752 : static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1118 : {
1119 2752 : struct tevent_req *req = tevent_req_callback_data(
1120 : subreq, struct tevent_req);
1121 2752 : struct cli_smb2_unlink_state *state = tevent_req_data(
1122 : req, struct cli_smb2_unlink_state);
1123 2752 : uint16_t fnum = 0xffff;
1124 0 : NTSTATUS status;
1125 :
1126 2752 : status = cli_smb2_create_fnum_recv(
1127 : subreq, &fnum, NULL, NULL, NULL, NULL);
1128 2752 : TALLOC_FREE(subreq);
1129 :
1130 2752 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1131 2752 : NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1132 : /*
1133 : * Naive option to match our SMB1 code. Assume the
1134 : * symlink path that tripped us up was the last
1135 : * component and try again. Eventually we will have to
1136 : * deal with the returned path unprocessed component. JRA.
1137 : */
1138 0 : subreq = cli_smb2_create_fnum_send(
1139 : state, /* mem_ctx */
1140 : state->ev, /* tevent_context */
1141 : state->cli, /* cli_struct */
1142 : state->fname, /* filename */
1143 0 : (struct cli_smb2_create_flags){0},
1144 : SMB2_IMPERSONATION_IMPERSONATION,
1145 : DELETE_ACCESS, /* desired_access */
1146 : FILE_ATTRIBUTE_NORMAL, /* file attributes */
1147 : FILE_SHARE_READ|
1148 : FILE_SHARE_WRITE|
1149 : FILE_SHARE_DELETE, /* share_access */
1150 : FILE_OPEN, /* create_disposition */
1151 : FILE_DELETE_ON_CLOSE|
1152 : FILE_OPEN_REPARSE_POINT, /* create_options */
1153 : state->in_cblobs); /* in_cblobs */
1154 0 : if (tevent_req_nomem(subreq, req)) {
1155 0 : return;
1156 : }
1157 0 : tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1158 0 : return;
1159 : }
1160 :
1161 2752 : if (tevent_req_nterror(req, status)) {
1162 279 : return;
1163 : }
1164 :
1165 0 : subreq =
1166 2473 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1167 2473 : if (tevent_req_nomem(subreq, req)) {
1168 0 : return;
1169 : }
1170 2473 : tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1171 : }
1172 :
1173 0 : static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1174 : {
1175 0 : struct tevent_req *req = tevent_req_callback_data(
1176 : subreq, struct tevent_req);
1177 0 : struct cli_smb2_unlink_state *state = tevent_req_data(
1178 : req, struct cli_smb2_unlink_state);
1179 0 : uint16_t fnum = 0xffff;
1180 0 : NTSTATUS status;
1181 :
1182 0 : status = cli_smb2_create_fnum_recv(
1183 : subreq, &fnum, NULL, NULL, NULL, NULL);
1184 0 : TALLOC_FREE(subreq);
1185 0 : if (tevent_req_nterror(req, status)) {
1186 0 : return;
1187 : }
1188 :
1189 0 : subreq =
1190 0 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1191 0 : if (tevent_req_nomem(subreq, req)) {
1192 0 : return;
1193 : }
1194 0 : tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1195 : }
1196 :
1197 2473 : static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1198 : {
1199 2473 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1200 2473 : tevent_req_simple_finish_ntstatus(subreq, status);
1201 2473 : }
1202 :
1203 2752 : NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1204 : {
1205 2752 : return tevent_req_simple_recv_ntstatus(req);
1206 : }
1207 :
1208 : /***************************************************************
1209 : Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1210 : ***************************************************************/
1211 :
1212 1449 : static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1213 : uint32_t dir_data_length,
1214 : struct file_info *finfo,
1215 : uint32_t *next_offset)
1216 : {
1217 1449 : struct smb3_file_posix_information info = {};
1218 0 : size_t consumed;
1219 0 : enum ndr_err_code ndr_err;
1220 1449 : size_t namelen = 0;
1221 1449 : size_t ret = 0;
1222 1449 : uint32_t _next_offset = 0;
1223 :
1224 1449 : if (dir_data_length < 4) {
1225 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1226 : }
1227 :
1228 1449 : _next_offset = IVAL(dir_data, 0);
1229 :
1230 1449 : if (_next_offset > dir_data_length) {
1231 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1232 : }
1233 :
1234 1449 : if (_next_offset != 0) {
1235 : /* Ensure we only read what in this record. */
1236 1443 : dir_data_length = _next_offset;
1237 : }
1238 :
1239 : /*
1240 : * Skip NextEntryOffset and FileIndex
1241 : */
1242 1449 : if (dir_data_length < 8) {
1243 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1244 : }
1245 1449 : dir_data += 8;
1246 1449 : dir_data_length -= 8;
1247 :
1248 1449 : ndr_err = ndr_pull_struct_blob_noalloc(
1249 : dir_data,
1250 : dir_data_length,
1251 : &info,
1252 : (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1253 : &consumed);
1254 1449 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1255 0 : return ndr_map_error2ntstatus(ndr_err);
1256 : }
1257 1449 : if (consumed > dir_data_length) {
1258 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1259 : }
1260 1449 : dir_data += consumed;
1261 1449 : dir_data_length -= consumed;
1262 :
1263 1449 : finfo->btime_ts = interpret_long_date(info.creation_time);
1264 1449 : finfo->atime_ts = interpret_long_date(info.last_access_time);
1265 1449 : finfo->mtime_ts = interpret_long_date(info.last_write_time);
1266 1449 : finfo->ctime_ts = interpret_long_date(info.change_time);
1267 1449 : finfo->allocated_size = info.allocation_size;
1268 1449 : finfo->size = info.end_of_file;
1269 1449 : finfo->attr = info.file_attributes;
1270 1449 : finfo->ino = info.inode;
1271 1449 : finfo->st_ex_dev = info.device;
1272 1449 : finfo->st_ex_nlink = info.cc.nlinks;
1273 1449 : finfo->reparse_tag = info.cc.reparse_tag;
1274 1449 : finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
1275 1449 : sid_copy(&finfo->owner_sid, &info.cc.owner);
1276 1449 : sid_copy(&finfo->group_sid, &info.cc.group);
1277 :
1278 1449 : if (dir_data_length < 4) {
1279 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1280 : }
1281 1449 : namelen = PULL_LE_U32(dir_data, 0);
1282 :
1283 1449 : dir_data += 4;
1284 1449 : dir_data_length -= 4;
1285 :
1286 1449 : if (namelen > dir_data_length) {
1287 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1288 : }
1289 :
1290 1449 : ret = pull_string_talloc(finfo,
1291 : dir_data,
1292 : FLAGS2_UNICODE_STRINGS,
1293 : &finfo->name,
1294 : dir_data,
1295 : namelen,
1296 : STR_UNICODE);
1297 1449 : if (ret == (size_t)-1) {
1298 : /* Bad conversion. */
1299 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1300 : }
1301 :
1302 1449 : if (finfo->name == NULL) {
1303 : /* Bad conversion. */
1304 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1305 : }
1306 :
1307 1449 : *next_offset = _next_offset;
1308 1449 : return NT_STATUS_OK;
1309 : }
1310 :
1311 : /***************************************************************
1312 : Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1313 : ***************************************************************/
1314 :
1315 58161 : static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1316 : uint32_t dir_data_length,
1317 : struct file_info *finfo,
1318 : uint32_t *next_offset)
1319 : {
1320 58161 : size_t namelen = 0;
1321 58161 : size_t slen = 0;
1322 58161 : size_t ret = 0;
1323 :
1324 58161 : if (dir_data_length < 4) {
1325 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1326 : }
1327 :
1328 58161 : *next_offset = IVAL(dir_data, 0);
1329 :
1330 58161 : if (*next_offset > dir_data_length) {
1331 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1332 : }
1333 :
1334 58161 : if (*next_offset != 0) {
1335 : /* Ensure we only read what in this record. */
1336 49012 : dir_data_length = *next_offset;
1337 : }
1338 :
1339 58161 : if (dir_data_length < 105) {
1340 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1341 : }
1342 :
1343 58161 : finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1344 58161 : finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1345 58161 : finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1346 58161 : finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1347 58161 : finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1348 58161 : finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1349 58161 : finfo->attr = IVAL(dir_data + 56, 0);
1350 58161 : finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1351 58161 : namelen = IVAL(dir_data + 60,0);
1352 58161 : if (namelen > (dir_data_length - 104)) {
1353 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1354 : }
1355 58161 : finfo->reparse_tag = IVAL(dir_data + 64, 0);
1356 58161 : slen = CVAL(dir_data + 68, 0);
1357 58161 : if (slen > 24) {
1358 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1359 : }
1360 58161 : ret = pull_string_talloc(finfo,
1361 : dir_data,
1362 : FLAGS2_UNICODE_STRINGS,
1363 : &finfo->short_name,
1364 58161 : dir_data + 70,
1365 : slen,
1366 : STR_UNICODE);
1367 58161 : if (ret == (size_t)-1) {
1368 : /* Bad conversion. */
1369 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1370 : }
1371 :
1372 58161 : ret = pull_string_talloc(finfo,
1373 : dir_data,
1374 : FLAGS2_UNICODE_STRINGS,
1375 : &finfo->name,
1376 58161 : dir_data + 104,
1377 : namelen,
1378 : STR_UNICODE);
1379 58161 : if (ret == (size_t)-1) {
1380 : /* Bad conversion. */
1381 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1382 : }
1383 :
1384 58161 : if (finfo->name == NULL) {
1385 : /* Bad conversion. */
1386 3 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1387 : }
1388 :
1389 58158 : return NT_STATUS_OK;
1390 : }
1391 :
1392 : /*******************************************************************
1393 : Given a filename - get its directory name
1394 : ********************************************************************/
1395 :
1396 9800 : static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1397 : const char *dir,
1398 : char **parent,
1399 : const char **name)
1400 : {
1401 0 : char *p;
1402 0 : ptrdiff_t len;
1403 :
1404 9800 : p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1405 :
1406 9800 : if (p == NULL) {
1407 97 : if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1408 0 : return false;
1409 : }
1410 97 : if (name) {
1411 97 : *name = dir;
1412 : }
1413 97 : return true;
1414 : }
1415 :
1416 9703 : len = p-dir;
1417 :
1418 9703 : if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1419 0 : return false;
1420 : }
1421 9703 : (*parent)[len] = '\0';
1422 :
1423 9703 : if (name) {
1424 9703 : *name = p+1;
1425 : }
1426 9703 : return true;
1427 : }
1428 :
1429 : struct cli_smb2_list_dir_data {
1430 : uint8_t *data;
1431 : uint32_t length;
1432 : };
1433 :
1434 : struct cli_smb2_list_state {
1435 : struct tevent_context *ev;
1436 : struct cli_state *cli;
1437 : const char *mask;
1438 :
1439 : uint16_t fnum;
1440 :
1441 : NTSTATUS status;
1442 : struct cli_smb2_list_dir_data *response;
1443 : uint32_t offset;
1444 : unsigned int info_level;
1445 : };
1446 :
1447 : static void cli_smb2_list_opened(struct tevent_req *subreq);
1448 : static void cli_smb2_list_done(struct tevent_req *subreq);
1449 : static void cli_smb2_list_closed(struct tevent_req *subreq);
1450 :
1451 9800 : struct tevent_req *cli_smb2_list_send(
1452 : TALLOC_CTX *mem_ctx,
1453 : struct tevent_context *ev,
1454 : struct cli_state *cli,
1455 : const char *pathname,
1456 : unsigned int info_level)
1457 : {
1458 9800 : struct tevent_req *req = NULL, *subreq = NULL;
1459 9800 : struct cli_smb2_list_state *state = NULL;
1460 9800 : char *parent = NULL;
1461 0 : bool ok;
1462 9800 : struct smb2_create_blobs *in_cblobs = NULL;
1463 :
1464 9800 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1465 9800 : if (req == NULL) {
1466 0 : return NULL;
1467 : }
1468 9800 : state->ev = ev;
1469 9800 : state->cli = cli;
1470 9800 : state->status = NT_STATUS_OK;
1471 9800 : state->info_level = info_level;
1472 :
1473 9800 : ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1474 9800 : if (!ok) {
1475 0 : tevent_req_oom(req);
1476 0 : return tevent_req_post(req, ev);
1477 : }
1478 :
1479 9800 : if (smbXcli_conn_have_posix(cli->conn) &&
1480 : info_level == SMB2_FIND_POSIX_INFORMATION)
1481 : {
1482 0 : NTSTATUS status;
1483 :
1484 : /* The mode MUST be 0 when opening an existing file/dir, and
1485 : * will be ignored by the server.
1486 : */
1487 6 : uint8_t linear_mode[4] = { 0 };
1488 6 : DATA_BLOB blob = { .data=linear_mode,
1489 : .length=sizeof(linear_mode) };
1490 :
1491 6 : in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1492 6 : if (in_cblobs == NULL) {
1493 0 : return NULL;
1494 : }
1495 :
1496 6 : status = smb2_create_blob_add(in_cblobs, in_cblobs,
1497 : SMB2_CREATE_TAG_POSIX, blob);
1498 6 : if (tevent_req_nterror(req, status)) {
1499 0 : tevent_req_nterror(req, status);
1500 0 : return tevent_req_post(req, ev);
1501 : }
1502 : }
1503 :
1504 9800 : subreq = cli_smb2_create_fnum_send(
1505 : state, /* mem_ctx */
1506 : ev, /* ev */
1507 : cli, /* cli */
1508 : parent, /* fname */
1509 9800 : (struct cli_smb2_create_flags){0}, /* create_flags */
1510 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1511 : SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1512 : FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1513 : FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1514 : FILE_OPEN, /* create_disposition */
1515 : FILE_DIRECTORY_FILE, /* create_options */
1516 : in_cblobs); /* in_cblobs */
1517 9800 : TALLOC_FREE(in_cblobs);
1518 9800 : if (tevent_req_nomem(subreq, req)) {
1519 0 : return tevent_req_post(req, ev);
1520 : }
1521 9800 : tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1522 9800 : return req;
1523 : }
1524 :
1525 9800 : static void cli_smb2_list_opened(struct tevent_req *subreq)
1526 : {
1527 9800 : struct tevent_req *req = tevent_req_callback_data(
1528 : subreq, struct tevent_req);
1529 9800 : struct cli_smb2_list_state *state = tevent_req_data(
1530 : req, struct cli_smb2_list_state);
1531 0 : NTSTATUS status;
1532 :
1533 9800 : status = cli_smb2_create_fnum_recv(
1534 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
1535 9800 : TALLOC_FREE(subreq);
1536 9800 : if (tevent_req_nterror(req, status)) {
1537 376 : return;
1538 : }
1539 :
1540 : /*
1541 : * Make our caller get back to us via cli_smb2_list_recv(),
1542 : * triggering the smb2_query_directory_send()
1543 : */
1544 9424 : tevent_req_defer_callback(req, state->ev);
1545 9424 : tevent_req_notify_callback(req);
1546 : }
1547 :
1548 18576 : static void cli_smb2_list_done(struct tevent_req *subreq)
1549 : {
1550 18576 : struct tevent_req *req = tevent_req_callback_data(
1551 : subreq, struct tevent_req);
1552 18576 : struct cli_smb2_list_state *state = tevent_req_data(
1553 : req, struct cli_smb2_list_state);
1554 18576 : struct cli_smb2_list_dir_data *response = NULL;
1555 :
1556 18576 : response = talloc(state, struct cli_smb2_list_dir_data);
1557 18576 : if (tevent_req_nomem(response, req)) {
1558 0 : return;
1559 : }
1560 :
1561 18576 : state->status = smb2cli_query_directory_recv(
1562 : subreq, response, &response->data, &response->length);
1563 18576 : TALLOC_FREE(subreq);
1564 :
1565 18576 : if (NT_STATUS_IS_OK(state->status)) {
1566 9155 : state->response = response;
1567 9155 : state->offset = 0;
1568 :
1569 9155 : tevent_req_defer_callback(req, state->ev);
1570 9155 : tevent_req_notify_callback(req);
1571 9155 : return;
1572 : }
1573 :
1574 9421 : TALLOC_FREE(response);
1575 :
1576 9421 : subreq = cli_smb2_close_fnum_send(state,
1577 : state->ev,
1578 : state->cli,
1579 9421 : state->fnum,
1580 : 0);
1581 9421 : if (tevent_req_nomem(subreq, req)) {
1582 0 : return;
1583 : }
1584 9421 : tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1585 : }
1586 :
1587 9421 : static void cli_smb2_list_closed(struct tevent_req *subreq)
1588 : {
1589 9421 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1590 9421 : tevent_req_simple_finish_ntstatus(subreq, status);
1591 9421 : }
1592 :
1593 : /*
1594 : * Return the next finfo directory.
1595 : *
1596 : * This parses the blob returned from QUERY_DIRECTORY step by step. If
1597 : * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1598 : * NT_STATUS_RETRY, which will then trigger the caller again when the
1599 : * QUERY_DIRECTORY has returned with another buffer. This way we
1600 : * guarantee that no asynchronous request is open after this call
1601 : * returns an entry, so that other synchronous requests can be issued
1602 : * on the same connection while the directory listing proceeds.
1603 : */
1604 87983 : NTSTATUS cli_smb2_list_recv(
1605 : struct tevent_req *req,
1606 : TALLOC_CTX *mem_ctx,
1607 : struct file_info **pfinfo)
1608 : {
1609 87983 : struct cli_smb2_list_state *state = tevent_req_data(
1610 : req, struct cli_smb2_list_state);
1611 87983 : struct cli_smb2_list_dir_data *response = NULL;
1612 87983 : struct file_info *finfo = NULL;
1613 0 : NTSTATUS status;
1614 87983 : uint32_t next_offset = 0;
1615 0 : bool in_progress;
1616 :
1617 87983 : in_progress = tevent_req_is_in_progress(req);
1618 :
1619 87983 : if (!in_progress) {
1620 9797 : if (!tevent_req_is_nterror(req, &status)) {
1621 9421 : status = NT_STATUS_NO_MORE_FILES;
1622 : }
1623 9797 : goto fail;
1624 : }
1625 :
1626 78186 : response = state->response;
1627 78186 : if (response == NULL) {
1628 18576 : struct tevent_req *subreq = NULL;
1629 18576 : struct cli_state *cli = state->cli;
1630 18576 : struct smb2_hnd *ph = NULL;
1631 0 : uint32_t max_trans, max_avail_len;
1632 0 : bool ok;
1633 :
1634 18576 : if (!NT_STATUS_IS_OK(state->status)) {
1635 0 : status = state->status;
1636 0 : goto fail;
1637 : }
1638 :
1639 18576 : status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1640 18576 : if (!NT_STATUS_IS_OK(status)) {
1641 0 : goto fail;
1642 : }
1643 :
1644 18576 : max_trans = smb2cli_conn_max_trans_size(cli->conn);
1645 18576 : ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1646 18576 : if (ok) {
1647 18576 : max_trans = MIN(max_trans, max_avail_len);
1648 : }
1649 :
1650 18576 : subreq = smb2cli_query_directory_send(
1651 : state, /* mem_ctx */
1652 : state->ev, /* ev */
1653 : cli->conn, /* conn */
1654 18576 : cli->timeout, /* timeout_msec */
1655 : cli->smb2.session, /* session */
1656 : cli->smb2.tcon, /* tcon */
1657 18576 : state->info_level, /* level */
1658 : 0, /* flags */
1659 : 0, /* file_index */
1660 18576 : ph->fid_persistent, /* fid_persistent */
1661 18576 : ph->fid_volatile, /* fid_volatile */
1662 : state->mask, /* mask */
1663 : max_trans); /* outbuf_len */
1664 18576 : if (subreq == NULL) {
1665 0 : status = NT_STATUS_NO_MEMORY;
1666 0 : goto fail;
1667 : }
1668 18576 : tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1669 18576 : return NT_STATUS_RETRY;
1670 : }
1671 :
1672 59610 : SMB_ASSERT(response->length > state->offset);
1673 :
1674 59610 : finfo = talloc_zero(mem_ctx, struct file_info);
1675 59610 : if (finfo == NULL) {
1676 0 : status = NT_STATUS_NO_MEMORY;
1677 0 : goto fail;
1678 : }
1679 :
1680 59610 : if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1681 1449 : status = parse_finfo_posix_info(
1682 1449 : response->data + state->offset,
1683 1449 : response->length - state->offset,
1684 : finfo,
1685 : &next_offset);
1686 : } else {
1687 58161 : status = parse_finfo_id_both_directory_info(
1688 58161 : response->data + state->offset,
1689 58161 : response->length - state->offset,
1690 : finfo,
1691 : &next_offset);
1692 : }
1693 59610 : if (!NT_STATUS_IS_OK(status)) {
1694 3 : goto fail;
1695 : }
1696 :
1697 59607 : status = is_bad_finfo_name(state->cli, finfo);
1698 59607 : if (!NT_STATUS_IS_OK(status)) {
1699 0 : goto fail;
1700 : }
1701 :
1702 : /*
1703 : * parse_finfo_id_both_directory_info() checks for overflow,
1704 : * no need to check again here.
1705 : */
1706 59607 : state->offset += next_offset;
1707 :
1708 59607 : if (next_offset == 0) {
1709 9152 : TALLOC_FREE(state->response);
1710 : }
1711 :
1712 59607 : tevent_req_defer_callback(req, state->ev);
1713 59607 : tevent_req_notify_callback(req);
1714 :
1715 59607 : *pfinfo = finfo;
1716 59607 : return NT_STATUS_OK;
1717 :
1718 9800 : fail:
1719 9800 : TALLOC_FREE(finfo);
1720 9800 : tevent_req_received(req);
1721 9800 : return status;
1722 : }
1723 :
1724 : /***************************************************************
1725 : Wrapper that allows SMB2 to query a path info (basic level).
1726 : Synchronous only.
1727 : ***************************************************************/
1728 :
1729 9968 : NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1730 : const char *name,
1731 : SMB_STRUCT_STAT *sbuf,
1732 : uint32_t *attributes)
1733 : {
1734 0 : NTSTATUS status;
1735 0 : struct smb_create_returns cr;
1736 9968 : uint16_t fnum = 0xffff;
1737 9968 : size_t namelen = strlen(name);
1738 :
1739 9968 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1740 : /*
1741 : * Can't use sync call while an async call is in flight
1742 : */
1743 0 : return NT_STATUS_INVALID_PARAMETER;
1744 : }
1745 :
1746 : /* SMB2 is pickier about pathnames. Ensure it doesn't
1747 : end in a '\' */
1748 9968 : if (namelen > 0 && name[namelen-1] == '\\') {
1749 168 : char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1750 168 : if (modname == NULL) {
1751 0 : return NT_STATUS_NO_MEMORY;
1752 : }
1753 168 : name = modname;
1754 : }
1755 :
1756 : /* This is commonly used as a 'cd'. Try qpathinfo on
1757 : a directory handle first. */
1758 :
1759 9968 : status = cli_smb2_create_fnum(cli,
1760 : name,
1761 9968 : (struct cli_smb2_create_flags){0},
1762 : SMB2_IMPERSONATION_IMPERSONATION,
1763 : FILE_READ_ATTRIBUTES, /* desired_access */
1764 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1765 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1766 : FILE_OPEN, /* create_disposition */
1767 : FILE_DIRECTORY_FILE, /* create_options */
1768 : NULL,
1769 : &fnum,
1770 : &cr,
1771 : NULL,
1772 : NULL);
1773 :
1774 9968 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1775 : /* Maybe a file ? */
1776 2552 : status = cli_smb2_create_fnum(cli,
1777 : name,
1778 2552 : (struct cli_smb2_create_flags){0},
1779 : SMB2_IMPERSONATION_IMPERSONATION,
1780 : FILE_READ_ATTRIBUTES, /* desired_access */
1781 : 0, /* file attributes */
1782 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1783 : FILE_OPEN, /* create_disposition */
1784 : 0, /* create_options */
1785 : NULL,
1786 : &fnum,
1787 : &cr,
1788 : NULL,
1789 : NULL);
1790 : }
1791 :
1792 9968 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1793 : /* Maybe a reparse point ? */
1794 0 : status = cli_smb2_create_fnum(cli,
1795 : name,
1796 0 : (struct cli_smb2_create_flags){0},
1797 : SMB2_IMPERSONATION_IMPERSONATION,
1798 : FILE_READ_ATTRIBUTES, /* desired_access */
1799 : 0, /* file attributes */
1800 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1801 : FILE_OPEN, /* create_disposition */
1802 : FILE_OPEN_REPARSE_POINT, /* create_options */
1803 : NULL,
1804 : &fnum,
1805 : &cr,
1806 : NULL,
1807 : NULL);
1808 : }
1809 :
1810 9968 : if (!NT_STATUS_IS_OK(status)) {
1811 4445 : return status;
1812 : }
1813 :
1814 5523 : status = cli_smb2_close_fnum(cli, fnum);
1815 :
1816 5523 : ZERO_STRUCTP(sbuf);
1817 :
1818 5523 : sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1819 5523 : sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1820 5523 : sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1821 5523 : sbuf->st_ex_size = cr.end_of_file;
1822 5523 : *attributes = cr.file_attributes;
1823 :
1824 5523 : return status;
1825 : }
1826 :
1827 : struct cli_smb2_query_info_fnum_state {
1828 : DATA_BLOB outbuf;
1829 : };
1830 :
1831 : static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1832 :
1833 14427 : struct tevent_req *cli_smb2_query_info_fnum_send(
1834 : TALLOC_CTX *mem_ctx,
1835 : struct tevent_context *ev,
1836 : struct cli_state *cli,
1837 : uint16_t fnum,
1838 : uint8_t in_info_type,
1839 : uint8_t in_info_class,
1840 : uint32_t in_max_output_length,
1841 : const DATA_BLOB *in_input_buffer,
1842 : uint32_t in_additional_info,
1843 : uint32_t in_flags)
1844 : {
1845 14427 : struct tevent_req *req = NULL, *subreq = NULL;
1846 14427 : struct cli_smb2_query_info_fnum_state *state = NULL;
1847 14427 : struct smb2_hnd *ph = NULL;
1848 0 : NTSTATUS status;
1849 :
1850 14427 : req = tevent_req_create(
1851 : mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1852 14427 : if (req == NULL) {
1853 0 : return req;
1854 : }
1855 :
1856 14427 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1857 14427 : if (tevent_req_nterror(req, status)) {
1858 6 : return tevent_req_post(req, ev);
1859 : }
1860 :
1861 14421 : subreq = smb2cli_query_info_send(
1862 : state,
1863 : ev,
1864 : cli->conn,
1865 14421 : cli->timeout,
1866 : cli->smb2.session,
1867 : cli->smb2.tcon,
1868 : in_info_type,
1869 : in_info_class,
1870 : in_max_output_length,
1871 : in_input_buffer,
1872 : in_additional_info,
1873 : in_flags,
1874 14421 : ph->fid_persistent,
1875 14421 : ph->fid_volatile);
1876 14421 : if (tevent_req_nomem(subreq, req)) {
1877 0 : return tevent_req_post(req, ev);
1878 : }
1879 14421 : tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1880 14421 : return req;
1881 : }
1882 :
1883 14421 : static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1884 : {
1885 14421 : struct tevent_req *req = tevent_req_callback_data(
1886 : subreq, struct tevent_req);
1887 14421 : struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1888 : req, struct cli_smb2_query_info_fnum_state);
1889 0 : DATA_BLOB outbuf;
1890 0 : NTSTATUS status;
1891 :
1892 14421 : status = smb2cli_query_info_recv(subreq, state, &outbuf);
1893 14421 : TALLOC_FREE(subreq);
1894 14421 : if (tevent_req_nterror(req, status)) {
1895 43 : return;
1896 : }
1897 :
1898 : /*
1899 : * We have to dup the memory here because outbuf.data is not
1900 : * returned as a talloc object by smb2cli_query_info_recv.
1901 : * It's a pointer into the received buffer.
1902 : */
1903 14378 : state->outbuf = data_blob_dup_talloc(state, outbuf);
1904 :
1905 28590 : if ((outbuf.length != 0) &&
1906 14212 : tevent_req_nomem(state->outbuf.data, req)) {
1907 0 : return;
1908 : }
1909 14378 : tevent_req_done(req);
1910 : }
1911 :
1912 14427 : NTSTATUS cli_smb2_query_info_fnum_recv(
1913 : struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1914 : {
1915 14427 : struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1916 : req, struct cli_smb2_query_info_fnum_state);
1917 0 : NTSTATUS status;
1918 :
1919 14427 : if (tevent_req_is_nterror(req, &status)) {
1920 49 : return status;
1921 : }
1922 14378 : *outbuf = (DATA_BLOB) {
1923 14378 : .data = talloc_move(mem_ctx, &state->outbuf.data),
1924 14378 : .length = state->outbuf.length,
1925 : };
1926 14378 : return NT_STATUS_OK;
1927 : }
1928 :
1929 1381 : NTSTATUS cli_smb2_query_info_fnum(
1930 : struct cli_state *cli,
1931 : uint16_t fnum,
1932 : uint8_t in_info_type,
1933 : uint8_t in_info_class,
1934 : uint32_t in_max_output_length,
1935 : const DATA_BLOB *in_input_buffer,
1936 : uint32_t in_additional_info,
1937 : uint32_t in_flags,
1938 : TALLOC_CTX *mem_ctx,
1939 : DATA_BLOB *outbuf)
1940 : {
1941 1381 : TALLOC_CTX *frame = talloc_stackframe();
1942 1381 : struct tevent_context *ev = NULL;
1943 1381 : struct tevent_req *req = NULL;
1944 1381 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1945 0 : bool ok;
1946 :
1947 1381 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1948 : /*
1949 : * Can't use sync call while an async call is in flight
1950 : */
1951 0 : status = NT_STATUS_INVALID_PARAMETER;
1952 0 : goto fail;
1953 : }
1954 1381 : ev = samba_tevent_context_init(frame);
1955 1381 : if (ev == NULL) {
1956 0 : goto fail;
1957 : }
1958 1381 : req = cli_smb2_query_info_fnum_send(
1959 : frame,
1960 : ev,
1961 : cli,
1962 : fnum,
1963 : in_info_type,
1964 : in_info_class,
1965 : in_max_output_length,
1966 : in_input_buffer,
1967 : in_additional_info,
1968 : in_flags);
1969 1381 : if (req == NULL) {
1970 0 : goto fail;
1971 : }
1972 1381 : ok = tevent_req_poll_ntstatus(req, ev, &status);
1973 1381 : if (!ok) {
1974 0 : goto fail;
1975 : }
1976 1381 : status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1977 1381 : fail:
1978 1381 : TALLOC_FREE(frame);
1979 1381 : return status;
1980 : }
1981 :
1982 : /***************************************************************
1983 : Helper function for pathname operations.
1984 : ***************************************************************/
1985 :
1986 : struct get_fnum_from_path_state {
1987 : struct tevent_context *ev;
1988 : struct cli_state *cli;
1989 : const char *name;
1990 : uint32_t desired_access;
1991 : uint16_t fnum;
1992 : };
1993 :
1994 : static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1995 : static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1996 : static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1997 :
1998 9155 : static struct tevent_req *get_fnum_from_path_send(
1999 : TALLOC_CTX *mem_ctx,
2000 : struct tevent_context *ev,
2001 : struct cli_state *cli,
2002 : const char *name,
2003 : uint32_t desired_access)
2004 : {
2005 9155 : struct tevent_req *req = NULL, *subreq = NULL;
2006 9155 : struct get_fnum_from_path_state *state = NULL;
2007 9155 : size_t namelen = strlen(name);
2008 :
2009 9155 : req = tevent_req_create(
2010 : mem_ctx, &state, struct get_fnum_from_path_state);
2011 9155 : if (req == NULL) {
2012 0 : return NULL;
2013 : }
2014 9155 : state->ev = ev;
2015 9155 : state->cli = cli;
2016 9155 : state->name = name;
2017 9155 : state->desired_access = desired_access;
2018 :
2019 : /*
2020 : * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2021 : * '\'
2022 : */
2023 9155 : if (namelen > 0 && name[namelen-1] == '\\') {
2024 149 : state->name = talloc_strndup(state, name, namelen-1);
2025 149 : if (tevent_req_nomem(state->name, req)) {
2026 0 : return tevent_req_post(req, ev);
2027 : }
2028 : }
2029 :
2030 9155 : subreq = cli_smb2_create_fnum_send(
2031 : state, /* mem_ctx, */
2032 : ev, /* ev */
2033 : cli, /* cli */
2034 9155 : state->name, /* fname */
2035 9155 : (struct cli_smb2_create_flags){0}, /* create_flags */
2036 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2037 : desired_access, /* desired_access */
2038 : 0, /* file_attributes */
2039 : FILE_SHARE_READ|
2040 : FILE_SHARE_WRITE|
2041 : FILE_SHARE_DELETE, /* share_access */
2042 : FILE_OPEN, /* create_disposition */
2043 : 0, /* create_options */
2044 : NULL); /* in_cblobs */
2045 9155 : if (tevent_req_nomem(subreq, req)) {
2046 0 : return tevent_req_post(req, ev);
2047 : }
2048 9155 : tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2049 9155 : return req;
2050 : }
2051 :
2052 9155 : static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2053 : {
2054 9155 : struct tevent_req *req = tevent_req_callback_data(
2055 : subreq, struct tevent_req);
2056 9155 : struct get_fnum_from_path_state *state = tevent_req_data(
2057 : req, struct get_fnum_from_path_state);
2058 0 : NTSTATUS status;
2059 :
2060 9155 : status = cli_smb2_create_fnum_recv(
2061 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
2062 9155 : TALLOC_FREE(subreq);
2063 :
2064 9155 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2065 9155 : NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2066 : /*
2067 : * Naive option to match our SMB1 code. Assume the
2068 : * symlink path that tripped us up was the last
2069 : * component and try again. Eventually we will have to
2070 : * deal with the returned path unprocessed component. JRA.
2071 : */
2072 0 : subreq = cli_smb2_create_fnum_send(
2073 : state, /* mem_ctx, */
2074 : state->ev, /* ev */
2075 : state->cli, /* cli */
2076 : state->name, /* fname */
2077 0 : (struct cli_smb2_create_flags){0}, /* create_flags */
2078 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2079 : state->desired_access, /* desired_access */
2080 : 0, /* file_attributes */
2081 : FILE_SHARE_READ|
2082 : FILE_SHARE_WRITE|
2083 : FILE_SHARE_DELETE, /* share_access */
2084 : FILE_OPEN, /* create_disposition */
2085 : FILE_OPEN_REPARSE_POINT, /* create_options */
2086 : NULL); /* in_cblobs */
2087 0 : if (tevent_req_nomem(subreq, req)) {
2088 0 : return;
2089 : }
2090 0 : tevent_req_set_callback(
2091 : subreq, get_fnum_from_path_opened_reparse, req);
2092 0 : return;
2093 : }
2094 :
2095 9155 : if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2096 0 : subreq = cli_smb2_create_fnum_send(
2097 : state, /* mem_ctx, */
2098 : state->ev, /* ev */
2099 : state->cli, /* cli */
2100 : state->name, /* fname */
2101 0 : (struct cli_smb2_create_flags){0}, /* create_flags */
2102 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2103 : state->desired_access, /* desired_access */
2104 : 0, /* file_attributes */
2105 : FILE_SHARE_READ|
2106 : FILE_SHARE_WRITE|
2107 : FILE_SHARE_DELETE, /* share_access */
2108 : FILE_OPEN, /* create_disposition */
2109 : FILE_DIRECTORY_FILE, /* create_options */
2110 : NULL); /* in_cblobs */
2111 0 : if (tevent_req_nomem(subreq, req)) {
2112 0 : return;
2113 : }
2114 0 : tevent_req_set_callback(
2115 : subreq, get_fnum_from_path_opened_dir, req);
2116 0 : return;
2117 : }
2118 :
2119 9155 : if (tevent_req_nterror(req, status)) {
2120 915 : return;
2121 : }
2122 8240 : tevent_req_done(req);
2123 : }
2124 :
2125 0 : static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2126 : {
2127 0 : struct tevent_req *req = tevent_req_callback_data(
2128 : subreq, struct tevent_req);
2129 0 : struct get_fnum_from_path_state *state = tevent_req_data(
2130 : req, struct get_fnum_from_path_state);
2131 0 : NTSTATUS status = cli_smb2_create_fnum_recv(
2132 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
2133 0 : tevent_req_simple_finish_ntstatus(subreq, status);
2134 0 : }
2135 :
2136 0 : static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2137 : {
2138 : /* Abstraction violation, but these two are just the same... */
2139 0 : get_fnum_from_path_opened_reparse(subreq);
2140 0 : }
2141 :
2142 9155 : static NTSTATUS get_fnum_from_path_recv(
2143 : struct tevent_req *req, uint16_t *pfnum)
2144 : {
2145 9155 : struct get_fnum_from_path_state *state = tevent_req_data(
2146 : req, struct get_fnum_from_path_state);
2147 9155 : NTSTATUS status = NT_STATUS_OK;
2148 :
2149 9155 : if (!tevent_req_is_nterror(req, &status)) {
2150 8240 : *pfnum = state->fnum;
2151 : }
2152 9155 : tevent_req_received(req);
2153 9155 : return status;
2154 : }
2155 :
2156 1836 : static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2157 : const char *name,
2158 : uint32_t desired_access,
2159 : uint16_t *pfnum)
2160 : {
2161 1836 : TALLOC_CTX *frame = talloc_stackframe();
2162 1836 : struct tevent_context *ev = NULL;
2163 1836 : struct tevent_req *req = NULL;
2164 1836 : NTSTATUS status = NT_STATUS_NO_MEMORY;
2165 :
2166 1836 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2167 0 : status = NT_STATUS_INVALID_PARAMETER;
2168 0 : goto fail;
2169 : }
2170 1836 : ev = samba_tevent_context_init(frame);
2171 1836 : if (ev == NULL) {
2172 0 : goto fail;
2173 : }
2174 1836 : req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2175 1836 : if (req == NULL) {
2176 0 : goto fail;
2177 : }
2178 1836 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2179 0 : goto fail;
2180 : }
2181 1836 : status = get_fnum_from_path_recv(req, pfnum);
2182 1836 : fail:
2183 1836 : TALLOC_FREE(frame);
2184 1836 : return status;
2185 : }
2186 :
2187 : struct cli_smb2_qpathinfo_state {
2188 : struct tevent_context *ev;
2189 : struct cli_state *cli;
2190 : const char *fname;
2191 : uint16_t fnum;
2192 : uint16_t level;
2193 : uint32_t min_rdata;
2194 : uint32_t max_rdata;
2195 :
2196 : NTSTATUS status;
2197 : DATA_BLOB out;
2198 : };
2199 :
2200 : static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2201 : static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2202 : static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2203 :
2204 7028 : struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2205 : struct tevent_context *ev,
2206 : struct cli_state *cli,
2207 : const char *fname,
2208 : uint16_t level,
2209 : uint32_t min_rdata,
2210 : uint32_t max_rdata)
2211 : {
2212 7028 : struct tevent_req *req = NULL, *subreq = NULL;
2213 7028 : struct cli_smb2_qpathinfo_state *state = NULL;
2214 :
2215 7028 : req = tevent_req_create(mem_ctx,
2216 : &state,
2217 : struct cli_smb2_qpathinfo_state);
2218 7028 : if (req == NULL) {
2219 0 : return NULL;
2220 : }
2221 7028 : state->ev = ev;
2222 7028 : state->cli = cli;
2223 7028 : state->level = level;
2224 7028 : state->min_rdata = min_rdata;
2225 7028 : state->max_rdata = max_rdata;
2226 :
2227 7028 : subreq = get_fnum_from_path_send(state,
2228 : ev,
2229 : cli,
2230 : fname,
2231 : FILE_READ_ATTRIBUTES);
2232 7028 : if (tevent_req_nomem(subreq, req)) {
2233 0 : return tevent_req_post(req, ev);
2234 : }
2235 7028 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2236 7028 : return req;
2237 : }
2238 :
2239 7028 : static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2240 : {
2241 0 : struct tevent_req *req =
2242 7028 : tevent_req_callback_data(subreq, struct tevent_req);
2243 0 : struct cli_smb2_qpathinfo_state *state =
2244 7028 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2245 0 : NTSTATUS status;
2246 :
2247 7028 : status = get_fnum_from_path_recv(subreq, &state->fnum);
2248 7028 : TALLOC_FREE(subreq);
2249 7028 : if (tevent_req_nterror(req, status)) {
2250 909 : return;
2251 : }
2252 :
2253 6119 : subreq = cli_smb2_query_info_fnum_send(state,
2254 : state->ev,
2255 : state->cli,
2256 6119 : state->fnum,
2257 : 1, /* in_info_type */
2258 6119 : state->level,
2259 : state->max_rdata,
2260 : NULL, /* in_input_buffer */
2261 : 0, /* in_additional_info */
2262 : 0); /* in_flags */
2263 6119 : if (tevent_req_nomem(subreq, req)) {
2264 0 : return;
2265 : }
2266 6119 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2267 : }
2268 :
2269 6119 : static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2270 : {
2271 0 : struct tevent_req *req =
2272 6119 : tevent_req_callback_data(subreq, struct tevent_req);
2273 0 : struct cli_smb2_qpathinfo_state *state =
2274 6119 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2275 :
2276 0 : state->status =
2277 6119 : cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2278 6119 : TALLOC_FREE(subreq);
2279 :
2280 6119 : if (NT_STATUS_IS_OK(state->status) &&
2281 6091 : (state->out.length < state->min_rdata)) {
2282 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2283 : }
2284 :
2285 6119 : subreq = cli_smb2_close_fnum_send(state,
2286 : state->ev,
2287 : state->cli,
2288 6119 : state->fnum,
2289 : 0);
2290 6119 : if (tevent_req_nomem(subreq, req)) {
2291 0 : return;
2292 : }
2293 6119 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2294 : }
2295 :
2296 6119 : static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2297 : {
2298 0 : struct tevent_req *req =
2299 6119 : tevent_req_callback_data(subreq, struct tevent_req);
2300 0 : struct cli_smb2_qpathinfo_state *state =
2301 6119 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2302 0 : NTSTATUS status;
2303 :
2304 6119 : status = cli_smb2_close_fnum_recv(subreq);
2305 6119 : TALLOC_FREE(subreq);
2306 6119 : if (tevent_req_nterror(req, status)) {
2307 28 : return;
2308 : }
2309 6119 : if (tevent_req_nterror(req, state->status)) {
2310 28 : return;
2311 : }
2312 6091 : tevent_req_done(req);
2313 : }
2314 :
2315 7028 : NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2316 : TALLOC_CTX *mem_ctx,
2317 : uint8_t **rdata,
2318 : uint32_t *num_rdata)
2319 : {
2320 0 : struct cli_smb2_qpathinfo_state *state =
2321 7028 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2322 0 : NTSTATUS status;
2323 :
2324 7028 : if (tevent_req_is_nterror(req, &status)) {
2325 937 : return status;
2326 : }
2327 :
2328 6091 : *rdata = talloc_move(mem_ctx, &state->out.data);
2329 6091 : *num_rdata = state->out.length;
2330 6091 : tevent_req_received(req);
2331 6091 : return NT_STATUS_OK;
2332 : }
2333 :
2334 : /***************************************************************
2335 : Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2336 : a pathname.
2337 : Synchronous only.
2338 : ***************************************************************/
2339 :
2340 1836 : NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2341 : const char *name,
2342 : uint8_t in_info_type,
2343 : uint8_t in_file_info_class,
2344 : const DATA_BLOB *p_in_data)
2345 : {
2346 0 : NTSTATUS status;
2347 1836 : uint16_t fnum = 0xffff;
2348 1836 : TALLOC_CTX *frame = talloc_stackframe();
2349 :
2350 1836 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2351 : /*
2352 : * Can't use sync call while an async call is in flight
2353 : */
2354 0 : status = NT_STATUS_INVALID_PARAMETER;
2355 0 : goto fail;
2356 : }
2357 :
2358 1836 : status = get_fnum_from_path(cli,
2359 : name,
2360 : FILE_WRITE_ATTRIBUTES,
2361 : &fnum);
2362 :
2363 1836 : if (!NT_STATUS_IS_OK(status)) {
2364 6 : goto fail;
2365 : }
2366 :
2367 1830 : status = cli_smb2_set_info_fnum(
2368 : cli,
2369 : fnum,
2370 : in_info_type,
2371 : in_file_info_class,
2372 : p_in_data, /* in_input_buffer */
2373 : 0); /* in_additional_info */
2374 1836 : fail:
2375 :
2376 1836 : if (fnum != 0xffff) {
2377 1830 : cli_smb2_close_fnum(cli, fnum);
2378 : }
2379 :
2380 1836 : cli->raw_status = status;
2381 :
2382 1836 : TALLOC_FREE(frame);
2383 1836 : return status;
2384 : }
2385 :
2386 :
2387 : /***************************************************************
2388 : Wrapper that allows SMB2 to set pathname attributes.
2389 : Synchronous only.
2390 : ***************************************************************/
2391 :
2392 1820 : NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2393 : const char *name,
2394 : uint32_t attr,
2395 : time_t mtime)
2396 : {
2397 0 : uint8_t inbuf_store[40];
2398 1820 : DATA_BLOB inbuf = data_blob_null;
2399 :
2400 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2401 : level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2402 :
2403 1820 : inbuf.data = inbuf_store;
2404 1820 : inbuf.length = sizeof(inbuf_store);
2405 1820 : data_blob_clear(&inbuf);
2406 :
2407 : /*
2408 : * SMB1 uses attr == 0 to clear all attributes
2409 : * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2410 : * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2411 : * request attribute change.
2412 : *
2413 : * SMB2 uses exactly the reverse. Unfortunately as the
2414 : * cli_setatr() ABI is exposed inside libsmbclient,
2415 : * we must make the SMB2 cli_smb2_setatr() call
2416 : * export the same ABI as the SMB1 cli_setatr()
2417 : * which calls it. This means reversing the sense
2418 : * of the requested attr argument if it's zero
2419 : * or FILE_ATTRIBUTE_NORMAL.
2420 : *
2421 : * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2422 : */
2423 :
2424 1820 : if (attr == 0) {
2425 157 : attr = FILE_ATTRIBUTE_NORMAL;
2426 1663 : } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2427 1014 : attr = 0;
2428 : }
2429 :
2430 1820 : SIVAL(inbuf.data, 32, attr);
2431 1820 : if (mtime != 0) {
2432 138 : put_long_date((char *)inbuf.data + 16,mtime);
2433 : }
2434 : /* Set all the other times to -1. */
2435 1820 : SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2436 1820 : SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2437 1820 : SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2438 :
2439 1820 : return cli_smb2_setpathinfo(cli,
2440 : name,
2441 : 1, /* in_info_type */
2442 : /* in_file_info_class */
2443 : SMB_FILE_BASIC_INFORMATION - 1000,
2444 : &inbuf);
2445 : }
2446 :
2447 :
2448 : /***************************************************************
2449 : Wrapper that allows SMB2 to set file handle times.
2450 : Synchronous only.
2451 : ***************************************************************/
2452 :
2453 0 : NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2454 : uint16_t fnum,
2455 : time_t change_time,
2456 : time_t access_time,
2457 : time_t write_time)
2458 : {
2459 0 : uint8_t inbuf_store[40];
2460 0 : DATA_BLOB inbuf = data_blob_null;
2461 0 : NTSTATUS status;
2462 :
2463 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2464 : /*
2465 : * Can't use sync call while an async call is in flight
2466 : */
2467 0 : return NT_STATUS_INVALID_PARAMETER;
2468 : }
2469 :
2470 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2471 : level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2472 :
2473 0 : inbuf.data = inbuf_store;
2474 0 : inbuf.length = sizeof(inbuf_store);
2475 0 : data_blob_clear(&inbuf);
2476 :
2477 0 : SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2478 0 : if (change_time != 0) {
2479 0 : put_long_date((char *)inbuf.data + 24, change_time);
2480 : }
2481 0 : if (access_time != 0) {
2482 0 : put_long_date((char *)inbuf.data + 8, access_time);
2483 : }
2484 0 : if (write_time != 0) {
2485 0 : put_long_date((char *)inbuf.data + 16, write_time);
2486 : }
2487 :
2488 0 : status = cli_smb2_set_info_fnum(cli,
2489 : fnum,
2490 : 1, /* in_info_type */
2491 : SMB_FILE_BASIC_INFORMATION -
2492 : 1000, /* in_file_info_class */
2493 : &inbuf, /* in_input_buffer */
2494 : 0); /* in_additional_info */
2495 0 : cli->raw_status = status;
2496 0 : return status;
2497 : }
2498 :
2499 : /***************************************************************
2500 : Wrapper that allows SMB2 to query disk attributes (size).
2501 : Synchronous only.
2502 : ***************************************************************/
2503 :
2504 1245 : NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2505 : uint64_t *bsize, uint64_t *total, uint64_t *avail)
2506 : {
2507 0 : NTSTATUS status;
2508 1245 : uint16_t fnum = 0xffff;
2509 1245 : DATA_BLOB outbuf = data_blob_null;
2510 1245 : uint32_t sectors_per_unit = 0;
2511 1245 : uint32_t bytes_per_sector = 0;
2512 1245 : uint64_t total_size = 0;
2513 1245 : uint64_t size_free = 0;
2514 1245 : TALLOC_CTX *frame = talloc_stackframe();
2515 :
2516 1245 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2517 : /*
2518 : * Can't use sync call while an async call is in flight
2519 : */
2520 0 : status = NT_STATUS_INVALID_PARAMETER;
2521 0 : goto fail;
2522 : }
2523 :
2524 : /* First open the top level directory. */
2525 1245 : status = cli_smb2_create_fnum(cli,
2526 : path,
2527 1245 : (struct cli_smb2_create_flags){0},
2528 : SMB2_IMPERSONATION_IMPERSONATION,
2529 : FILE_READ_ATTRIBUTES, /* desired_access */
2530 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2531 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2532 : FILE_OPEN, /* create_disposition */
2533 : FILE_DIRECTORY_FILE, /* create_options */
2534 : NULL,
2535 : &fnum,
2536 : NULL,
2537 : NULL,
2538 : NULL);
2539 :
2540 1245 : if (!NT_STATUS_IS_OK(status)) {
2541 0 : goto fail;
2542 : }
2543 :
2544 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2545 : level 3 (SMB_FS_SIZE_INFORMATION). */
2546 :
2547 1245 : status = cli_smb2_query_info_fnum(
2548 : cli,
2549 : fnum,
2550 : 2, /* in_info_type */
2551 : 3, /* in_file_info_class */
2552 : 0xFFFF, /* in_max_output_length */
2553 : NULL, /* in_input_buffer */
2554 : 0, /* in_additional_info */
2555 : 0, /* in_flags */
2556 : frame,
2557 : &outbuf);
2558 1245 : if (!NT_STATUS_IS_OK(status)) {
2559 0 : goto fail;
2560 : }
2561 :
2562 : /* Parse the reply. */
2563 1245 : if (outbuf.length != 24) {
2564 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2565 0 : goto fail;
2566 : }
2567 :
2568 1245 : total_size = BVAL(outbuf.data, 0);
2569 1245 : size_free = BVAL(outbuf.data, 8);
2570 1245 : sectors_per_unit = IVAL(outbuf.data, 16);
2571 1245 : bytes_per_sector = IVAL(outbuf.data, 20);
2572 :
2573 1245 : if (bsize) {
2574 1245 : *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2575 : }
2576 1245 : if (total) {
2577 1245 : *total = total_size;
2578 : }
2579 1245 : if (avail) {
2580 1245 : *avail = size_free;
2581 : }
2582 :
2583 1245 : status = NT_STATUS_OK;
2584 :
2585 1245 : fail:
2586 :
2587 1245 : if (fnum != 0xffff) {
2588 1245 : cli_smb2_close_fnum(cli, fnum);
2589 : }
2590 :
2591 1245 : cli->raw_status = status;
2592 :
2593 1245 : TALLOC_FREE(frame);
2594 1245 : return status;
2595 : }
2596 :
2597 : /***************************************************************
2598 : Wrapper that allows SMB2 to query file system sizes.
2599 : Synchronous only.
2600 : ***************************************************************/
2601 :
2602 0 : NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2603 : uint64_t *total_allocation_units,
2604 : uint64_t *caller_allocation_units,
2605 : uint64_t *actual_allocation_units,
2606 : uint64_t *sectors_per_allocation_unit,
2607 : uint64_t *bytes_per_sector)
2608 : {
2609 0 : NTSTATUS status;
2610 0 : uint16_t fnum = 0xffff;
2611 0 : DATA_BLOB outbuf = data_blob_null;
2612 0 : TALLOC_CTX *frame = talloc_stackframe();
2613 :
2614 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2615 : /*
2616 : * Can't use sync call while an async call is in flight
2617 : */
2618 0 : status = NT_STATUS_INVALID_PARAMETER;
2619 0 : goto fail;
2620 : }
2621 :
2622 : /* First open the top level directory. */
2623 0 : status =
2624 0 : cli_smb2_create_fnum(cli, "",
2625 0 : (struct cli_smb2_create_flags){0},
2626 : SMB2_IMPERSONATION_IMPERSONATION,
2627 : FILE_READ_ATTRIBUTES, /* desired_access */
2628 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2629 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2630 : FILE_SHARE_DELETE, /* share_access */
2631 : FILE_OPEN, /* create_disposition */
2632 : FILE_DIRECTORY_FILE, /* create_options */
2633 : NULL,
2634 : &fnum,
2635 : NULL,
2636 : NULL,
2637 : NULL);
2638 :
2639 0 : if (!NT_STATUS_IS_OK(status)) {
2640 0 : goto fail;
2641 : }
2642 :
2643 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2644 : level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2645 :
2646 0 : status = cli_smb2_query_info_fnum(
2647 : cli,
2648 : fnum,
2649 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2650 : SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2651 : 0xFFFF, /* in_max_output_length */
2652 : NULL, /* in_input_buffer */
2653 : 0, /* in_additional_info */
2654 : 0, /* in_flags */
2655 : frame,
2656 : &outbuf);
2657 0 : if (!NT_STATUS_IS_OK(status)) {
2658 0 : goto fail;
2659 : }
2660 :
2661 0 : if (outbuf.length < 32) {
2662 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2663 0 : goto fail;
2664 : }
2665 :
2666 0 : *total_allocation_units = BIG_UINT(outbuf.data, 0);
2667 0 : *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2668 0 : *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2669 0 : *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2670 0 : *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2671 :
2672 0 : fail:
2673 :
2674 0 : if (fnum != 0xffff) {
2675 0 : cli_smb2_close_fnum(cli, fnum);
2676 : }
2677 :
2678 0 : cli->raw_status = status;
2679 :
2680 0 : TALLOC_FREE(frame);
2681 0 : return status;
2682 : }
2683 :
2684 : /***************************************************************
2685 : Wrapper that allows SMB2 to query file system attributes.
2686 : Synchronous only.
2687 : ***************************************************************/
2688 :
2689 86 : NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2690 : {
2691 0 : NTSTATUS status;
2692 86 : uint16_t fnum = 0xffff;
2693 86 : DATA_BLOB outbuf = data_blob_null;
2694 86 : TALLOC_CTX *frame = talloc_stackframe();
2695 :
2696 86 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2697 : /*
2698 : * Can't use sync call while an async call is in flight
2699 : */
2700 0 : status = NT_STATUS_INVALID_PARAMETER;
2701 0 : goto fail;
2702 : }
2703 :
2704 : /* First open the top level directory. */
2705 0 : status =
2706 86 : cli_smb2_create_fnum(cli, "",
2707 86 : (struct cli_smb2_create_flags){0},
2708 : SMB2_IMPERSONATION_IMPERSONATION,
2709 : FILE_READ_ATTRIBUTES, /* desired_access */
2710 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2711 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2712 : FILE_SHARE_DELETE, /* share_access */
2713 : FILE_OPEN, /* create_disposition */
2714 : FILE_DIRECTORY_FILE, /* create_options */
2715 : NULL,
2716 : &fnum,
2717 : NULL,
2718 : NULL,
2719 : NULL);
2720 :
2721 86 : if (!NT_STATUS_IS_OK(status)) {
2722 0 : goto fail;
2723 : }
2724 :
2725 86 : status = cli_smb2_query_info_fnum(
2726 : cli,
2727 : fnum,
2728 : 2, /* in_info_type */
2729 : 5, /* in_file_info_class */
2730 : 0xFFFF, /* in_max_output_length */
2731 : NULL, /* in_input_buffer */
2732 : 0, /* in_additional_info */
2733 : 0, /* in_flags */
2734 : frame,
2735 : &outbuf);
2736 86 : if (!NT_STATUS_IS_OK(status)) {
2737 0 : goto fail;
2738 : }
2739 :
2740 86 : if (outbuf.length < 12) {
2741 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2742 0 : goto fail;
2743 : }
2744 :
2745 86 : *fs_attr = IVAL(outbuf.data, 0);
2746 :
2747 86 : fail:
2748 :
2749 86 : if (fnum != 0xffff) {
2750 86 : cli_smb2_close_fnum(cli, fnum);
2751 : }
2752 :
2753 86 : cli->raw_status = status;
2754 :
2755 86 : TALLOC_FREE(frame);
2756 86 : return status;
2757 : }
2758 :
2759 : /***************************************************************
2760 : Wrapper that allows SMB2 to query file system volume info.
2761 : Synchronous only.
2762 : ***************************************************************/
2763 :
2764 17 : NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2765 : TALLOC_CTX *mem_ctx,
2766 : char **_volume_name,
2767 : uint32_t *pserial_number,
2768 : time_t *pdate)
2769 : {
2770 0 : NTSTATUS status;
2771 17 : uint16_t fnum = 0xffff;
2772 17 : DATA_BLOB outbuf = data_blob_null;
2773 0 : uint32_t nlen;
2774 17 : char *volume_name = NULL;
2775 17 : TALLOC_CTX *frame = talloc_stackframe();
2776 :
2777 17 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2778 : /*
2779 : * Can't use sync call while an async call is in flight
2780 : */
2781 0 : status = NT_STATUS_INVALID_PARAMETER;
2782 0 : goto fail;
2783 : }
2784 :
2785 : /* First open the top level directory. */
2786 0 : status =
2787 17 : cli_smb2_create_fnum(cli, "",
2788 17 : (struct cli_smb2_create_flags){0},
2789 : SMB2_IMPERSONATION_IMPERSONATION,
2790 : FILE_READ_ATTRIBUTES, /* desired_access */
2791 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2792 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2793 : FILE_SHARE_DELETE, /* share_access */
2794 : FILE_OPEN, /* create_disposition */
2795 : FILE_DIRECTORY_FILE, /* create_options */
2796 : NULL,
2797 : &fnum,
2798 : NULL,
2799 : NULL,
2800 : NULL);
2801 :
2802 17 : if (!NT_STATUS_IS_OK(status)) {
2803 0 : goto fail;
2804 : }
2805 :
2806 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2807 : level 1 (SMB_FS_VOLUME_INFORMATION). */
2808 :
2809 17 : status = cli_smb2_query_info_fnum(
2810 : cli,
2811 : fnum,
2812 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2813 : /* in_file_info_class */
2814 : SMB_FS_VOLUME_INFORMATION - 1000,
2815 : 0xFFFF, /* in_max_output_length */
2816 : NULL, /* in_input_buffer */
2817 : 0, /* in_additional_info */
2818 : 0, /* in_flags */
2819 : frame,
2820 : &outbuf);
2821 17 : if (!NT_STATUS_IS_OK(status)) {
2822 0 : goto fail;
2823 : }
2824 :
2825 17 : if (outbuf.length < 24) {
2826 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2827 0 : goto fail;
2828 : }
2829 :
2830 17 : if (pdate) {
2831 0 : struct timespec ts;
2832 17 : ts = interpret_long_date(BVAL(outbuf.data, 0));
2833 17 : *pdate = ts.tv_sec;
2834 : }
2835 17 : if (pserial_number) {
2836 17 : *pserial_number = IVAL(outbuf.data,8);
2837 : }
2838 17 : nlen = IVAL(outbuf.data,12);
2839 17 : if (nlen + 18 < 18) {
2840 : /* Integer wrap. */
2841 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2842 0 : goto fail;
2843 : }
2844 : /*
2845 : * The next check is safe as we know outbuf.length >= 24
2846 : * from above.
2847 : */
2848 17 : if (nlen > (outbuf.length - 18)) {
2849 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2850 0 : goto fail;
2851 : }
2852 :
2853 17 : pull_string_talloc(mem_ctx,
2854 17 : (const char *)outbuf.data,
2855 : 0,
2856 : &volume_name,
2857 17 : outbuf.data + 18,
2858 : nlen,
2859 : STR_UNICODE);
2860 17 : if (volume_name == NULL) {
2861 0 : status = map_nt_error_from_unix(errno);
2862 0 : goto fail;
2863 : }
2864 :
2865 17 : *_volume_name = volume_name;
2866 :
2867 17 : fail:
2868 :
2869 17 : if (fnum != 0xffff) {
2870 17 : cli_smb2_close_fnum(cli, fnum);
2871 : }
2872 :
2873 17 : cli->raw_status = status;
2874 :
2875 17 : TALLOC_FREE(frame);
2876 17 : return status;
2877 : }
2878 :
2879 : struct cli_smb2_mxac_state {
2880 : struct tevent_context *ev;
2881 : struct cli_state *cli;
2882 : const char *fname;
2883 : struct smb2_create_blobs in_cblobs;
2884 : uint16_t fnum;
2885 : NTSTATUS status;
2886 : uint32_t mxac;
2887 : };
2888 :
2889 : static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2890 : static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2891 :
2892 6 : struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2893 : struct tevent_context *ev,
2894 : struct cli_state *cli,
2895 : const char *fname)
2896 : {
2897 6 : struct tevent_req *req = NULL, *subreq = NULL;
2898 6 : struct cli_smb2_mxac_state *state = NULL;
2899 0 : NTSTATUS status;
2900 :
2901 6 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2902 6 : if (req == NULL) {
2903 0 : return NULL;
2904 : }
2905 6 : *state = (struct cli_smb2_mxac_state) {
2906 : .ev = ev,
2907 : .cli = cli,
2908 : .fname = fname,
2909 : };
2910 :
2911 6 : status = smb2_create_blob_add(state,
2912 6 : &state->in_cblobs,
2913 : SMB2_CREATE_TAG_MXAC,
2914 : data_blob(NULL, 0));
2915 6 : if (tevent_req_nterror(req, status)) {
2916 0 : return tevent_req_post(req, ev);
2917 : }
2918 :
2919 6 : subreq = cli_smb2_create_fnum_send(
2920 : state,
2921 6 : state->ev,
2922 6 : state->cli,
2923 6 : state->fname,
2924 6 : (struct cli_smb2_create_flags){0},
2925 : SMB2_IMPERSONATION_IMPERSONATION,
2926 : FILE_READ_ATTRIBUTES,
2927 : 0, /* file attributes */
2928 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2929 : FILE_OPEN,
2930 : 0, /* create_options */
2931 6 : &state->in_cblobs);
2932 6 : if (tevent_req_nomem(subreq, req)) {
2933 0 : return tevent_req_post(req, ev);
2934 : }
2935 6 : tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2936 6 : return req;
2937 : }
2938 :
2939 6 : static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2940 : {
2941 6 : struct tevent_req *req = tevent_req_callback_data(
2942 : subreq, struct tevent_req);
2943 6 : struct cli_smb2_mxac_state *state = tevent_req_data(
2944 : req, struct cli_smb2_mxac_state);
2945 6 : struct smb2_create_blobs out_cblobs = {0};
2946 6 : struct smb2_create_blob *mxac_blob = NULL;
2947 0 : NTSTATUS status;
2948 :
2949 6 : status = cli_smb2_create_fnum_recv(
2950 : subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
2951 6 : TALLOC_FREE(subreq);
2952 :
2953 6 : if (tevent_req_nterror(req, status)) {
2954 0 : return;
2955 : }
2956 :
2957 6 : mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
2958 6 : if (mxac_blob == NULL) {
2959 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2960 0 : goto close;
2961 : }
2962 6 : if (mxac_blob->data.length != 8) {
2963 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2964 0 : goto close;
2965 : }
2966 :
2967 6 : state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
2968 6 : state->mxac = IVAL(mxac_blob->data.data, 4);
2969 :
2970 6 : close:
2971 6 : subreq = cli_smb2_close_fnum_send(state,
2972 : state->ev,
2973 : state->cli,
2974 6 : state->fnum,
2975 : 0);
2976 6 : if (tevent_req_nomem(subreq, req)) {
2977 0 : return;
2978 : }
2979 6 : tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
2980 :
2981 6 : return;
2982 : }
2983 :
2984 6 : static void cli_smb2_mxac_closed(struct tevent_req *subreq)
2985 : {
2986 6 : struct tevent_req *req = tevent_req_callback_data(
2987 : subreq, struct tevent_req);
2988 0 : NTSTATUS status;
2989 :
2990 6 : status = cli_smb2_close_fnum_recv(subreq);
2991 6 : if (tevent_req_nterror(req, status)) {
2992 0 : return;
2993 : }
2994 :
2995 6 : tevent_req_done(req);
2996 : }
2997 :
2998 6 : NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
2999 : {
3000 6 : struct cli_smb2_mxac_state *state = tevent_req_data(
3001 : req, struct cli_smb2_mxac_state);
3002 0 : NTSTATUS status;
3003 :
3004 6 : if (tevent_req_is_nterror(req, &status)) {
3005 0 : return status;
3006 : }
3007 :
3008 6 : if (!NT_STATUS_IS_OK(state->status)) {
3009 0 : return state->status;
3010 : }
3011 :
3012 6 : *mxac = state->mxac;
3013 6 : return NT_STATUS_OK;
3014 : }
3015 :
3016 6 : NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3017 : const char *fname,
3018 : uint32_t *_mxac)
3019 : {
3020 6 : TALLOC_CTX *frame = talloc_stackframe();
3021 6 : struct tevent_context *ev = NULL;
3022 6 : struct tevent_req *req = NULL;
3023 6 : NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3024 0 : bool ok;
3025 :
3026 6 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3027 : /*
3028 : * Can't use sync call while an async call is in flight
3029 : */
3030 0 : status = NT_STATUS_INVALID_PARAMETER;
3031 0 : goto fail;
3032 : }
3033 :
3034 6 : ev = samba_tevent_context_init(frame);
3035 6 : if (ev == NULL) {
3036 0 : goto fail;
3037 : }
3038 6 : req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3039 6 : if (req == NULL) {
3040 0 : goto fail;
3041 : }
3042 6 : ok = tevent_req_poll_ntstatus(req, ev, &status);
3043 6 : if (!ok) {
3044 0 : goto fail;
3045 : }
3046 6 : status = cli_smb2_query_mxac_recv(req, _mxac);
3047 :
3048 6 : fail:
3049 6 : cli->raw_status = status;
3050 6 : TALLOC_FREE(frame);
3051 6 : return status;
3052 : }
3053 :
3054 : struct cli_smb2_rename_fnum_state {
3055 : DATA_BLOB inbuf;
3056 : };
3057 :
3058 : static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3059 :
3060 291 : static struct tevent_req *cli_smb2_rename_fnum_send(
3061 : TALLOC_CTX *mem_ctx,
3062 : struct tevent_context *ev,
3063 : struct cli_state *cli,
3064 : uint16_t fnum,
3065 : const char *fname_dst,
3066 : bool replace)
3067 : {
3068 291 : struct tevent_req *req = NULL, *subreq = NULL;
3069 291 : struct cli_smb2_rename_fnum_state *state = NULL;
3070 291 : size_t namelen = strlen(fname_dst);
3071 291 : smb_ucs2_t *converted_str = NULL;
3072 291 : size_t converted_size_bytes = 0;
3073 0 : size_t inbuf_size;
3074 0 : bool ok;
3075 :
3076 291 : req = tevent_req_create(
3077 : mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3078 291 : if (req == NULL) {
3079 0 : return NULL;
3080 : }
3081 :
3082 : /*
3083 : * SMB2 is pickier about pathnames. Ensure it doesn't start in
3084 : * a '\'
3085 : */
3086 291 : if (*fname_dst == '\\') {
3087 247 : fname_dst++;
3088 : }
3089 :
3090 : /*
3091 : * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3092 : * '\'
3093 : */
3094 291 : if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3095 0 : fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3096 0 : if (tevent_req_nomem(fname_dst, req)) {
3097 0 : return tevent_req_post(req, ev);
3098 : }
3099 : }
3100 :
3101 291 : ok = push_ucs2_talloc(
3102 : state, &converted_str, fname_dst, &converted_size_bytes);
3103 291 : if (!ok) {
3104 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3105 0 : return tevent_req_post(req, ev);
3106 : }
3107 :
3108 : /*
3109 : * W2K8 insists the dest name is not null terminated. Remove
3110 : * the last 2 zero bytes and reduce the name length.
3111 : */
3112 291 : if (converted_size_bytes < 2) {
3113 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3114 0 : return tevent_req_post(req, ev);
3115 : }
3116 291 : converted_size_bytes -= 2;
3117 :
3118 291 : inbuf_size = 20 + converted_size_bytes;
3119 291 : if (inbuf_size < 20) {
3120 : /* Integer wrap check. */
3121 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3122 0 : return tevent_req_post(req, ev);
3123 : }
3124 :
3125 : /*
3126 : * The Windows 10 SMB2 server has a minimum length
3127 : * for a SMB2_FILE_RENAME_INFORMATION buffer of
3128 : * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3129 : * if the length is less. This isn't an alignment
3130 : * issue as Windows client accepts happily 2-byte align
3131 : * for larger target name sizes. Also the Windows 10
3132 : * SMB1 server doesn't have this restriction.
3133 : *
3134 : * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3135 : */
3136 291 : inbuf_size = MAX(inbuf_size, 24);
3137 :
3138 291 : state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3139 291 : if (tevent_req_nomem(state->inbuf.data, req)) {
3140 0 : return tevent_req_post(req, ev);
3141 : }
3142 :
3143 291 : if (replace) {
3144 14 : SCVAL(state->inbuf.data, 0, 1);
3145 : }
3146 :
3147 291 : SIVAL(state->inbuf.data, 16, converted_size_bytes);
3148 291 : memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3149 :
3150 291 : TALLOC_FREE(converted_str);
3151 :
3152 : /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3153 : level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3154 :
3155 291 : subreq = cli_smb2_set_info_fnum_send(
3156 : state, /* mem_ctx */
3157 : ev, /* ev */
3158 : cli, /* cli */
3159 : fnum, /* fnum */
3160 : 1, /* in_info_type */
3161 : SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3162 291 : &state->inbuf, /* in_input_buffer */
3163 : 0); /* in_additional_info */
3164 291 : if (tevent_req_nomem(subreq, req)) {
3165 0 : return tevent_req_post(req, ev);
3166 : }
3167 291 : tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3168 291 : return req;
3169 : }
3170 :
3171 291 : static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3172 : {
3173 291 : NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3174 291 : tevent_req_simple_finish_ntstatus(subreq, status);
3175 291 : }
3176 :
3177 291 : static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3178 : {
3179 291 : return tevent_req_simple_recv_ntstatus(req);
3180 : }
3181 :
3182 : /***************************************************************
3183 : Wrapper that allows SMB2 to rename a file.
3184 : ***************************************************************/
3185 :
3186 : struct cli_smb2_rename_state {
3187 : struct tevent_context *ev;
3188 : struct cli_state *cli;
3189 : const char *fname_dst;
3190 : bool replace;
3191 : uint16_t fnum;
3192 :
3193 : NTSTATUS rename_status;
3194 : };
3195 :
3196 : static void cli_smb2_rename_opened(struct tevent_req *subreq);
3197 : static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3198 : static void cli_smb2_rename_closed(struct tevent_req *subreq);
3199 :
3200 291 : struct tevent_req *cli_smb2_rename_send(
3201 : TALLOC_CTX *mem_ctx,
3202 : struct tevent_context *ev,
3203 : struct cli_state *cli,
3204 : const char *fname_src,
3205 : const char *fname_dst,
3206 : bool replace)
3207 : {
3208 291 : struct tevent_req *req = NULL, *subreq = NULL;
3209 291 : struct cli_smb2_rename_state *state = NULL;
3210 0 : NTSTATUS status;
3211 :
3212 291 : req = tevent_req_create(
3213 : mem_ctx, &state, struct cli_smb2_rename_state);
3214 291 : if (req == NULL) {
3215 0 : return NULL;
3216 : }
3217 :
3218 : /*
3219 : * Strip a MSDFS path from fname_dst if we were given one.
3220 : */
3221 291 : status = cli_dfs_target_check(state,
3222 : cli,
3223 : fname_dst,
3224 : &fname_dst);
3225 291 : if (tevent_req_nterror(req, status)) {
3226 0 : return tevent_req_post(req, ev);
3227 : }
3228 :
3229 291 : state->ev = ev;
3230 291 : state->cli = cli;
3231 291 : state->fname_dst = fname_dst;
3232 291 : state->replace = replace;
3233 :
3234 291 : subreq = get_fnum_from_path_send(
3235 : state, ev, cli, fname_src, DELETE_ACCESS);
3236 291 : if (tevent_req_nomem(subreq, req)) {
3237 0 : return tevent_req_post(req, ev);
3238 : }
3239 291 : tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3240 291 : return req;
3241 : }
3242 :
3243 291 : static void cli_smb2_rename_opened(struct tevent_req *subreq)
3244 : {
3245 291 : struct tevent_req *req = tevent_req_callback_data(
3246 : subreq, struct tevent_req);
3247 291 : struct cli_smb2_rename_state *state = tevent_req_data(
3248 : req, struct cli_smb2_rename_state);
3249 0 : NTSTATUS status;
3250 :
3251 291 : status = get_fnum_from_path_recv(subreq, &state->fnum);
3252 291 : TALLOC_FREE(subreq);
3253 291 : if (tevent_req_nterror(req, status)) {
3254 0 : return;
3255 : }
3256 :
3257 291 : subreq = cli_smb2_rename_fnum_send(
3258 : state,
3259 : state->ev,
3260 : state->cli,
3261 291 : state->fnum,
3262 : state->fname_dst,
3263 291 : state->replace);
3264 291 : if (tevent_req_nomem(subreq, req)) {
3265 0 : return;
3266 : }
3267 291 : tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3268 : }
3269 :
3270 291 : static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3271 : {
3272 291 : struct tevent_req *req = tevent_req_callback_data(
3273 : subreq, struct tevent_req);
3274 291 : struct cli_smb2_rename_state *state = tevent_req_data(
3275 : req, struct cli_smb2_rename_state);
3276 :
3277 291 : state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3278 291 : TALLOC_FREE(subreq);
3279 :
3280 291 : subreq = cli_smb2_close_fnum_send(state,
3281 : state->ev,
3282 : state->cli,
3283 291 : state->fnum,
3284 : 0);
3285 291 : if (tevent_req_nomem(subreq, req)) {
3286 0 : return;
3287 : }
3288 291 : tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3289 : }
3290 :
3291 291 : static void cli_smb2_rename_closed(struct tevent_req *subreq)
3292 : {
3293 291 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3294 291 : tevent_req_simple_finish_ntstatus(subreq, status);
3295 291 : }
3296 :
3297 291 : NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3298 : {
3299 291 : struct cli_smb2_rename_state *state = tevent_req_data(
3300 : req, struct cli_smb2_rename_state);
3301 291 : NTSTATUS status = NT_STATUS_OK;
3302 :
3303 291 : if (!tevent_req_is_nterror(req, &status)) {
3304 291 : status = state->rename_status;
3305 : }
3306 291 : tevent_req_received(req);
3307 291 : return status;
3308 : }
3309 :
3310 : /***************************************************************
3311 : Wrapper that allows SMB2 to set an EA on a fnum.
3312 : Synchronous only.
3313 : ***************************************************************/
3314 :
3315 0 : NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3316 : uint16_t fnum,
3317 : const char *ea_name,
3318 : const char *ea_val,
3319 : size_t ea_len)
3320 : {
3321 0 : NTSTATUS status;
3322 0 : DATA_BLOB inbuf = data_blob_null;
3323 0 : size_t bloblen = 0;
3324 0 : char *ea_name_ascii = NULL;
3325 0 : size_t namelen = 0;
3326 0 : TALLOC_CTX *frame = talloc_stackframe();
3327 :
3328 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3329 : /*
3330 : * Can't use sync call while an async call is in flight
3331 : */
3332 0 : status = NT_STATUS_INVALID_PARAMETER;
3333 0 : goto fail;
3334 : }
3335 :
3336 : /* Marshall the SMB2 EA data. */
3337 0 : if (ea_len > 0xFFFF) {
3338 0 : status = NT_STATUS_INVALID_PARAMETER;
3339 0 : goto fail;
3340 : }
3341 :
3342 0 : if (!push_ascii_talloc(frame,
3343 : &ea_name_ascii,
3344 : ea_name,
3345 : &namelen)) {
3346 0 : status = NT_STATUS_INVALID_PARAMETER;
3347 0 : goto fail;
3348 : }
3349 :
3350 0 : if (namelen < 2 || namelen > 0xFF) {
3351 0 : status = NT_STATUS_INVALID_PARAMETER;
3352 0 : goto fail;
3353 : }
3354 :
3355 0 : bloblen = 8 + ea_len + namelen;
3356 : /* Round up to a 4 byte boundary. */
3357 0 : bloblen = ((bloblen + 3)&~3);
3358 :
3359 0 : inbuf = data_blob_talloc_zero(frame, bloblen);
3360 0 : if (inbuf.data == NULL) {
3361 0 : status = NT_STATUS_NO_MEMORY;
3362 0 : goto fail;
3363 : }
3364 : /* namelen doesn't include the NULL byte. */
3365 0 : SCVAL(inbuf.data, 5, namelen - 1);
3366 0 : SSVAL(inbuf.data, 6, ea_len);
3367 0 : memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3368 0 : memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3369 :
3370 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3371 : level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3372 :
3373 0 : status = cli_smb2_set_info_fnum(
3374 : cli,
3375 : fnum,
3376 : 1, /* in_info_type */
3377 : SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3378 : &inbuf, /* in_input_buffer */
3379 : 0); /* in_additional_info */
3380 :
3381 0 : fail:
3382 :
3383 0 : cli->raw_status = status;
3384 :
3385 0 : TALLOC_FREE(frame);
3386 0 : return status;
3387 : }
3388 :
3389 : /***************************************************************
3390 : Wrapper that allows SMB2 to set an EA on a pathname.
3391 : Synchronous only.
3392 : ***************************************************************/
3393 :
3394 0 : NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3395 : const char *name,
3396 : const char *ea_name,
3397 : const char *ea_val,
3398 : size_t ea_len)
3399 : {
3400 0 : NTSTATUS status;
3401 0 : uint16_t fnum = 0xffff;
3402 :
3403 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3404 : /*
3405 : * Can't use sync call while an async call is in flight
3406 : */
3407 0 : status = NT_STATUS_INVALID_PARAMETER;
3408 0 : goto fail;
3409 : }
3410 :
3411 0 : status = get_fnum_from_path(cli,
3412 : name,
3413 : FILE_WRITE_EA,
3414 : &fnum);
3415 :
3416 0 : if (!NT_STATUS_IS_OK(status)) {
3417 0 : goto fail;
3418 : }
3419 :
3420 0 : status = cli_set_ea_fnum(cli,
3421 : fnum,
3422 : ea_name,
3423 : ea_val,
3424 : ea_len);
3425 0 : if (!NT_STATUS_IS_OK(status)) {
3426 0 : goto fail;
3427 : }
3428 :
3429 0 : fail:
3430 :
3431 0 : if (fnum != 0xffff) {
3432 0 : cli_smb2_close_fnum(cli, fnum);
3433 : }
3434 :
3435 0 : cli->raw_status = status;
3436 :
3437 0 : return status;
3438 : }
3439 :
3440 : /***************************************************************
3441 : Wrapper that allows SMB2 to get an EA list on a pathname.
3442 : Synchronous only.
3443 : ***************************************************************/
3444 :
3445 0 : NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3446 : const char *name,
3447 : TALLOC_CTX *ctx,
3448 : size_t *pnum_eas,
3449 : struct ea_struct **pea_array)
3450 : {
3451 0 : NTSTATUS status;
3452 0 : uint16_t fnum = 0xffff;
3453 0 : DATA_BLOB outbuf = data_blob_null;
3454 0 : struct ea_list *ea_list = NULL;
3455 0 : struct ea_list *eal = NULL;
3456 0 : size_t ea_count = 0;
3457 0 : TALLOC_CTX *frame = talloc_stackframe();
3458 :
3459 0 : *pnum_eas = 0;
3460 0 : *pea_array = NULL;
3461 :
3462 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3463 : /*
3464 : * Can't use sync call while an async call is in flight
3465 : */
3466 0 : status = NT_STATUS_INVALID_PARAMETER;
3467 0 : goto fail;
3468 : }
3469 :
3470 0 : status = get_fnum_from_path(cli,
3471 : name,
3472 : FILE_READ_EA,
3473 : &fnum);
3474 :
3475 0 : if (!NT_STATUS_IS_OK(status)) {
3476 0 : goto fail;
3477 : }
3478 :
3479 : /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3480 : level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3481 :
3482 0 : status = cli_smb2_query_info_fnum(
3483 : cli,
3484 : fnum,
3485 : 1, /* in_info_type */
3486 : SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3487 : 0xFFFF, /* in_max_output_length */
3488 : NULL, /* in_input_buffer */
3489 : 0, /* in_additional_info */
3490 : 0, /* in_flags */
3491 : frame,
3492 : &outbuf);
3493 :
3494 0 : if (!NT_STATUS_IS_OK(status)) {
3495 0 : goto fail;
3496 : }
3497 :
3498 : /* Parse the reply. */
3499 0 : ea_list = read_nttrans_ea_list(ctx,
3500 0 : (const char *)outbuf.data,
3501 : outbuf.length);
3502 0 : if (ea_list == NULL) {
3503 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3504 0 : goto fail;
3505 : }
3506 :
3507 : /* Convert to an array. */
3508 0 : for (eal = ea_list; eal; eal = eal->next) {
3509 0 : ea_count++;
3510 : }
3511 :
3512 0 : if (ea_count) {
3513 0 : *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3514 0 : if (*pea_array == NULL) {
3515 0 : status = NT_STATUS_NO_MEMORY;
3516 0 : goto fail;
3517 : }
3518 0 : ea_count = 0;
3519 0 : for (eal = ea_list; eal; eal = eal->next) {
3520 0 : (*pea_array)[ea_count++] = eal->ea;
3521 : }
3522 0 : *pnum_eas = ea_count;
3523 : }
3524 :
3525 0 : fail:
3526 :
3527 0 : if (fnum != 0xffff) {
3528 0 : cli_smb2_close_fnum(cli, fnum);
3529 : }
3530 :
3531 0 : cli->raw_status = status;
3532 :
3533 0 : TALLOC_FREE(frame);
3534 0 : return status;
3535 : }
3536 :
3537 : /***************************************************************
3538 : Wrapper that allows SMB2 to get user quota.
3539 : Synchronous only.
3540 : ***************************************************************/
3541 :
3542 21 : NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3543 : int quota_fnum,
3544 : SMB_NTQUOTA_STRUCT *pqt)
3545 : {
3546 0 : NTSTATUS status;
3547 21 : DATA_BLOB inbuf = data_blob_null;
3548 21 : DATA_BLOB info_blob = data_blob_null;
3549 21 : DATA_BLOB outbuf = data_blob_null;
3550 21 : TALLOC_CTX *frame = talloc_stackframe();
3551 0 : unsigned sid_len;
3552 0 : unsigned int offset;
3553 21 : struct smb2_query_quota_info query = {0};
3554 21 : struct file_get_quota_info info = {0};
3555 0 : enum ndr_err_code err;
3556 21 : struct ndr_push *ndr_push = NULL;
3557 :
3558 21 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3559 : /*
3560 : * Can't use sync call while an async call is in flight
3561 : */
3562 0 : status = NT_STATUS_INVALID_PARAMETER;
3563 0 : goto fail;
3564 : }
3565 :
3566 21 : sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3567 :
3568 21 : query.return_single = 1;
3569 :
3570 21 : info.next_entry_offset = 0;
3571 21 : info.sid_length = sid_len;
3572 21 : info.sid = pqt->sid;
3573 :
3574 21 : err = ndr_push_struct_blob(
3575 : &info_blob,
3576 : frame,
3577 : &info,
3578 : (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3579 :
3580 21 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3581 0 : status = NT_STATUS_INTERNAL_ERROR;
3582 0 : goto fail;
3583 : }
3584 :
3585 21 : query.sid_list_length = info_blob.length;
3586 21 : ndr_push = ndr_push_init_ctx(frame);
3587 21 : if (!ndr_push) {
3588 0 : status = NT_STATUS_NO_MEMORY;
3589 0 : goto fail;
3590 : }
3591 :
3592 21 : err = ndr_push_smb2_query_quota_info(ndr_push,
3593 : NDR_SCALARS | NDR_BUFFERS,
3594 : &query);
3595 :
3596 21 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3597 0 : status = NT_STATUS_INTERNAL_ERROR;
3598 0 : goto fail;
3599 : }
3600 :
3601 21 : err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3602 21 : info_blob.length);
3603 :
3604 21 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3605 0 : status = NT_STATUS_INTERNAL_ERROR;
3606 0 : goto fail;
3607 : }
3608 21 : inbuf.data = ndr_push->data;
3609 21 : inbuf.length = ndr_push->offset;
3610 :
3611 21 : status = cli_smb2_query_info_fnum(
3612 : cli,
3613 : quota_fnum,
3614 : 4, /* in_info_type */
3615 : 0, /* in_file_info_class */
3616 : 0xFFFF, /* in_max_output_length */
3617 : &inbuf, /* in_input_buffer */
3618 : 0, /* in_additional_info */
3619 : 0, /* in_flags */
3620 : frame,
3621 : &outbuf);
3622 :
3623 21 : if (!NT_STATUS_IS_OK(status)) {
3624 6 : goto fail;
3625 : }
3626 :
3627 15 : if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3628 : pqt)) {
3629 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3630 0 : DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3631 : }
3632 :
3633 15 : fail:
3634 21 : cli->raw_status = status;
3635 :
3636 21 : TALLOC_FREE(frame);
3637 21 : return status;
3638 : }
3639 :
3640 : /***************************************************************
3641 : Wrapper that allows SMB2 to list user quota.
3642 : Synchronous only.
3643 : ***************************************************************/
3644 :
3645 12 : NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3646 : TALLOC_CTX *mem_ctx,
3647 : int quota_fnum,
3648 : SMB_NTQUOTA_LIST **pqt_list,
3649 : bool first)
3650 : {
3651 0 : NTSTATUS status;
3652 12 : DATA_BLOB inbuf = data_blob_null;
3653 12 : DATA_BLOB outbuf = data_blob_null;
3654 12 : TALLOC_CTX *frame = talloc_stackframe();
3655 12 : struct smb2_query_quota_info info = {0};
3656 0 : enum ndr_err_code err;
3657 :
3658 12 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3659 : /*
3660 : * Can't use sync call while an async call is in flight
3661 : */
3662 0 : status = NT_STATUS_INVALID_PARAMETER;
3663 0 : goto cleanup;
3664 : }
3665 :
3666 12 : info.restart_scan = first ? 1 : 0;
3667 :
3668 12 : err = ndr_push_struct_blob(
3669 : &inbuf,
3670 : frame,
3671 : &info,
3672 : (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3673 :
3674 12 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3675 0 : status = NT_STATUS_INTERNAL_ERROR;
3676 0 : goto cleanup;
3677 : }
3678 :
3679 12 : status = cli_smb2_query_info_fnum(
3680 : cli,
3681 : quota_fnum,
3682 : 4, /* in_info_type */
3683 : 0, /* in_file_info_class */
3684 : 0xFFFF, /* in_max_output_length */
3685 : &inbuf, /* in_input_buffer */
3686 : 0, /* in_additional_info */
3687 : 0, /* in_flags */
3688 : frame,
3689 : &outbuf);
3690 :
3691 : /*
3692 : * safeguard against panic from calling parse_user_quota_list with
3693 : * NULL buffer
3694 : */
3695 12 : if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3696 0 : status = NT_STATUS_NO_MORE_ENTRIES;
3697 : }
3698 :
3699 12 : if (!NT_STATUS_IS_OK(status)) {
3700 6 : goto cleanup;
3701 : }
3702 :
3703 6 : status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3704 : pqt_list);
3705 :
3706 12 : cleanup:
3707 12 : cli->raw_status = status;
3708 :
3709 12 : TALLOC_FREE(frame);
3710 12 : return status;
3711 : }
3712 :
3713 : /***************************************************************
3714 : Wrapper that allows SMB2 to get file system quota.
3715 : Synchronous only.
3716 : ***************************************************************/
3717 :
3718 0 : NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3719 : int quota_fnum,
3720 : SMB_NTQUOTA_STRUCT *pqt)
3721 : {
3722 0 : NTSTATUS status;
3723 0 : DATA_BLOB outbuf = data_blob_null;
3724 0 : TALLOC_CTX *frame = talloc_stackframe();
3725 :
3726 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3727 : /*
3728 : * Can't use sync call while an async call is in flight
3729 : */
3730 0 : status = NT_STATUS_INVALID_PARAMETER;
3731 0 : goto cleanup;
3732 : }
3733 :
3734 0 : status = cli_smb2_query_info_fnum(
3735 : cli,
3736 : quota_fnum,
3737 : 2, /* in_info_type */
3738 : SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3739 : 0xFFFF, /* in_max_output_length */
3740 : NULL, /* in_input_buffer */
3741 : 0, /* in_additional_info */
3742 : 0, /* in_flags */
3743 : frame,
3744 : &outbuf);
3745 :
3746 0 : if (!NT_STATUS_IS_OK(status)) {
3747 0 : goto cleanup;
3748 : }
3749 :
3750 0 : status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3751 :
3752 0 : cleanup:
3753 0 : cli->raw_status = status;
3754 :
3755 0 : TALLOC_FREE(frame);
3756 0 : return status;
3757 : }
3758 :
3759 : /***************************************************************
3760 : Wrapper that allows SMB2 to set user quota.
3761 : Synchronous only.
3762 : ***************************************************************/
3763 :
3764 6 : NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3765 : int quota_fnum,
3766 : SMB_NTQUOTA_LIST *qtl)
3767 : {
3768 0 : NTSTATUS status;
3769 6 : DATA_BLOB inbuf = data_blob_null;
3770 6 : TALLOC_CTX *frame = talloc_stackframe();
3771 :
3772 6 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3773 : /*
3774 : * Can't use sync call while an async call is in flight
3775 : */
3776 0 : status = NT_STATUS_INVALID_PARAMETER;
3777 0 : goto cleanup;
3778 : }
3779 :
3780 6 : status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3781 6 : if (!NT_STATUS_IS_OK(status)) {
3782 0 : goto cleanup;
3783 : }
3784 :
3785 6 : status = cli_smb2_set_info_fnum(
3786 : cli,
3787 : quota_fnum,
3788 : 4, /* in_info_type */
3789 : 0, /* in_file_info_class */
3790 : &inbuf, /* in_input_buffer */
3791 : 0); /* in_additional_info */
3792 6 : cleanup:
3793 :
3794 6 : cli->raw_status = status;
3795 :
3796 6 : TALLOC_FREE(frame);
3797 :
3798 6 : return status;
3799 : }
3800 :
3801 0 : NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3802 : int quota_fnum,
3803 : SMB_NTQUOTA_STRUCT *pqt)
3804 : {
3805 0 : NTSTATUS status;
3806 0 : DATA_BLOB inbuf = data_blob_null;
3807 0 : TALLOC_CTX *frame = talloc_stackframe();
3808 :
3809 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3810 : /*
3811 : * Can't use sync call while an async call is in flight
3812 : */
3813 0 : status = NT_STATUS_INVALID_PARAMETER;
3814 0 : goto cleanup;
3815 : }
3816 :
3817 0 : status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3818 0 : if (!NT_STATUS_IS_OK(status)) {
3819 0 : goto cleanup;
3820 : }
3821 :
3822 0 : status = cli_smb2_set_info_fnum(
3823 : cli,
3824 : quota_fnum,
3825 : 2, /* in_info_type */
3826 : SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3827 : &inbuf, /* in_input_buffer */
3828 : 0); /* in_additional_info */
3829 0 : cleanup:
3830 0 : cli->raw_status = status;
3831 :
3832 0 : TALLOC_FREE(frame);
3833 0 : return status;
3834 : }
3835 :
3836 : struct cli_smb2_read_state {
3837 : struct tevent_context *ev;
3838 : struct cli_state *cli;
3839 : struct smb2_hnd *ph;
3840 : uint64_t start_offset;
3841 : uint32_t size;
3842 : uint32_t received;
3843 : uint8_t *buf;
3844 : };
3845 :
3846 : static void cli_smb2_read_done(struct tevent_req *subreq);
3847 :
3848 4340 : struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3849 : struct tevent_context *ev,
3850 : struct cli_state *cli,
3851 : uint16_t fnum,
3852 : off_t offset,
3853 : size_t size)
3854 : {
3855 0 : NTSTATUS status;
3856 0 : struct tevent_req *req, *subreq;
3857 0 : struct cli_smb2_read_state *state;
3858 :
3859 4340 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3860 4340 : if (req == NULL) {
3861 0 : return NULL;
3862 : }
3863 4340 : state->ev = ev;
3864 4340 : state->cli = cli;
3865 4340 : state->start_offset = (uint64_t)offset;
3866 4340 : state->size = (uint32_t)size;
3867 4340 : state->received = 0;
3868 4340 : state->buf = NULL;
3869 :
3870 4340 : status = map_fnum_to_smb2_handle(cli,
3871 : fnum,
3872 4340 : &state->ph);
3873 4340 : if (tevent_req_nterror(req, status)) {
3874 0 : return tevent_req_post(req, ev);
3875 : }
3876 :
3877 4340 : subreq = smb2cli_read_send(state,
3878 4340 : state->ev,
3879 4340 : state->cli->conn,
3880 4340 : state->cli->timeout,
3881 4340 : state->cli->smb2.session,
3882 4340 : state->cli->smb2.tcon,
3883 4340 : state->size,
3884 4340 : state->start_offset,
3885 4340 : state->ph->fid_persistent,
3886 4340 : state->ph->fid_volatile,
3887 : 0, /* minimum_count */
3888 : 0); /* remaining_bytes */
3889 :
3890 4340 : if (tevent_req_nomem(subreq, req)) {
3891 0 : return tevent_req_post(req, ev);
3892 : }
3893 4340 : tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3894 4340 : return req;
3895 : }
3896 :
3897 4340 : static void cli_smb2_read_done(struct tevent_req *subreq)
3898 : {
3899 4340 : struct tevent_req *req = tevent_req_callback_data(
3900 : subreq, struct tevent_req);
3901 4340 : struct cli_smb2_read_state *state = tevent_req_data(
3902 : req, struct cli_smb2_read_state);
3903 0 : NTSTATUS status;
3904 :
3905 4340 : status = smb2cli_read_recv(subreq, state,
3906 : &state->buf, &state->received);
3907 4340 : if (tevent_req_nterror(req, status)) {
3908 468 : return;
3909 : }
3910 :
3911 3872 : if (state->received > state->size) {
3912 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3913 0 : return;
3914 : }
3915 :
3916 3872 : tevent_req_done(req);
3917 : }
3918 :
3919 4340 : NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3920 : ssize_t *received,
3921 : uint8_t **rcvbuf)
3922 : {
3923 0 : NTSTATUS status;
3924 4340 : struct cli_smb2_read_state *state = tevent_req_data(
3925 : req, struct cli_smb2_read_state);
3926 :
3927 4340 : if (tevent_req_is_nterror(req, &status)) {
3928 468 : state->cli->raw_status = status;
3929 468 : return status;
3930 : }
3931 : /*
3932 : * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3933 : * better make sure that you copy it away before you talloc_free(req).
3934 : * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3935 : */
3936 3872 : *received = (ssize_t)state->received;
3937 3872 : *rcvbuf = state->buf;
3938 3872 : state->cli->raw_status = NT_STATUS_OK;
3939 3872 : return NT_STATUS_OK;
3940 : }
3941 :
3942 : struct cli_smb2_write_state {
3943 : struct tevent_context *ev;
3944 : struct cli_state *cli;
3945 : struct smb2_hnd *ph;
3946 : uint32_t flags;
3947 : const uint8_t *buf;
3948 : uint64_t offset;
3949 : uint32_t size;
3950 : uint32_t written;
3951 : };
3952 :
3953 : static void cli_smb2_write_written(struct tevent_req *req);
3954 :
3955 2304 : struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3956 : struct tevent_context *ev,
3957 : struct cli_state *cli,
3958 : uint16_t fnum,
3959 : uint16_t mode,
3960 : const uint8_t *buf,
3961 : off_t offset,
3962 : size_t size)
3963 : {
3964 0 : NTSTATUS status;
3965 2304 : struct tevent_req *req, *subreq = NULL;
3966 2304 : struct cli_smb2_write_state *state = NULL;
3967 :
3968 2304 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3969 2304 : if (req == NULL) {
3970 0 : return NULL;
3971 : }
3972 2304 : state->ev = ev;
3973 2304 : state->cli = cli;
3974 : /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
3975 2304 : state->flags = (uint32_t)mode;
3976 2304 : state->buf = buf;
3977 2304 : state->offset = (uint64_t)offset;
3978 2304 : state->size = (uint32_t)size;
3979 2304 : state->written = 0;
3980 :
3981 2304 : status = map_fnum_to_smb2_handle(cli,
3982 : fnum,
3983 2304 : &state->ph);
3984 2304 : if (tevent_req_nterror(req, status)) {
3985 0 : return tevent_req_post(req, ev);
3986 : }
3987 :
3988 2304 : subreq = smb2cli_write_send(state,
3989 2304 : state->ev,
3990 2304 : state->cli->conn,
3991 2304 : state->cli->timeout,
3992 2304 : state->cli->smb2.session,
3993 2304 : state->cli->smb2.tcon,
3994 2304 : state->size,
3995 2304 : state->offset,
3996 2304 : state->ph->fid_persistent,
3997 2304 : state->ph->fid_volatile,
3998 : 0, /* remaining_bytes */
3999 2304 : state->flags, /* flags */
4000 2304 : state->buf);
4001 :
4002 2304 : if (tevent_req_nomem(subreq, req)) {
4003 0 : return tevent_req_post(req, ev);
4004 : }
4005 2304 : tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4006 2304 : return req;
4007 : }
4008 :
4009 2304 : static void cli_smb2_write_written(struct tevent_req *subreq)
4010 : {
4011 2304 : struct tevent_req *req = tevent_req_callback_data(
4012 : subreq, struct tevent_req);
4013 2304 : struct cli_smb2_write_state *state = tevent_req_data(
4014 : req, struct cli_smb2_write_state);
4015 0 : NTSTATUS status;
4016 0 : uint32_t written;
4017 :
4018 2304 : status = smb2cli_write_recv(subreq, &written);
4019 2304 : TALLOC_FREE(subreq);
4020 2304 : if (tevent_req_nterror(req, status)) {
4021 0 : return;
4022 : }
4023 :
4024 2304 : state->written = written;
4025 :
4026 2304 : tevent_req_done(req);
4027 : }
4028 :
4029 2304 : NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4030 : size_t *pwritten)
4031 : {
4032 2304 : struct cli_smb2_write_state *state = tevent_req_data(
4033 : req, struct cli_smb2_write_state);
4034 0 : NTSTATUS status;
4035 :
4036 2304 : if (tevent_req_is_nterror(req, &status)) {
4037 0 : state->cli->raw_status = status;
4038 0 : tevent_req_received(req);
4039 0 : return status;
4040 : }
4041 :
4042 2304 : if (pwritten != NULL) {
4043 2304 : *pwritten = (size_t)state->written;
4044 : }
4045 2304 : state->cli->raw_status = NT_STATUS_OK;
4046 2304 : tevent_req_received(req);
4047 2304 : return NT_STATUS_OK;
4048 : }
4049 :
4050 : /***************************************************************
4051 : Wrapper that allows SMB2 async write using an fnum.
4052 : This is mostly cut-and-paste from Volker's code inside
4053 : source3/libsmb/clireadwrite.c, adapted for SMB2.
4054 :
4055 : Done this way so I can reuse all the logic inside cli_push()
4056 : for free :-).
4057 : ***************************************************************/
4058 :
4059 : struct cli_smb2_writeall_state {
4060 : struct tevent_context *ev;
4061 : struct cli_state *cli;
4062 : struct smb2_hnd *ph;
4063 : uint32_t flags;
4064 : const uint8_t *buf;
4065 : uint64_t offset;
4066 : uint32_t size;
4067 : uint32_t written;
4068 : };
4069 :
4070 : static void cli_smb2_writeall_written(struct tevent_req *req);
4071 :
4072 565 : struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4073 : struct tevent_context *ev,
4074 : struct cli_state *cli,
4075 : uint16_t fnum,
4076 : uint16_t mode,
4077 : const uint8_t *buf,
4078 : off_t offset,
4079 : size_t size)
4080 : {
4081 0 : NTSTATUS status;
4082 565 : struct tevent_req *req, *subreq = NULL;
4083 565 : struct cli_smb2_writeall_state *state = NULL;
4084 0 : uint32_t to_write;
4085 0 : uint32_t max_size;
4086 0 : bool ok;
4087 :
4088 565 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4089 565 : if (req == NULL) {
4090 0 : return NULL;
4091 : }
4092 565 : state->ev = ev;
4093 565 : state->cli = cli;
4094 : /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4095 565 : state->flags = (uint32_t)mode;
4096 565 : state->buf = buf;
4097 565 : state->offset = (uint64_t)offset;
4098 565 : state->size = (uint32_t)size;
4099 565 : state->written = 0;
4100 :
4101 565 : status = map_fnum_to_smb2_handle(cli,
4102 : fnum,
4103 565 : &state->ph);
4104 565 : if (tevent_req_nterror(req, status)) {
4105 0 : return tevent_req_post(req, ev);
4106 : }
4107 :
4108 565 : to_write = state->size;
4109 565 : max_size = smb2cli_conn_max_write_size(state->cli->conn);
4110 565 : to_write = MIN(max_size, to_write);
4111 565 : ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4112 565 : if (ok) {
4113 565 : to_write = MIN(max_size, to_write);
4114 : }
4115 :
4116 565 : subreq = smb2cli_write_send(state,
4117 565 : state->ev,
4118 565 : state->cli->conn,
4119 565 : state->cli->timeout,
4120 565 : state->cli->smb2.session,
4121 565 : state->cli->smb2.tcon,
4122 : to_write,
4123 565 : state->offset,
4124 565 : state->ph->fid_persistent,
4125 565 : state->ph->fid_volatile,
4126 : 0, /* remaining_bytes */
4127 565 : state->flags, /* flags */
4128 565 : state->buf + state->written);
4129 :
4130 565 : if (tevent_req_nomem(subreq, req)) {
4131 0 : return tevent_req_post(req, ev);
4132 : }
4133 565 : tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4134 565 : return req;
4135 : }
4136 :
4137 655 : static void cli_smb2_writeall_written(struct tevent_req *subreq)
4138 : {
4139 655 : struct tevent_req *req = tevent_req_callback_data(
4140 : subreq, struct tevent_req);
4141 655 : struct cli_smb2_writeall_state *state = tevent_req_data(
4142 : req, struct cli_smb2_writeall_state);
4143 0 : NTSTATUS status;
4144 0 : uint32_t written, to_write;
4145 0 : uint32_t max_size;
4146 0 : bool ok;
4147 :
4148 655 : status = smb2cli_write_recv(subreq, &written);
4149 655 : TALLOC_FREE(subreq);
4150 655 : if (tevent_req_nterror(req, status)) {
4151 565 : return;
4152 : }
4153 :
4154 655 : state->written += written;
4155 :
4156 655 : if (state->written > state->size) {
4157 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4158 0 : return;
4159 : }
4160 :
4161 655 : to_write = state->size - state->written;
4162 :
4163 655 : if (to_write == 0) {
4164 565 : tevent_req_done(req);
4165 565 : return;
4166 : }
4167 :
4168 90 : max_size = smb2cli_conn_max_write_size(state->cli->conn);
4169 90 : to_write = MIN(max_size, to_write);
4170 90 : ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4171 90 : if (ok) {
4172 90 : to_write = MIN(max_size, to_write);
4173 : }
4174 :
4175 90 : subreq = smb2cli_write_send(state,
4176 : state->ev,
4177 90 : state->cli->conn,
4178 90 : state->cli->timeout,
4179 90 : state->cli->smb2.session,
4180 90 : state->cli->smb2.tcon,
4181 : to_write,
4182 90 : state->offset + state->written,
4183 90 : state->ph->fid_persistent,
4184 90 : state->ph->fid_volatile,
4185 : 0, /* remaining_bytes */
4186 : state->flags, /* flags */
4187 90 : state->buf + state->written);
4188 :
4189 90 : if (tevent_req_nomem(subreq, req)) {
4190 0 : return;
4191 : }
4192 90 : tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4193 : }
4194 :
4195 565 : NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4196 : size_t *pwritten)
4197 : {
4198 565 : struct cli_smb2_writeall_state *state = tevent_req_data(
4199 : req, struct cli_smb2_writeall_state);
4200 0 : NTSTATUS status;
4201 :
4202 565 : if (tevent_req_is_nterror(req, &status)) {
4203 0 : state->cli->raw_status = status;
4204 0 : return status;
4205 : }
4206 565 : if (pwritten != NULL) {
4207 565 : *pwritten = (size_t)state->written;
4208 : }
4209 565 : state->cli->raw_status = NT_STATUS_OK;
4210 565 : return NT_STATUS_OK;
4211 : }
4212 :
4213 : struct cli_smb2_splice_state {
4214 : struct tevent_context *ev;
4215 : struct cli_state *cli;
4216 : struct smb2_hnd *src_ph;
4217 : struct smb2_hnd *dst_ph;
4218 : int (*splice_cb)(off_t n, void *priv);
4219 : void *priv;
4220 : off_t written;
4221 : off_t size;
4222 : off_t src_offset;
4223 : off_t dst_offset;
4224 : bool resized;
4225 : struct req_resume_key_rsp resume_rsp;
4226 : struct srv_copychunk_copy cc_copy;
4227 : };
4228 :
4229 : static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4230 : struct tevent_req *req);
4231 :
4232 0 : static void cli_splice_copychunk_done(struct tevent_req *subreq)
4233 : {
4234 0 : struct tevent_req *req = tevent_req_callback_data(
4235 : subreq, struct tevent_req);
4236 0 : struct cli_smb2_splice_state *state =
4237 0 : tevent_req_data(req,
4238 : struct cli_smb2_splice_state);
4239 0 : struct smbXcli_conn *conn = state->cli->conn;
4240 0 : DATA_BLOB out_input_buffer = data_blob_null;
4241 0 : DATA_BLOB out_output_buffer = data_blob_null;
4242 0 : struct srv_copychunk_rsp cc_copy_rsp;
4243 0 : enum ndr_err_code ndr_ret;
4244 0 : NTSTATUS status;
4245 :
4246 0 : status = smb2cli_ioctl_recv(subreq, state,
4247 : &out_input_buffer,
4248 : &out_output_buffer);
4249 0 : TALLOC_FREE(subreq);
4250 0 : if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4251 0 : state->resized) && tevent_req_nterror(req, status)) {
4252 0 : return;
4253 : }
4254 :
4255 0 : ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4256 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4257 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4258 0 : DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4259 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4260 0 : return;
4261 : }
4262 :
4263 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4264 0 : uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4265 : cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4266 0 : if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4267 0 : max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4268 0 : tevent_req_nterror(req, status)) {
4269 0 : return;
4270 : }
4271 :
4272 0 : state->resized = true;
4273 0 : smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4274 0 : smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4275 : } else {
4276 0 : if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4277 0 : (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4278 0 : (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4279 0 : tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4280 0 : return;
4281 : }
4282 0 : state->src_offset += cc_copy_rsp.total_bytes_written;
4283 0 : state->dst_offset += cc_copy_rsp.total_bytes_written;
4284 0 : state->written += cc_copy_rsp.total_bytes_written;
4285 0 : if (!state->splice_cb(state->written, state->priv)) {
4286 0 : tevent_req_nterror(req, NT_STATUS_CANCELLED);
4287 0 : return;
4288 : }
4289 : }
4290 :
4291 0 : cli_splice_copychunk_send(state, req);
4292 : }
4293 :
4294 0 : static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4295 : struct tevent_req *req)
4296 : {
4297 0 : struct tevent_req *subreq;
4298 0 : enum ndr_err_code ndr_ret;
4299 0 : struct smbXcli_conn *conn = state->cli->conn;
4300 0 : struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4301 0 : off_t src_offset = state->src_offset;
4302 0 : off_t dst_offset = state->dst_offset;
4303 0 : uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4304 : state->size - state->written);
4305 0 : DATA_BLOB in_input_buffer = data_blob_null;
4306 0 : DATA_BLOB in_output_buffer = data_blob_null;
4307 :
4308 0 : if (state->size - state->written == 0) {
4309 0 : tevent_req_done(req);
4310 0 : return;
4311 : }
4312 :
4313 0 : cc_copy->chunk_count = 0;
4314 0 : while (req_len) {
4315 0 : cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4316 0 : cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4317 0 : cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4318 : smb2cli_conn_cc_chunk_len(conn));
4319 0 : if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4320 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4321 0 : return;
4322 : }
4323 0 : req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4324 0 : if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4325 0 : (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4326 0 : tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4327 0 : return;
4328 : }
4329 0 : src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4330 0 : dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4331 0 : cc_copy->chunk_count++;
4332 : }
4333 :
4334 0 : ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4335 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4336 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4337 0 : DEBUG(0, ("failed to marshall copy chunk req\n"));
4338 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4339 0 : return;
4340 : }
4341 :
4342 0 : subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4343 0 : state->cli->timeout,
4344 0 : state->cli->smb2.session,
4345 0 : state->cli->smb2.tcon,
4346 0 : state->dst_ph->fid_persistent, /* in_fid_persistent */
4347 0 : state->dst_ph->fid_volatile, /* in_fid_volatile */
4348 : FSCTL_SRV_COPYCHUNK_WRITE,
4349 : 0, /* in_max_input_length */
4350 : &in_input_buffer,
4351 : 12, /* in_max_output_length */
4352 : &in_output_buffer,
4353 : SMB2_IOCTL_FLAG_IS_FSCTL);
4354 0 : if (tevent_req_nomem(subreq, req)) {
4355 0 : return;
4356 : }
4357 0 : tevent_req_set_callback(subreq,
4358 : cli_splice_copychunk_done,
4359 : req);
4360 : }
4361 :
4362 0 : static void cli_splice_key_done(struct tevent_req *subreq)
4363 : {
4364 0 : struct tevent_req *req = tevent_req_callback_data(
4365 : subreq, struct tevent_req);
4366 0 : struct cli_smb2_splice_state *state =
4367 0 : tevent_req_data(req,
4368 : struct cli_smb2_splice_state);
4369 0 : enum ndr_err_code ndr_ret;
4370 0 : NTSTATUS status;
4371 :
4372 0 : DATA_BLOB out_input_buffer = data_blob_null;
4373 0 : DATA_BLOB out_output_buffer = data_blob_null;
4374 :
4375 0 : status = smb2cli_ioctl_recv(subreq, state,
4376 : &out_input_buffer,
4377 : &out_output_buffer);
4378 0 : TALLOC_FREE(subreq);
4379 0 : if (tevent_req_nterror(req, status)) {
4380 0 : return;
4381 : }
4382 :
4383 0 : ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4384 0 : state, &state->resume_rsp,
4385 : (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4386 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4387 0 : DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4388 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4389 0 : return;
4390 : }
4391 :
4392 0 : memcpy(&state->cc_copy.source_key,
4393 0 : &state->resume_rsp.resume_key,
4394 : sizeof state->resume_rsp.resume_key);
4395 :
4396 0 : cli_splice_copychunk_send(state, req);
4397 : }
4398 :
4399 0 : struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4400 : struct tevent_context *ev,
4401 : struct cli_state *cli,
4402 : uint16_t src_fnum, uint16_t dst_fnum,
4403 : off_t size, off_t src_offset, off_t dst_offset,
4404 : int (*splice_cb)(off_t n, void *priv),
4405 : void *priv)
4406 : {
4407 0 : struct tevent_req *req;
4408 0 : struct tevent_req *subreq;
4409 0 : struct cli_smb2_splice_state *state;
4410 0 : NTSTATUS status;
4411 0 : DATA_BLOB in_input_buffer = data_blob_null;
4412 0 : DATA_BLOB in_output_buffer = data_blob_null;
4413 :
4414 0 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4415 0 : if (req == NULL) {
4416 0 : return NULL;
4417 : }
4418 0 : state->cli = cli;
4419 0 : state->ev = ev;
4420 0 : state->splice_cb = splice_cb;
4421 0 : state->priv = priv;
4422 0 : state->size = size;
4423 0 : state->written = 0;
4424 0 : state->src_offset = src_offset;
4425 0 : state->dst_offset = dst_offset;
4426 0 : state->cc_copy.chunks = talloc_array(state,
4427 : struct srv_copychunk,
4428 : smb2cli_conn_cc_max_chunks(cli->conn));
4429 0 : if (state->cc_copy.chunks == NULL) {
4430 0 : return NULL;
4431 : }
4432 :
4433 0 : status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4434 0 : if (tevent_req_nterror(req, status))
4435 0 : return tevent_req_post(req, ev);
4436 :
4437 0 : status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4438 0 : if (tevent_req_nterror(req, status))
4439 0 : return tevent_req_post(req, ev);
4440 :
4441 0 : subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4442 0 : cli->timeout,
4443 : cli->smb2.session,
4444 : cli->smb2.tcon,
4445 0 : state->src_ph->fid_persistent, /* in_fid_persistent */
4446 0 : state->src_ph->fid_volatile, /* in_fid_volatile */
4447 : FSCTL_SRV_REQUEST_RESUME_KEY,
4448 : 0, /* in_max_input_length */
4449 : &in_input_buffer,
4450 : 32, /* in_max_output_length */
4451 : &in_output_buffer,
4452 : SMB2_IOCTL_FLAG_IS_FSCTL);
4453 0 : if (tevent_req_nomem(subreq, req)) {
4454 0 : return NULL;
4455 : }
4456 0 : tevent_req_set_callback(subreq,
4457 : cli_splice_key_done,
4458 : req);
4459 :
4460 0 : return req;
4461 : }
4462 :
4463 0 : NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4464 : {
4465 0 : struct cli_smb2_splice_state *state = tevent_req_data(
4466 : req, struct cli_smb2_splice_state);
4467 0 : NTSTATUS status;
4468 :
4469 0 : if (tevent_req_is_nterror(req, &status)) {
4470 0 : state->cli->raw_status = status;
4471 0 : tevent_req_received(req);
4472 0 : return status;
4473 : }
4474 0 : if (written != NULL) {
4475 0 : *written = state->written;
4476 : }
4477 0 : state->cli->raw_status = NT_STATUS_OK;
4478 0 : tevent_req_received(req);
4479 0 : return NT_STATUS_OK;
4480 : }
4481 :
4482 : /***************************************************************
4483 : SMB2 enum shadow copy data.
4484 : ***************************************************************/
4485 :
4486 : struct cli_smb2_shadow_copy_data_fnum_state {
4487 : struct cli_state *cli;
4488 : uint16_t fnum;
4489 : struct smb2_hnd *ph;
4490 : DATA_BLOB out_input_buffer;
4491 : DATA_BLOB out_output_buffer;
4492 : };
4493 :
4494 : static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4495 :
4496 2334 : static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4497 : TALLOC_CTX *mem_ctx,
4498 : struct tevent_context *ev,
4499 : struct cli_state *cli,
4500 : uint16_t fnum,
4501 : bool get_names)
4502 : {
4503 0 : struct tevent_req *req, *subreq;
4504 0 : struct cli_smb2_shadow_copy_data_fnum_state *state;
4505 0 : NTSTATUS status;
4506 :
4507 2334 : req = tevent_req_create(mem_ctx, &state,
4508 : struct cli_smb2_shadow_copy_data_fnum_state);
4509 2334 : if (req == NULL) {
4510 0 : return NULL;
4511 : }
4512 :
4513 2334 : state->cli = cli;
4514 2334 : state->fnum = fnum;
4515 :
4516 2334 : status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4517 2334 : if (tevent_req_nterror(req, status)) {
4518 0 : return tevent_req_post(req, ev);
4519 : }
4520 :
4521 : /*
4522 : * TODO. Under SMB2 we should send a zero max_output_length
4523 : * ioctl to get the required size, then send another ioctl
4524 : * to get the data, but the current SMB1 implementation just
4525 : * does one roundtrip with a 64K buffer size. Do the same
4526 : * for now. JRA.
4527 : */
4528 :
4529 2334 : subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4530 2334 : state->cli->timeout,
4531 2334 : state->cli->smb2.session,
4532 2334 : state->cli->smb2.tcon,
4533 2334 : state->ph->fid_persistent, /* in_fid_persistent */
4534 2334 : state->ph->fid_volatile, /* in_fid_volatile */
4535 : FSCTL_GET_SHADOW_COPY_DATA,
4536 : 0, /* in_max_input_length */
4537 : NULL, /* in_input_buffer */
4538 : get_names ?
4539 : CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4540 : NULL, /* in_output_buffer */
4541 : SMB2_IOCTL_FLAG_IS_FSCTL);
4542 :
4543 2334 : if (tevent_req_nomem(subreq, req)) {
4544 0 : return tevent_req_post(req, ev);
4545 : }
4546 2334 : tevent_req_set_callback(subreq,
4547 : cli_smb2_shadow_copy_data_fnum_done,
4548 : req);
4549 :
4550 2334 : return req;
4551 : }
4552 :
4553 2334 : static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4554 : {
4555 2334 : struct tevent_req *req = tevent_req_callback_data(
4556 : subreq, struct tevent_req);
4557 2334 : struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4558 : req, struct cli_smb2_shadow_copy_data_fnum_state);
4559 0 : NTSTATUS status;
4560 :
4561 2334 : status = smb2cli_ioctl_recv(subreq, state,
4562 : &state->out_input_buffer,
4563 : &state->out_output_buffer);
4564 2334 : tevent_req_simple_finish_ntstatus(subreq, status);
4565 2334 : }
4566 :
4567 2334 : static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4568 : TALLOC_CTX *mem_ctx,
4569 : bool get_names,
4570 : char ***pnames,
4571 : int *pnum_names)
4572 : {
4573 2334 : struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4574 : req, struct cli_smb2_shadow_copy_data_fnum_state);
4575 2334 : char **names = NULL;
4576 2334 : uint32_t num_names = 0;
4577 2334 : uint32_t num_names_returned = 0;
4578 2334 : uint32_t dlength = 0;
4579 0 : uint32_t i;
4580 2334 : uint8_t *endp = NULL;
4581 0 : NTSTATUS status;
4582 :
4583 2334 : if (tevent_req_is_nterror(req, &status)) {
4584 212 : return status;
4585 : }
4586 :
4587 2122 : if (state->out_output_buffer.length < 16) {
4588 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4589 : }
4590 :
4591 2122 : num_names = IVAL(state->out_output_buffer.data, 0);
4592 2122 : num_names_returned = IVAL(state->out_output_buffer.data, 4);
4593 2122 : dlength = IVAL(state->out_output_buffer.data, 8);
4594 :
4595 2122 : if (num_names > 0x7FFFFFFF) {
4596 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4597 : }
4598 :
4599 2122 : if (get_names == false) {
4600 1061 : *pnum_names = (int)num_names;
4601 1061 : return NT_STATUS_OK;
4602 : }
4603 1061 : if (num_names != num_names_returned) {
4604 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4605 : }
4606 1061 : if (dlength + 12 < 12) {
4607 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4608 : }
4609 : /*
4610 : * NB. The below is an allowable return if there are
4611 : * more snapshots than the buffer size we told the
4612 : * server we can receive. We currently don't support
4613 : * this.
4614 : */
4615 1061 : if (dlength + 12 > state->out_output_buffer.length) {
4616 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4617 : }
4618 1061 : if (state->out_output_buffer.length +
4619 : (2 * sizeof(SHADOW_COPY_LABEL)) <
4620 : state->out_output_buffer.length) {
4621 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4622 : }
4623 :
4624 1061 : names = talloc_array(mem_ctx, char *, num_names_returned);
4625 1061 : if (names == NULL) {
4626 0 : return NT_STATUS_NO_MEMORY;
4627 : }
4628 :
4629 1061 : endp = state->out_output_buffer.data +
4630 1061 : state->out_output_buffer.length;
4631 :
4632 4219 : for (i=0; i<num_names_returned; i++) {
4633 0 : bool ret;
4634 0 : uint8_t *src;
4635 0 : size_t converted_size;
4636 :
4637 3158 : src = state->out_output_buffer.data + 12 +
4638 3158 : (i * 2 * sizeof(SHADOW_COPY_LABEL));
4639 :
4640 3158 : if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4641 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4642 : }
4643 3158 : ret = convert_string_talloc(
4644 : names, CH_UTF16LE, CH_UNIX,
4645 : src, 2 * sizeof(SHADOW_COPY_LABEL),
4646 3158 : &names[i], &converted_size);
4647 3158 : if (!ret) {
4648 0 : TALLOC_FREE(names);
4649 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4650 : }
4651 : }
4652 1061 : *pnum_names = num_names;
4653 1061 : *pnames = names;
4654 1061 : return NT_STATUS_OK;
4655 : }
4656 :
4657 2334 : NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4658 : struct cli_state *cli,
4659 : uint16_t fnum,
4660 : bool get_names,
4661 : char ***pnames,
4662 : int *pnum_names)
4663 : {
4664 2334 : TALLOC_CTX *frame = talloc_stackframe();
4665 0 : struct tevent_context *ev;
4666 0 : struct tevent_req *req;
4667 2334 : NTSTATUS status = NT_STATUS_NO_MEMORY;
4668 :
4669 2334 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4670 : /*
4671 : * Can't use sync call while an async call is in flight
4672 : */
4673 0 : status = NT_STATUS_INVALID_PARAMETER;
4674 0 : goto fail;
4675 : }
4676 2334 : ev = samba_tevent_context_init(frame);
4677 2334 : if (ev == NULL) {
4678 0 : goto fail;
4679 : }
4680 2334 : req = cli_smb2_shadow_copy_data_fnum_send(frame,
4681 : ev,
4682 : cli,
4683 : fnum,
4684 : get_names);
4685 2334 : if (req == NULL) {
4686 0 : goto fail;
4687 : }
4688 2334 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4689 0 : goto fail;
4690 : }
4691 2334 : status = cli_smb2_shadow_copy_data_fnum_recv(req,
4692 : mem_ctx,
4693 : get_names,
4694 : pnames,
4695 : pnum_names);
4696 2334 : fail:
4697 2334 : cli->raw_status = status;
4698 :
4699 2334 : TALLOC_FREE(frame);
4700 2334 : return status;
4701 : }
4702 :
4703 : /***************************************************************
4704 : Wrapper that allows SMB2 to truncate a file.
4705 : Synchronous only.
4706 : ***************************************************************/
4707 :
4708 60 : NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4709 : uint16_t fnum,
4710 : uint64_t newsize)
4711 : {
4712 0 : NTSTATUS status;
4713 60 : uint8_t buf[8] = {0};
4714 60 : DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4715 60 : TALLOC_CTX *frame = talloc_stackframe();
4716 :
4717 60 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4718 : /*
4719 : * Can't use sync call while an async call is in flight
4720 : */
4721 0 : status = NT_STATUS_INVALID_PARAMETER;
4722 0 : goto fail;
4723 : }
4724 :
4725 60 : SBVAL(buf, 0, newsize);
4726 :
4727 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4728 : level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4729 :
4730 60 : status = cli_smb2_set_info_fnum(
4731 : cli,
4732 : fnum,
4733 : 1, /* in_info_type */
4734 : SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4735 : &inbuf, /* in_input_buffer */
4736 : 0);
4737 :
4738 60 : fail:
4739 :
4740 60 : cli->raw_status = status;
4741 :
4742 60 : TALLOC_FREE(frame);
4743 60 : return status;
4744 : }
4745 :
4746 : struct cli_smb2_notify_state {
4747 : struct tevent_req *subreq;
4748 : struct notify_change *changes;
4749 : size_t num_changes;
4750 : };
4751 :
4752 : static void cli_smb2_notify_done(struct tevent_req *subreq);
4753 : static bool cli_smb2_notify_cancel(struct tevent_req *req);
4754 :
4755 42 : struct tevent_req *cli_smb2_notify_send(
4756 : TALLOC_CTX *mem_ctx,
4757 : struct tevent_context *ev,
4758 : struct cli_state *cli,
4759 : uint16_t fnum,
4760 : uint32_t buffer_size,
4761 : uint32_t completion_filter,
4762 : bool recursive)
4763 : {
4764 42 : struct tevent_req *req = NULL;
4765 42 : struct cli_smb2_notify_state *state = NULL;
4766 42 : struct smb2_hnd *ph = NULL;
4767 0 : NTSTATUS status;
4768 :
4769 42 : req = tevent_req_create(mem_ctx, &state,
4770 : struct cli_smb2_notify_state);
4771 42 : if (req == NULL) {
4772 0 : return NULL;
4773 : }
4774 :
4775 42 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4776 42 : if (tevent_req_nterror(req, status)) {
4777 0 : return tevent_req_post(req, ev);
4778 : }
4779 :
4780 84 : state->subreq = smb2cli_notify_send(
4781 : state,
4782 : ev,
4783 : cli->conn,
4784 42 : cli->timeout,
4785 : cli->smb2.session,
4786 : cli->smb2.tcon,
4787 : buffer_size,
4788 42 : ph->fid_persistent,
4789 42 : ph->fid_volatile,
4790 : completion_filter,
4791 : recursive);
4792 42 : if (tevent_req_nomem(state->subreq, req)) {
4793 0 : return tevent_req_post(req, ev);
4794 : }
4795 42 : tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4796 42 : tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4797 42 : return req;
4798 : }
4799 :
4800 0 : static bool cli_smb2_notify_cancel(struct tevent_req *req)
4801 : {
4802 0 : struct cli_smb2_notify_state *state = tevent_req_data(
4803 : req, struct cli_smb2_notify_state);
4804 0 : bool ok;
4805 :
4806 0 : ok = tevent_req_cancel(state->subreq);
4807 0 : return ok;
4808 : }
4809 :
4810 42 : static void cli_smb2_notify_done(struct tevent_req *subreq)
4811 : {
4812 42 : struct tevent_req *req = tevent_req_callback_data(
4813 : subreq, struct tevent_req);
4814 42 : struct cli_smb2_notify_state *state = tevent_req_data(
4815 : req, struct cli_smb2_notify_state);
4816 0 : uint8_t *base;
4817 0 : uint32_t len;
4818 0 : uint32_t ofs;
4819 0 : NTSTATUS status;
4820 :
4821 42 : status = smb2cli_notify_recv(subreq, state, &base, &len);
4822 42 : TALLOC_FREE(subreq);
4823 :
4824 42 : if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4825 0 : tevent_req_done(req);
4826 0 : return;
4827 : }
4828 42 : if (tevent_req_nterror(req, status)) {
4829 12 : return;
4830 : }
4831 :
4832 30 : ofs = 0;
4833 :
4834 30 : while (len - ofs >= 12) {
4835 0 : struct notify_change *tmp;
4836 0 : struct notify_change *c;
4837 30 : uint32_t next_ofs = IVAL(base, ofs);
4838 30 : uint32_t file_name_length = IVAL(base, ofs+8);
4839 0 : size_t namelen;
4840 0 : bool ok;
4841 :
4842 30 : tmp = talloc_realloc(
4843 : state,
4844 : state->changes,
4845 : struct notify_change,
4846 : state->num_changes + 1);
4847 30 : if (tevent_req_nomem(tmp, req)) {
4848 0 : return;
4849 : }
4850 30 : state->changes = tmp;
4851 30 : c = &state->changes[state->num_changes];
4852 30 : state->num_changes += 1;
4853 :
4854 60 : if (smb_buffer_oob(len, ofs, next_ofs) ||
4855 30 : smb_buffer_oob(len, ofs+12, file_name_length)) {
4856 0 : tevent_req_nterror(
4857 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4858 0 : return;
4859 : }
4860 :
4861 30 : c->action = IVAL(base, ofs+4);
4862 :
4863 30 : ok = convert_string_talloc(
4864 30 : state->changes,
4865 : CH_UTF16LE,
4866 : CH_UNIX,
4867 30 : base + ofs + 12,
4868 : file_name_length,
4869 30 : &c->name,
4870 : &namelen);
4871 30 : if (!ok) {
4872 0 : tevent_req_nterror(
4873 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4874 0 : return;
4875 : }
4876 :
4877 30 : if (next_ofs == 0) {
4878 30 : break;
4879 : }
4880 0 : ofs += next_ofs;
4881 : }
4882 :
4883 30 : tevent_req_done(req);
4884 : }
4885 :
4886 42 : NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4887 : TALLOC_CTX *mem_ctx,
4888 : struct notify_change **pchanges,
4889 : uint32_t *pnum_changes)
4890 : {
4891 42 : struct cli_smb2_notify_state *state = tevent_req_data(
4892 : req, struct cli_smb2_notify_state);
4893 0 : NTSTATUS status;
4894 :
4895 42 : if (tevent_req_is_nterror(req, &status)) {
4896 12 : return status;
4897 : }
4898 30 : *pchanges = talloc_move(mem_ctx, &state->changes);
4899 30 : *pnum_changes = state->num_changes;
4900 30 : return NT_STATUS_OK;
4901 : }
4902 :
4903 0 : NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4904 : uint32_t buffer_size, uint32_t completion_filter,
4905 : bool recursive, TALLOC_CTX *mem_ctx,
4906 : struct notify_change **pchanges,
4907 : uint32_t *pnum_changes)
4908 : {
4909 0 : TALLOC_CTX *frame = talloc_stackframe();
4910 0 : struct tevent_context *ev;
4911 0 : struct tevent_req *req;
4912 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
4913 :
4914 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4915 : /*
4916 : * Can't use sync call while an async call is in flight
4917 : */
4918 0 : status = NT_STATUS_INVALID_PARAMETER;
4919 0 : goto fail;
4920 : }
4921 0 : ev = samba_tevent_context_init(frame);
4922 0 : if (ev == NULL) {
4923 0 : goto fail;
4924 : }
4925 0 : req = cli_smb2_notify_send(
4926 : frame,
4927 : ev,
4928 : cli,
4929 : fnum,
4930 : buffer_size,
4931 : completion_filter,
4932 : recursive);
4933 0 : if (req == NULL) {
4934 0 : goto fail;
4935 : }
4936 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4937 0 : goto fail;
4938 : }
4939 0 : status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4940 0 : fail:
4941 0 : TALLOC_FREE(frame);
4942 0 : return status;
4943 : }
4944 :
4945 : struct cli_smb2_fsctl_state {
4946 : DATA_BLOB out;
4947 : };
4948 :
4949 : static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4950 :
4951 14 : struct tevent_req *cli_smb2_fsctl_send(
4952 : TALLOC_CTX *mem_ctx,
4953 : struct tevent_context *ev,
4954 : struct cli_state *cli,
4955 : uint16_t fnum,
4956 : uint32_t ctl_code,
4957 : const DATA_BLOB *in,
4958 : uint32_t max_out)
4959 : {
4960 14 : struct tevent_req *req = NULL, *subreq = NULL;
4961 14 : struct cli_smb2_fsctl_state *state = NULL;
4962 14 : struct smb2_hnd *ph = NULL;
4963 0 : NTSTATUS status;
4964 :
4965 14 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4966 14 : if (req == NULL) {
4967 0 : return NULL;
4968 : }
4969 :
4970 14 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4971 14 : if (tevent_req_nterror(req, status)) {
4972 0 : return tevent_req_post(req, ev);
4973 : }
4974 :
4975 14 : subreq = smb2cli_ioctl_send(
4976 : state,
4977 : ev,
4978 : cli->conn,
4979 14 : cli->timeout,
4980 : cli->smb2.session,
4981 : cli->smb2.tcon,
4982 14 : ph->fid_persistent,
4983 14 : ph->fid_volatile,
4984 : ctl_code,
4985 : 0, /* in_max_input_length */
4986 : in,
4987 : max_out,
4988 : NULL,
4989 : SMB2_IOCTL_FLAG_IS_FSCTL);
4990 :
4991 14 : if (tevent_req_nomem(subreq, req)) {
4992 0 : return tevent_req_post(req, ev);
4993 : }
4994 14 : tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
4995 14 : return req;
4996 : }
4997 :
4998 14 : static void cli_smb2_fsctl_done(struct tevent_req *subreq)
4999 : {
5000 14 : struct tevent_req *req = tevent_req_callback_data(
5001 : subreq, struct tevent_req);
5002 14 : struct cli_smb2_fsctl_state *state = tevent_req_data(
5003 : req, struct cli_smb2_fsctl_state);
5004 0 : NTSTATUS status;
5005 :
5006 14 : status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5007 14 : tevent_req_simple_finish_ntstatus(subreq, status);
5008 14 : }
5009 :
5010 14 : NTSTATUS cli_smb2_fsctl_recv(
5011 : struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5012 : {
5013 14 : struct cli_smb2_fsctl_state *state = tevent_req_data(
5014 : req, struct cli_smb2_fsctl_state);
5015 14 : NTSTATUS status = NT_STATUS_OK;
5016 :
5017 14 : if (tevent_req_is_nterror(req, &status)) {
5018 14 : tevent_req_received(req);
5019 14 : return status;
5020 : }
5021 :
5022 0 : if (state->out.length == 0) {
5023 0 : *out = (DATA_BLOB) { .data = NULL, };
5024 : } else {
5025 : /*
5026 : * Can't use talloc_move() here, the outblobs from
5027 : * smb2cli_ioctl_recv() are not standalone talloc
5028 : * objects but just peek into the larger buffers
5029 : * received, hanging off "state".
5030 : */
5031 0 : *out = data_blob_talloc(
5032 : mem_ctx, state->out.data, state->out.length);
5033 0 : if (out->data == NULL) {
5034 0 : status = NT_STATUS_NO_MEMORY;
5035 : }
5036 : }
5037 :
5038 0 : tevent_req_received(req);
5039 0 : return NT_STATUS_OK;
5040 : }
|