s4:auth: Enforce machine authentication policy for NTLM authentication
[Samba.git] / source3 / libsmb / cli_smb2_fnum.c
blob3e4ff82b77614b50e4aeb6c7074d25814063994d
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
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
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.
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 "lib/util/string_wrappers.h"
46 #include "lib/util/idtree.h"
48 struct smb2_hnd {
49 uint64_t fid_persistent;
50 uint64_t fid_volatile;
54 * Handle mapping code.
57 /***************************************************************
58 Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
59 Ensures handle is owned by cli struct.
60 ***************************************************************/
62 static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
63 const struct smb2_hnd *ph, /* In */
64 uint16_t *pfnum) /* Out */
66 int ret;
67 struct idr_context *idp = cli->smb2.open_handles;
68 struct smb2_hnd *owned_h = talloc_memdup(cli,
69 ph,
70 sizeof(struct smb2_hnd));
72 if (owned_h == NULL) {
73 return NT_STATUS_NO_MEMORY;
76 if (idp == NULL) {
77 /* Lazy init */
78 cli->smb2.open_handles = idr_init(cli);
79 if (cli->smb2.open_handles == NULL) {
80 TALLOC_FREE(owned_h);
81 return NT_STATUS_NO_MEMORY;
83 idp = cli->smb2.open_handles;
86 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
87 if (ret == -1) {
88 TALLOC_FREE(owned_h);
89 return NT_STATUS_NO_MEMORY;
92 *pfnum = (uint16_t)ret;
93 return NT_STATUS_OK;
96 /***************************************************************
97 Return the smb2_hnd pointer associated with the given fnum.
98 ***************************************************************/
100 static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
101 uint16_t fnum, /* In */
102 struct smb2_hnd **pph) /* Out */
104 struct idr_context *idp = cli->smb2.open_handles;
106 if (idp == NULL) {
107 return NT_STATUS_INVALID_PARAMETER;
109 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
110 if (*pph == NULL) {
111 return NT_STATUS_INVALID_HANDLE;
113 return NT_STATUS_OK;
116 /***************************************************************
117 Delete the fnum to smb2_hnd mapping. Zeros out handle on
118 successful return.
119 ***************************************************************/
121 static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
122 struct smb2_hnd **pph, /* In */
123 uint16_t fnum) /* In */
125 struct idr_context *idp = cli->smb2.open_handles;
126 struct smb2_hnd *ph;
128 if (idp == NULL) {
129 return NT_STATUS_INVALID_PARAMETER;
132 ph = (struct smb2_hnd *)idr_find(idp, fnum);
133 if (ph != *pph) {
134 return NT_STATUS_INVALID_PARAMETER;
136 idr_remove(idp, fnum);
137 TALLOC_FREE(*pph);
138 return NT_STATUS_OK;
141 /***************************************************************
142 Oplock mapping code.
143 ***************************************************************/
145 static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
147 if (create_flags.batch_oplock) {
148 return SMB2_OPLOCK_LEVEL_BATCH;
149 } else if (create_flags.exclusive_oplock) {
150 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
153 /* create_flags doesn't do a level2 request. */
154 return SMB2_OPLOCK_LEVEL_NONE;
157 /***************************************************************
158 If we're on a DFS share, ensure we convert to a full DFS path
159 if this hasn't already been done.
160 ***************************************************************/
162 static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
163 struct cli_state *cli,
164 char *path)
166 bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
167 smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
168 bool is_already_dfs_path = false;
170 if (!is_dfs) {
171 return path;
173 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
174 if (is_already_dfs_path) {
175 return path;
177 if (path[0] == '\0') {
178 return talloc_asprintf(ctx,
179 "%s\\%s",
180 smbXcli_conn_remote_name(cli->conn),
181 cli->share);
183 while (*path == '\\') {
184 path++;
186 return talloc_asprintf(ctx,
187 "%s\\%s\\%s",
188 smbXcli_conn_remote_name(cli->conn),
189 cli->share,
190 path);
193 /***************************************************************
194 Small wrapper that allows SMB2 create to return a uint16_t fnum.
195 ***************************************************************/
197 struct cli_smb2_create_fnum_state {
198 struct cli_state *cli;
199 struct smb2_create_blobs in_cblobs;
200 struct smb2_create_blobs out_cblobs;
201 struct smb_create_returns cr;
202 struct symlink_reparse_struct *symlink;
203 uint16_t fnum;
204 struct tevent_req *subreq;
207 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
208 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
210 struct tevent_req *cli_smb2_create_fnum_send(
211 TALLOC_CTX *mem_ctx,
212 struct tevent_context *ev,
213 struct cli_state *cli,
214 const char *fname_in,
215 struct cli_smb2_create_flags create_flags,
216 uint32_t impersonation_level,
217 uint32_t desired_access,
218 uint32_t file_attributes,
219 uint32_t share_access,
220 uint32_t create_disposition,
221 uint32_t create_options,
222 const struct smb2_create_blobs *in_cblobs)
224 struct tevent_req *req, *subreq;
225 struct cli_smb2_create_fnum_state *state;
226 char *fname = NULL;
227 size_t fname_len = 0;
228 bool have_twrp;
229 NTTIME ntt;
230 NTSTATUS status;
232 req = tevent_req_create(mem_ctx, &state,
233 struct cli_smb2_create_fnum_state);
234 if (req == NULL) {
235 return NULL;
237 state->cli = cli;
239 fname = talloc_strdup(state, fname_in);
240 if (tevent_req_nomem(fname, req)) {
241 return tevent_req_post(req, ev);
244 if (cli->backup_intent) {
245 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
248 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
249 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
250 if (have_twrp) {
251 status = smb2_create_blob_add(
252 state,
253 &state->in_cblobs,
254 SMB2_CREATE_TAG_TWRP,
255 (DATA_BLOB) {
256 .data = (uint8_t *)&ntt,
257 .length = sizeof(ntt),
259 if (tevent_req_nterror(req, status)) {
260 return tevent_req_post(req, ev);
264 if (in_cblobs != NULL) {
265 uint32_t i;
266 for (i=0; i<in_cblobs->num_blobs; i++) {
267 struct smb2_create_blob *b = &in_cblobs->blobs[i];
268 status = smb2_create_blob_add(
269 state, &state->in_cblobs, b->tag, b->data);
270 if (!NT_STATUS_IS_OK(status)) {
271 tevent_req_nterror(req, status);
272 return tevent_req_post(req, ev);
277 fname = smb2_dfs_share_path(state, cli, fname);
278 if (tevent_req_nomem(fname, req)) {
279 return tevent_req_post(req, ev);
281 fname_len = strlen(fname);
283 /* SMB2 is pickier about pathnames. Ensure it doesn't
284 start in a '\' */
285 if (*fname == '\\') {
286 fname++;
287 fname_len--;
290 /* Or end in a '\' */
291 if (fname_len > 0 && fname[fname_len-1] == '\\') {
292 fname[fname_len-1] = '\0';
295 subreq = smb2cli_create_send(state, ev,
296 cli->conn,
297 cli->timeout,
298 cli->smb2.session,
299 cli->smb2.tcon,
300 fname,
301 flags_to_smb2_oplock(create_flags),
302 impersonation_level,
303 desired_access,
304 file_attributes,
305 share_access,
306 create_disposition,
307 create_options,
308 &state->in_cblobs);
309 if (tevent_req_nomem(subreq, req)) {
310 return tevent_req_post(req, ev);
312 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
314 state->subreq = subreq;
315 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
317 return req;
320 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
322 struct tevent_req *req = tevent_req_callback_data(
323 subreq, struct tevent_req);
324 struct cli_smb2_create_fnum_state *state = tevent_req_data(
325 req, struct cli_smb2_create_fnum_state);
326 struct smb2_hnd h;
327 NTSTATUS status;
329 status = smb2cli_create_recv(
330 subreq,
331 &h.fid_persistent,
332 &h.fid_volatile, &state->cr,
333 state,
334 &state->out_cblobs,
335 &state->symlink);
336 TALLOC_FREE(subreq);
337 if (tevent_req_nterror(req, status)) {
338 return;
341 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
342 if (tevent_req_nterror(req, status)) {
343 return;
345 tevent_req_done(req);
348 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
350 struct cli_smb2_create_fnum_state *state = tevent_req_data(
351 req, struct cli_smb2_create_fnum_state);
352 return tevent_req_cancel(state->subreq);
355 NTSTATUS cli_smb2_create_fnum_recv(
356 struct tevent_req *req,
357 uint16_t *pfnum,
358 struct smb_create_returns *cr,
359 TALLOC_CTX *mem_ctx,
360 struct smb2_create_blobs *out_cblobs,
361 struct symlink_reparse_struct **symlink)
363 struct cli_smb2_create_fnum_state *state = tevent_req_data(
364 req, struct cli_smb2_create_fnum_state);
365 NTSTATUS status;
367 if (tevent_req_is_nterror(req, &status)) {
368 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
369 (symlink != NULL)) {
370 *symlink = talloc_move(mem_ctx, &state->symlink);
372 state->cli->raw_status = status;
373 return status;
375 if (pfnum != NULL) {
376 *pfnum = state->fnum;
378 if (cr != NULL) {
379 *cr = state->cr;
381 if (out_cblobs != NULL) {
382 *out_cblobs = (struct smb2_create_blobs) {
383 .num_blobs = state->out_cblobs.num_blobs,
384 .blobs = talloc_move(
385 mem_ctx, &state->out_cblobs.blobs),
388 state->cli->raw_status = NT_STATUS_OK;
389 return NT_STATUS_OK;
392 NTSTATUS cli_smb2_create_fnum(
393 struct cli_state *cli,
394 const char *fname,
395 struct cli_smb2_create_flags create_flags,
396 uint32_t impersonation_level,
397 uint32_t desired_access,
398 uint32_t file_attributes,
399 uint32_t share_access,
400 uint32_t create_disposition,
401 uint32_t create_options,
402 const struct smb2_create_blobs *in_cblobs,
403 uint16_t *pfid,
404 struct smb_create_returns *cr,
405 TALLOC_CTX *mem_ctx,
406 struct smb2_create_blobs *out_cblobs)
408 TALLOC_CTX *frame = talloc_stackframe();
409 struct tevent_context *ev;
410 struct tevent_req *req;
411 NTSTATUS status = NT_STATUS_NO_MEMORY;
413 if (smbXcli_conn_has_async_calls(cli->conn)) {
415 * Can't use sync call while an async call is in flight
417 status = NT_STATUS_INVALID_PARAMETER;
418 goto fail;
420 ev = samba_tevent_context_init(frame);
421 if (ev == NULL) {
422 goto fail;
424 req = cli_smb2_create_fnum_send(
425 frame,
427 cli,
428 fname,
429 create_flags,
430 impersonation_level,
431 desired_access,
432 file_attributes,
433 share_access,
434 create_disposition,
435 create_options,
436 in_cblobs);
437 if (req == NULL) {
438 goto fail;
440 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
441 goto fail;
443 status = cli_smb2_create_fnum_recv(
444 req, pfid, cr, mem_ctx, out_cblobs, NULL);
445 fail:
446 TALLOC_FREE(frame);
447 return status;
450 /***************************************************************
451 Small wrapper that allows SMB2 close to use a uint16_t fnum.
452 ***************************************************************/
454 struct cli_smb2_close_fnum_state {
455 struct cli_state *cli;
456 uint16_t fnum;
457 struct smb2_hnd *ph;
460 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
462 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
463 struct tevent_context *ev,
464 struct cli_state *cli,
465 uint16_t fnum)
467 struct tevent_req *req, *subreq;
468 struct cli_smb2_close_fnum_state *state;
469 NTSTATUS status;
471 req = tevent_req_create(mem_ctx, &state,
472 struct cli_smb2_close_fnum_state);
473 if (req == NULL) {
474 return NULL;
476 state->cli = cli;
477 state->fnum = fnum;
479 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
480 if (tevent_req_nterror(req, status)) {
481 return tevent_req_post(req, ev);
484 subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
485 cli->smb2.session, cli->smb2.tcon,
486 0, state->ph->fid_persistent,
487 state->ph->fid_volatile);
488 if (tevent_req_nomem(subreq, req)) {
489 return tevent_req_post(req, ev);
491 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
492 return req;
495 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
497 struct tevent_req *req = tevent_req_callback_data(
498 subreq, struct tevent_req);
499 struct cli_smb2_close_fnum_state *state = tevent_req_data(
500 req, struct cli_smb2_close_fnum_state);
501 NTSTATUS status;
503 status = smb2cli_close_recv(subreq);
504 if (tevent_req_nterror(req, status)) {
505 return;
508 /* Delete the fnum -> handle mapping. */
509 status = delete_smb2_handle_mapping(state->cli, &state->ph,
510 state->fnum);
511 if (tevent_req_nterror(req, status)) {
512 return;
514 tevent_req_done(req);
517 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
519 struct cli_smb2_close_fnum_state *state = tevent_req_data(
520 req, struct cli_smb2_close_fnum_state);
521 NTSTATUS status = NT_STATUS_OK;
523 if (tevent_req_is_nterror(req, &status)) {
524 state->cli->raw_status = status;
526 tevent_req_received(req);
527 return status;
530 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
532 TALLOC_CTX *frame = talloc_stackframe();
533 struct tevent_context *ev;
534 struct tevent_req *req;
535 NTSTATUS status = NT_STATUS_NO_MEMORY;
537 if (smbXcli_conn_has_async_calls(cli->conn)) {
539 * Can't use sync call while an async call is in flight
541 status = NT_STATUS_INVALID_PARAMETER;
542 goto fail;
544 ev = samba_tevent_context_init(frame);
545 if (ev == NULL) {
546 goto fail;
548 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
549 if (req == NULL) {
550 goto fail;
552 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
553 goto fail;
555 status = cli_smb2_close_fnum_recv(req);
556 fail:
557 TALLOC_FREE(frame);
558 return status;
561 struct cli_smb2_set_info_fnum_state {
562 uint8_t dummy;
565 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
567 struct tevent_req *cli_smb2_set_info_fnum_send(
568 TALLOC_CTX *mem_ctx,
569 struct tevent_context *ev,
570 struct cli_state *cli,
571 uint16_t fnum,
572 uint8_t in_info_type,
573 uint8_t in_info_class,
574 const DATA_BLOB *in_input_buffer,
575 uint32_t in_additional_info)
577 struct tevent_req *req = NULL, *subreq = NULL;
578 struct cli_smb2_set_info_fnum_state *state = NULL;
579 struct smb2_hnd *ph = NULL;
580 NTSTATUS status;
582 req = tevent_req_create(
583 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
584 if (req == NULL) {
585 return NULL;
588 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
589 if (tevent_req_nterror(req, status)) {
590 return tevent_req_post(req, ev);
593 subreq = smb2cli_set_info_send(
594 state,
596 cli->conn,
597 cli->timeout,
598 cli->smb2.session,
599 cli->smb2.tcon,
600 in_info_type,
601 in_info_class,
602 in_input_buffer,
603 in_additional_info,
604 ph->fid_persistent,
605 ph->fid_volatile);
606 if (tevent_req_nomem(subreq, req)) {
607 return tevent_req_post(req, ev);
609 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
610 return req;
613 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
615 NTSTATUS status = smb2cli_set_info_recv(subreq);
616 tevent_req_simple_finish_ntstatus(subreq, status);
619 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
621 return tevent_req_simple_recv_ntstatus(req);
624 NTSTATUS cli_smb2_set_info_fnum(
625 struct cli_state *cli,
626 uint16_t fnum,
627 uint8_t in_info_type,
628 uint8_t in_info_class,
629 const DATA_BLOB *in_input_buffer,
630 uint32_t in_additional_info)
632 TALLOC_CTX *frame = talloc_stackframe();
633 struct tevent_context *ev = NULL;
634 struct tevent_req *req = NULL;
635 NTSTATUS status = NT_STATUS_NO_MEMORY;
636 bool ok;
638 if (smbXcli_conn_has_async_calls(cli->conn)) {
640 * Can't use sync call while an async call is in flight
642 status = NT_STATUS_INVALID_PARAMETER;
643 goto fail;
645 ev = samba_tevent_context_init(frame);
646 if (ev == NULL) {
647 goto fail;
649 req = cli_smb2_set_info_fnum_send(
650 frame,
652 cli,
653 fnum,
654 in_info_type,
655 in_info_class,
656 in_input_buffer,
657 in_additional_info);
658 if (req == NULL) {
659 goto fail;
661 ok = tevent_req_poll_ntstatus(req, ev, &status);
662 if (!ok) {
663 goto fail;
665 status = cli_smb2_set_info_fnum_recv(req);
666 fail:
667 TALLOC_FREE(frame);
668 return status;
671 struct cli_smb2_delete_on_close_state {
672 struct cli_state *cli;
673 uint8_t data[1];
674 DATA_BLOB inbuf;
677 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
679 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
680 struct tevent_context *ev,
681 struct cli_state *cli,
682 uint16_t fnum,
683 bool flag)
685 struct tevent_req *req = NULL;
686 struct cli_smb2_delete_on_close_state *state = NULL;
687 struct tevent_req *subreq = NULL;
688 uint8_t in_info_type;
689 uint8_t in_file_info_class;
691 req = tevent_req_create(mem_ctx, &state,
692 struct cli_smb2_delete_on_close_state);
693 if (req == NULL) {
694 return NULL;
696 state->cli = cli;
699 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
700 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
702 in_info_type = 1;
703 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
704 /* Setup data array. */
705 SCVAL(&state->data[0], 0, flag ? 1 : 0);
706 state->inbuf.data = &state->data[0];
707 state->inbuf.length = 1;
709 subreq = cli_smb2_set_info_fnum_send(
710 state,
712 cli,
713 fnum,
714 in_info_type,
715 in_file_info_class,
716 &state->inbuf,
718 if (tevent_req_nomem(subreq, req)) {
719 return tevent_req_post(req, ev);
721 tevent_req_set_callback(subreq,
722 cli_smb2_delete_on_close_done,
723 req);
724 return req;
727 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
729 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
730 tevent_req_simple_finish_ntstatus(subreq, status);
733 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
735 struct cli_smb2_delete_on_close_state *state =
736 tevent_req_data(req,
737 struct cli_smb2_delete_on_close_state);
738 NTSTATUS status;
740 if (tevent_req_is_nterror(req, &status)) {
741 state->cli->raw_status = status;
742 tevent_req_received(req);
743 return status;
746 state->cli->raw_status = NT_STATUS_OK;
747 tevent_req_received(req);
748 return NT_STATUS_OK;
751 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
753 TALLOC_CTX *frame = talloc_stackframe();
754 struct tevent_context *ev;
755 struct tevent_req *req;
756 NTSTATUS status = NT_STATUS_NO_MEMORY;
758 if (smbXcli_conn_has_async_calls(cli->conn)) {
760 * Can't use sync call while an async call is in flight
762 status = NT_STATUS_INVALID_PARAMETER;
763 goto fail;
765 ev = samba_tevent_context_init(frame);
766 if (ev == NULL) {
767 goto fail;
769 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
770 if (req == NULL) {
771 goto fail;
773 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
774 goto fail;
776 status = cli_smb2_delete_on_close_recv(req);
777 fail:
778 TALLOC_FREE(frame);
779 return status;
782 struct cli_smb2_mkdir_state {
783 struct tevent_context *ev;
784 struct cli_state *cli;
787 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
788 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
790 struct tevent_req *cli_smb2_mkdir_send(
791 TALLOC_CTX *mem_ctx,
792 struct tevent_context *ev,
793 struct cli_state *cli,
794 const char *dname)
796 struct tevent_req *req = NULL, *subreq = NULL;
797 struct cli_smb2_mkdir_state *state = NULL;
799 req = tevent_req_create(
800 mem_ctx, &state, struct cli_smb2_mkdir_state);
801 if (req == NULL) {
802 return NULL;
804 state->ev = ev;
805 state->cli = cli;
807 /* Ensure this is a directory. */
808 subreq = cli_smb2_create_fnum_send(
809 state, /* mem_ctx */
810 ev, /* ev */
811 cli, /* cli */
812 dname, /* fname */
813 (struct cli_smb2_create_flags){0}, /* create_flags */
814 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
815 FILE_READ_ATTRIBUTES, /* desired_access */
816 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
817 FILE_SHARE_READ|
818 FILE_SHARE_WRITE, /* share_access */
819 FILE_CREATE, /* create_disposition */
820 FILE_DIRECTORY_FILE, /* create_options */
821 NULL); /* in_cblobs */
822 if (tevent_req_nomem(subreq, req)) {
823 return tevent_req_post(req, ev);
825 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
826 return req;
829 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
831 struct tevent_req *req = tevent_req_callback_data(
832 subreq, struct tevent_req);
833 struct cli_smb2_mkdir_state *state = tevent_req_data(
834 req, struct cli_smb2_mkdir_state);
835 NTSTATUS status;
836 uint16_t fnum = 0xffff;
838 status = cli_smb2_create_fnum_recv(
839 subreq, &fnum, NULL, NULL, NULL, NULL);
840 TALLOC_FREE(subreq);
841 if (tevent_req_nterror(req, status)) {
842 return;
845 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
846 if (tevent_req_nomem(subreq, req)) {
847 return;
849 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
852 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
854 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
855 tevent_req_simple_finish_ntstatus(subreq, status);
858 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
860 return tevent_req_simple_recv_ntstatus(req);
863 struct cli_smb2_rmdir_state {
864 struct tevent_context *ev;
865 struct cli_state *cli;
866 const char *dname;
867 const struct smb2_create_blobs *in_cblobs;
868 uint16_t fnum;
869 NTSTATUS status;
872 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
873 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
874 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
875 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
877 struct tevent_req *cli_smb2_rmdir_send(
878 TALLOC_CTX *mem_ctx,
879 struct tevent_context *ev,
880 struct cli_state *cli,
881 const char *dname,
882 const struct smb2_create_blobs *in_cblobs)
884 struct tevent_req *req = NULL, *subreq = NULL;
885 struct cli_smb2_rmdir_state *state = NULL;
887 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
888 if (req == NULL) {
889 return NULL;
891 state->ev = ev;
892 state->cli = cli;
893 state->dname = dname;
894 state->in_cblobs = in_cblobs;
896 subreq = cli_smb2_create_fnum_send(
897 state,
898 state->ev,
899 state->cli,
900 state->dname,
901 (struct cli_smb2_create_flags){0},
902 SMB2_IMPERSONATION_IMPERSONATION,
903 DELETE_ACCESS, /* desired_access */
904 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
905 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
906 FILE_OPEN, /* create_disposition */
907 FILE_DIRECTORY_FILE, /* create_options */
908 state->in_cblobs); /* in_cblobs */
909 if (tevent_req_nomem(subreq, req)) {
910 return tevent_req_post(req, ev);
912 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
913 return req;
916 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
918 struct tevent_req *req = tevent_req_callback_data(
919 subreq, struct tevent_req);
920 struct cli_smb2_rmdir_state *state = tevent_req_data(
921 req, struct cli_smb2_rmdir_state);
922 NTSTATUS status;
924 status = cli_smb2_create_fnum_recv(
925 subreq, &state->fnum, NULL, NULL, NULL, NULL);
926 TALLOC_FREE(subreq);
928 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
930 * Naive option to match our SMB1 code. Assume the
931 * symlink path that tripped us up was the last
932 * component and try again. Eventually we will have to
933 * deal with the returned path unprocessed component. JRA.
935 subreq = cli_smb2_create_fnum_send(
936 state,
937 state->ev,
938 state->cli,
939 state->dname,
940 (struct cli_smb2_create_flags){0},
941 SMB2_IMPERSONATION_IMPERSONATION,
942 DELETE_ACCESS, /* desired_access */
943 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
944 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
945 FILE_OPEN, /* create_disposition */
946 FILE_DIRECTORY_FILE|
947 FILE_DELETE_ON_CLOSE|
948 FILE_OPEN_REPARSE_POINT, /* create_options */
949 state->in_cblobs); /* in_cblobs */
950 if (tevent_req_nomem(subreq, req)) {
951 return;
953 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
954 return;
957 if (tevent_req_nterror(req, status)) {
958 return;
961 subreq = cli_smb2_delete_on_close_send(
962 state, state->ev, state->cli, state->fnum, true);
963 if (tevent_req_nomem(subreq, req)) {
964 return;
966 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
969 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
971 struct tevent_req *req = tevent_req_callback_data(
972 subreq, struct tevent_req);
973 struct cli_smb2_rmdir_state *state = tevent_req_data(
974 req, struct cli_smb2_rmdir_state);
975 NTSTATUS status;
977 status = cli_smb2_create_fnum_recv(
978 subreq, &state->fnum, NULL, NULL, NULL, NULL);
979 TALLOC_FREE(subreq);
980 if (tevent_req_nterror(req, status)) {
981 return;
984 subreq = cli_smb2_delete_on_close_send(
985 state, state->ev, state->cli, state->fnum, true);
986 if (tevent_req_nomem(subreq, req)) {
987 return;
989 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
992 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
994 struct tevent_req *req = tevent_req_callback_data(
995 subreq, struct tevent_req);
996 struct cli_smb2_rmdir_state *state = tevent_req_data(
997 req, struct cli_smb2_rmdir_state);
999 state->status = cli_smb2_delete_on_close_recv(subreq);
1000 TALLOC_FREE(subreq);
1003 * Close the fd even if the set_disp failed
1006 subreq = cli_smb2_close_fnum_send(
1007 state, state->ev, state->cli, state->fnum);
1008 if (tevent_req_nomem(subreq, req)) {
1009 return;
1011 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1014 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1016 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1017 tevent_req_simple_finish_ntstatus(subreq, status);
1020 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1022 struct cli_smb2_rmdir_state *state = tevent_req_data(
1023 req, struct cli_smb2_rmdir_state);
1024 NTSTATUS status;
1026 if (tevent_req_is_nterror(req, &status)) {
1027 return status;
1029 return state->status;
1032 /***************************************************************
1033 Small wrapper that allows SMB2 to unlink a pathname.
1034 ***************************************************************/
1036 struct cli_smb2_unlink_state {
1037 struct tevent_context *ev;
1038 struct cli_state *cli;
1039 const char *fname;
1040 const struct smb2_create_blobs *in_cblobs;
1043 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1044 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1045 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1047 struct tevent_req *cli_smb2_unlink_send(
1048 TALLOC_CTX *mem_ctx,
1049 struct tevent_context *ev,
1050 struct cli_state *cli,
1051 const char *fname,
1052 const struct smb2_create_blobs *in_cblobs)
1054 struct tevent_req *req = NULL, *subreq = NULL;
1055 struct cli_smb2_unlink_state *state = NULL;
1057 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1058 if (req == NULL) {
1059 return NULL;
1061 state->ev = ev;
1062 state->cli = cli;
1063 state->fname = fname;
1064 state->in_cblobs = in_cblobs;
1066 subreq = cli_smb2_create_fnum_send(
1067 state, /* mem_ctx */
1068 state->ev, /* tevent_context */
1069 state->cli, /* cli_struct */
1070 state->fname, /* filename */
1071 (struct cli_smb2_create_flags){0},
1072 SMB2_IMPERSONATION_IMPERSONATION,
1073 DELETE_ACCESS, /* desired_access */
1074 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1075 FILE_SHARE_READ|
1076 FILE_SHARE_WRITE|
1077 FILE_SHARE_DELETE, /* share_access */
1078 FILE_OPEN, /* create_disposition */
1079 FILE_DELETE_ON_CLOSE, /* create_options */
1080 state->in_cblobs); /* in_cblobs */
1081 if (tevent_req_nomem(subreq, req)) {
1082 return tevent_req_post(req, ev);
1084 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1085 return req;
1088 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1090 struct tevent_req *req = tevent_req_callback_data(
1091 subreq, struct tevent_req);
1092 struct cli_smb2_unlink_state *state = tevent_req_data(
1093 req, struct cli_smb2_unlink_state);
1094 uint16_t fnum = 0xffff;
1095 NTSTATUS status;
1097 status = cli_smb2_create_fnum_recv(
1098 subreq, &fnum, NULL, NULL, NULL, NULL);
1099 TALLOC_FREE(subreq);
1101 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1102 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1104 * Naive option to match our SMB1 code. Assume the
1105 * symlink path that tripped us up was the last
1106 * component and try again. Eventually we will have to
1107 * deal with the returned path unprocessed component. JRA.
1109 subreq = cli_smb2_create_fnum_send(
1110 state, /* mem_ctx */
1111 state->ev, /* tevent_context */
1112 state->cli, /* cli_struct */
1113 state->fname, /* filename */
1114 (struct cli_smb2_create_flags){0},
1115 SMB2_IMPERSONATION_IMPERSONATION,
1116 DELETE_ACCESS, /* desired_access */
1117 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1118 FILE_SHARE_READ|
1119 FILE_SHARE_WRITE|
1120 FILE_SHARE_DELETE, /* share_access */
1121 FILE_OPEN, /* create_disposition */
1122 FILE_DELETE_ON_CLOSE|
1123 FILE_OPEN_REPARSE_POINT, /* create_options */
1124 state->in_cblobs); /* in_cblobs */
1125 if (tevent_req_nomem(subreq, req)) {
1126 return;
1128 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1129 return;
1132 if (tevent_req_nterror(req, status)) {
1133 return;
1136 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1137 if (tevent_req_nomem(subreq, req)) {
1138 return;
1140 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1143 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1145 struct tevent_req *req = tevent_req_callback_data(
1146 subreq, struct tevent_req);
1147 struct cli_smb2_unlink_state *state = tevent_req_data(
1148 req, struct cli_smb2_unlink_state);
1149 uint16_t fnum = 0xffff;
1150 NTSTATUS status;
1152 status = cli_smb2_create_fnum_recv(
1153 subreq, &fnum, NULL, NULL, NULL, NULL);
1154 TALLOC_FREE(subreq);
1155 if (tevent_req_nterror(req, status)) {
1156 return;
1159 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1160 if (tevent_req_nomem(subreq, req)) {
1161 return;
1163 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1166 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1168 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1169 tevent_req_simple_finish_ntstatus(subreq, status);
1172 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1174 return tevent_req_simple_recv_ntstatus(req);
1177 static ssize_t sid_parse_wire(TALLOC_CTX *mem_ctx, const uint8_t *data,
1178 struct dom_sid *sid, size_t num_rdata)
1180 size_t sid_size;
1181 enum ndr_err_code ndr_err;
1182 DATA_BLOB in = data_blob_const(data, num_rdata);
1184 ndr_err = ndr_pull_struct_blob(&in,
1185 mem_ctx,
1186 sid,
1187 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
1188 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1189 return 0;
1192 sid_size = ndr_size_dom_sid(sid, 0);
1193 if (sid_size > num_rdata) {
1194 return 0;
1197 return sid_size;
1200 /***************************************************************
1201 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1202 ***************************************************************/
1204 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1205 uint32_t dir_data_length,
1206 struct file_info *finfo,
1207 uint32_t *next_offset)
1209 size_t namelen = 0;
1210 size_t slen = 0, slen2 = 0;
1211 size_t ret = 0;
1212 uint32_t _next_offset = 0;
1214 if (dir_data_length < 4) {
1215 return NT_STATUS_INFO_LENGTH_MISMATCH;
1218 _next_offset = IVAL(dir_data, 0);
1220 if (_next_offset > dir_data_length) {
1221 return NT_STATUS_INFO_LENGTH_MISMATCH;
1224 if (_next_offset != 0) {
1225 /* Ensure we only read what in this record. */
1226 dir_data_length = _next_offset;
1229 if (dir_data_length < 92) {
1230 return NT_STATUS_INFO_LENGTH_MISMATCH;
1233 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1234 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1235 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1236 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1237 finfo->allocated_size = PULL_LE_U64(dir_data, 40);
1238 finfo->size = PULL_LE_U64(dir_data, 48);
1239 finfo->mode = PULL_LE_U32(dir_data, 56);
1240 finfo->ino = PULL_LE_U64(dir_data, 60);
1241 finfo->st_ex_dev = PULL_LE_U32(dir_data, 68);
1242 finfo->st_ex_nlink = PULL_LE_U32(dir_data, 76);
1243 finfo->reparse_tag = PULL_LE_U32(dir_data, 80);
1244 finfo->st_ex_mode = wire_perms_to_unix(PULL_LE_U32(dir_data, 84));
1246 slen = sid_parse_wire(finfo, dir_data+88, &finfo->owner_sid,
1247 dir_data_length-88);
1248 if (slen == 0) {
1249 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1251 slen2 = sid_parse_wire(finfo, dir_data+88+slen, &finfo->group_sid,
1252 dir_data_length-88-slen);
1253 if (slen2 == 0) {
1254 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1256 slen += slen2;
1258 namelen = PULL_LE_U32(dir_data, 88+slen);
1259 ret = pull_string_talloc(finfo,
1260 dir_data,
1261 FLAGS2_UNICODE_STRINGS,
1262 &finfo->name,
1263 dir_data+92+slen,
1264 namelen,
1265 STR_UNICODE);
1266 if (ret == (size_t)-1) {
1267 /* Bad conversion. */
1268 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1271 if (finfo->name == NULL) {
1272 /* Bad conversion. */
1273 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1276 *next_offset = _next_offset;
1277 return NT_STATUS_OK;
1280 /***************************************************************
1281 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1282 ***************************************************************/
1284 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1285 uint32_t dir_data_length,
1286 struct file_info *finfo,
1287 uint32_t *next_offset)
1289 size_t namelen = 0;
1290 size_t slen = 0;
1291 size_t ret = 0;
1293 if (dir_data_length < 4) {
1294 return NT_STATUS_INFO_LENGTH_MISMATCH;
1297 *next_offset = IVAL(dir_data, 0);
1299 if (*next_offset > dir_data_length) {
1300 return NT_STATUS_INFO_LENGTH_MISMATCH;
1303 if (*next_offset != 0) {
1304 /* Ensure we only read what in this record. */
1305 dir_data_length = *next_offset;
1308 if (dir_data_length < 105) {
1309 return NT_STATUS_INFO_LENGTH_MISMATCH;
1312 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1313 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1314 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1315 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1316 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1317 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1318 finfo->attr = IVAL(dir_data + 56, 0);
1319 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1320 namelen = IVAL(dir_data + 60,0);
1321 if (namelen > (dir_data_length - 104)) {
1322 return NT_STATUS_INFO_LENGTH_MISMATCH;
1324 slen = CVAL(dir_data + 68, 0);
1325 if (slen > 24) {
1326 return NT_STATUS_INFO_LENGTH_MISMATCH;
1328 ret = pull_string_talloc(finfo,
1329 dir_data,
1330 FLAGS2_UNICODE_STRINGS,
1331 &finfo->short_name,
1332 dir_data + 70,
1333 slen,
1334 STR_UNICODE);
1335 if (ret == (size_t)-1) {
1336 /* Bad conversion. */
1337 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1340 ret = pull_string_talloc(finfo,
1341 dir_data,
1342 FLAGS2_UNICODE_STRINGS,
1343 &finfo->name,
1344 dir_data + 104,
1345 namelen,
1346 STR_UNICODE);
1347 if (ret == (size_t)-1) {
1348 /* Bad conversion. */
1349 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1352 if (finfo->name == NULL) {
1353 /* Bad conversion. */
1354 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1357 return NT_STATUS_OK;
1360 /*******************************************************************
1361 Given a filename - get its directory name
1362 ********************************************************************/
1364 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1365 const char *dir,
1366 char **parent,
1367 const char **name)
1369 char *p;
1370 ptrdiff_t len;
1372 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1374 if (p == NULL) {
1375 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1376 return false;
1378 if (name) {
1379 *name = dir;
1381 return true;
1384 len = p-dir;
1386 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1387 return false;
1389 (*parent)[len] = '\0';
1391 if (name) {
1392 *name = p+1;
1394 return true;
1397 struct cli_smb2_list_dir_data {
1398 uint8_t *data;
1399 uint32_t length;
1402 struct cli_smb2_list_state {
1403 struct tevent_context *ev;
1404 struct cli_state *cli;
1405 const char *mask;
1407 uint16_t fnum;
1409 NTSTATUS status;
1410 struct cli_smb2_list_dir_data *response;
1411 uint32_t offset;
1412 unsigned int info_level;
1415 static void cli_smb2_list_opened(struct tevent_req *subreq);
1416 static void cli_smb2_list_done(struct tevent_req *subreq);
1417 static void cli_smb2_list_closed(struct tevent_req *subreq);
1419 struct tevent_req *cli_smb2_list_send(
1420 TALLOC_CTX *mem_ctx,
1421 struct tevent_context *ev,
1422 struct cli_state *cli,
1423 const char *pathname,
1424 unsigned int info_level,
1425 bool posix)
1427 struct tevent_req *req = NULL, *subreq = NULL;
1428 struct cli_smb2_list_state *state = NULL;
1429 char *parent = NULL;
1430 bool ok;
1431 struct smb2_create_blobs *in_cblobs = NULL;
1433 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1434 if (req == NULL) {
1435 return NULL;
1437 state->ev = ev;
1438 state->cli = cli;
1439 state->status = NT_STATUS_OK;
1440 state->info_level = info_level;
1442 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1443 if (!ok) {
1444 tevent_req_oom(req);
1445 return tevent_req_post(req, ev);
1448 if (smbXcli_conn_have_posix(cli->conn) && posix) {
1449 NTSTATUS status;
1451 /* The mode MUST be 0 when opening an existing file/dir, and
1452 * will be ignored by the server.
1454 uint8_t linear_mode[4] = { 0 };
1455 DATA_BLOB blob = { .data=linear_mode,
1456 .length=sizeof(linear_mode) };
1458 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1459 if (in_cblobs == NULL) {
1460 return NULL;
1463 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1464 SMB2_CREATE_TAG_POSIX, blob);
1465 if (!NT_STATUS_IS_OK(status)) {
1466 tevent_req_nterror(req, status);
1467 return tevent_req_post(req, ev);
1471 subreq = cli_smb2_create_fnum_send(
1472 state, /* mem_ctx */
1473 ev, /* ev */
1474 cli, /* cli */
1475 parent, /* fname */
1476 (struct cli_smb2_create_flags){0}, /* create_flags */
1477 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1478 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1479 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1480 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1481 FILE_OPEN, /* create_disposition */
1482 FILE_DIRECTORY_FILE, /* create_options */
1483 in_cblobs); /* in_cblobs */
1484 TALLOC_FREE(in_cblobs);
1485 if (tevent_req_nomem(subreq, req)) {
1486 return tevent_req_post(req, ev);
1488 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1489 return req;
1492 static void cli_smb2_list_opened(struct tevent_req *subreq)
1494 struct tevent_req *req = tevent_req_callback_data(
1495 subreq, struct tevent_req);
1496 struct cli_smb2_list_state *state = tevent_req_data(
1497 req, struct cli_smb2_list_state);
1498 NTSTATUS status;
1500 status = cli_smb2_create_fnum_recv(
1501 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1502 TALLOC_FREE(subreq);
1503 if (tevent_req_nterror(req, status)) {
1504 return;
1508 * Make our caller get back to us via cli_smb2_list_recv(),
1509 * triggering the smb2_query_directory_send()
1511 tevent_req_defer_callback(req, state->ev);
1512 tevent_req_notify_callback(req);
1515 static void cli_smb2_list_done(struct tevent_req *subreq)
1517 struct tevent_req *req = tevent_req_callback_data(
1518 subreq, struct tevent_req);
1519 struct cli_smb2_list_state *state = tevent_req_data(
1520 req, struct cli_smb2_list_state);
1521 struct cli_smb2_list_dir_data *response = NULL;
1523 response = talloc(state, struct cli_smb2_list_dir_data);
1524 if (tevent_req_nomem(response, req)) {
1525 return;
1528 state->status = smb2cli_query_directory_recv(
1529 subreq, response, &response->data, &response->length);
1530 TALLOC_FREE(subreq);
1532 if (NT_STATUS_IS_OK(state->status)) {
1533 state->response = response;
1534 state->offset = 0;
1536 tevent_req_defer_callback(req, state->ev);
1537 tevent_req_notify_callback(req);
1538 return;
1541 TALLOC_FREE(response);
1543 subreq = cli_smb2_close_fnum_send(
1544 state, state->ev, state->cli, state->fnum);
1545 if (tevent_req_nomem(subreq, req)) {
1546 return;
1548 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1551 static void cli_smb2_list_closed(struct tevent_req *subreq)
1553 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1554 tevent_req_simple_finish_ntstatus(subreq, status);
1558 * Return the next finfo directory.
1560 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1561 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1562 * NT_STATUS_RETRY, which will then trigger the caller again when the
1563 * QUERY_DIRECTORY has returned with another buffer. This way we
1564 * guarantee that no asynchronous request is open after this call
1565 * returns an entry, so that other synchronous requests can be issued
1566 * on the same connection while the directoy listing proceeds.
1568 NTSTATUS cli_smb2_list_recv(
1569 struct tevent_req *req,
1570 TALLOC_CTX *mem_ctx,
1571 struct file_info **pfinfo)
1573 struct cli_smb2_list_state *state = tevent_req_data(
1574 req, struct cli_smb2_list_state);
1575 struct cli_smb2_list_dir_data *response = NULL;
1576 struct file_info *finfo = NULL;
1577 NTSTATUS status;
1578 uint32_t next_offset = 0;
1579 bool in_progress;
1581 in_progress = tevent_req_is_in_progress(req);
1583 if (!in_progress) {
1584 if (!tevent_req_is_nterror(req, &status)) {
1585 status = NT_STATUS_NO_MORE_FILES;
1587 goto fail;
1590 response = state->response;
1591 if (response == NULL) {
1592 struct tevent_req *subreq = NULL;
1593 struct cli_state *cli = state->cli;
1594 struct smb2_hnd *ph = NULL;
1595 uint32_t max_trans, max_avail_len;
1596 bool ok;
1598 if (!NT_STATUS_IS_OK(state->status)) {
1599 status = state->status;
1600 goto fail;
1603 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1604 if (!NT_STATUS_IS_OK(status)) {
1605 goto fail;
1608 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1609 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1610 if (ok) {
1611 max_trans = MIN(max_trans, max_avail_len);
1614 subreq = smb2cli_query_directory_send(
1615 state, /* mem_ctx */
1616 state->ev, /* ev */
1617 cli->conn, /* conn */
1618 cli->timeout, /* timeout_msec */
1619 cli->smb2.session, /* session */
1620 cli->smb2.tcon, /* tcon */
1621 state->info_level, /* level */
1622 0, /* flags */
1623 0, /* file_index */
1624 ph->fid_persistent, /* fid_persistent */
1625 ph->fid_volatile, /* fid_volatile */
1626 state->mask, /* mask */
1627 max_trans); /* outbuf_len */
1628 if (subreq == NULL) {
1629 status = NT_STATUS_NO_MEMORY;
1630 goto fail;
1632 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1633 return NT_STATUS_RETRY;
1636 SMB_ASSERT(response->length > state->offset);
1638 finfo = talloc_zero(mem_ctx, struct file_info);
1639 if (finfo == NULL) {
1640 status = NT_STATUS_NO_MEMORY;
1641 goto fail;
1644 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1645 status = parse_finfo_posix_info(
1646 response->data + state->offset,
1647 response->length - state->offset,
1648 finfo,
1649 &next_offset);
1650 } else {
1651 status = parse_finfo_id_both_directory_info(
1652 response->data + state->offset,
1653 response->length - state->offset,
1654 finfo,
1655 &next_offset);
1657 if (!NT_STATUS_IS_OK(status)) {
1658 goto fail;
1661 status = is_bad_finfo_name(state->cli, finfo);
1662 if (!NT_STATUS_IS_OK(status)) {
1663 goto fail;
1667 * parse_finfo_id_both_directory_info() checks for overflow,
1668 * no need to check again here.
1670 state->offset += next_offset;
1672 if (next_offset == 0) {
1673 TALLOC_FREE(state->response);
1676 tevent_req_defer_callback(req, state->ev);
1677 tevent_req_notify_callback(req);
1679 *pfinfo = finfo;
1680 return NT_STATUS_OK;
1682 fail:
1683 TALLOC_FREE(finfo);
1684 tevent_req_received(req);
1685 return status;
1688 /***************************************************************
1689 Wrapper that allows SMB2 to query a path info (basic level).
1690 Synchronous only.
1691 ***************************************************************/
1693 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1694 const char *name,
1695 SMB_STRUCT_STAT *sbuf,
1696 uint32_t *attributes)
1698 NTSTATUS status;
1699 struct smb_create_returns cr;
1700 uint16_t fnum = 0xffff;
1701 size_t namelen = strlen(name);
1703 if (smbXcli_conn_has_async_calls(cli->conn)) {
1705 * Can't use sync call while an async call is in flight
1707 return NT_STATUS_INVALID_PARAMETER;
1710 /* SMB2 is pickier about pathnames. Ensure it doesn't
1711 end in a '\' */
1712 if (namelen > 0 && name[namelen-1] == '\\') {
1713 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1714 if (modname == NULL) {
1715 return NT_STATUS_NO_MEMORY;
1717 name = modname;
1720 /* This is commonly used as a 'cd'. Try qpathinfo on
1721 a directory handle first. */
1723 status = cli_smb2_create_fnum(cli,
1724 name,
1725 (struct cli_smb2_create_flags){0},
1726 SMB2_IMPERSONATION_IMPERSONATION,
1727 FILE_READ_ATTRIBUTES, /* desired_access */
1728 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1729 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1730 FILE_OPEN, /* create_disposition */
1731 FILE_DIRECTORY_FILE, /* create_options */
1732 NULL,
1733 &fnum,
1734 &cr,
1735 NULL,
1736 NULL);
1738 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1739 /* Maybe a file ? */
1740 status = cli_smb2_create_fnum(cli,
1741 name,
1742 (struct cli_smb2_create_flags){0},
1743 SMB2_IMPERSONATION_IMPERSONATION,
1744 FILE_READ_ATTRIBUTES, /* desired_access */
1745 0, /* file attributes */
1746 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1747 FILE_OPEN, /* create_disposition */
1748 0, /* create_options */
1749 NULL,
1750 &fnum,
1751 &cr,
1752 NULL,
1753 NULL);
1756 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1757 /* Maybe a reparse point ? */
1758 status = cli_smb2_create_fnum(cli,
1759 name,
1760 (struct cli_smb2_create_flags){0},
1761 SMB2_IMPERSONATION_IMPERSONATION,
1762 FILE_READ_ATTRIBUTES, /* desired_access */
1763 0, /* file attributes */
1764 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1765 FILE_OPEN, /* create_disposition */
1766 FILE_OPEN_REPARSE_POINT, /* create_options */
1767 NULL,
1768 &fnum,
1769 &cr,
1770 NULL,
1771 NULL);
1774 if (!NT_STATUS_IS_OK(status)) {
1775 return status;
1778 status = cli_smb2_close_fnum(cli, fnum);
1780 ZERO_STRUCTP(sbuf);
1782 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1783 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1784 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1785 sbuf->st_ex_size = cr.end_of_file;
1786 *attributes = cr.file_attributes;
1788 return status;
1791 struct cli_smb2_query_info_fnum_state {
1792 DATA_BLOB outbuf;
1795 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1797 struct tevent_req *cli_smb2_query_info_fnum_send(
1798 TALLOC_CTX *mem_ctx,
1799 struct tevent_context *ev,
1800 struct cli_state *cli,
1801 uint16_t fnum,
1802 uint8_t in_info_type,
1803 uint8_t in_info_class,
1804 uint32_t in_max_output_length,
1805 const DATA_BLOB *in_input_buffer,
1806 uint32_t in_additional_info,
1807 uint32_t in_flags)
1809 struct tevent_req *req = NULL, *subreq = NULL;
1810 struct cli_smb2_query_info_fnum_state *state = NULL;
1811 struct smb2_hnd *ph = NULL;
1812 NTSTATUS status;
1814 req = tevent_req_create(
1815 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1816 if (req == NULL) {
1817 return req;
1820 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1821 if (tevent_req_nterror(req, status)) {
1822 return tevent_req_post(req, ev);
1825 subreq = smb2cli_query_info_send(
1826 state,
1828 cli->conn,
1829 cli->timeout,
1830 cli->smb2.session,
1831 cli->smb2.tcon,
1832 in_info_type,
1833 in_info_class,
1834 in_max_output_length,
1835 in_input_buffer,
1836 in_additional_info,
1837 in_flags,
1838 ph->fid_persistent,
1839 ph->fid_volatile);
1840 if (tevent_req_nomem(subreq, req)) {
1841 return tevent_req_post(req, ev);
1843 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1844 return req;
1847 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1849 struct tevent_req *req = tevent_req_callback_data(
1850 subreq, struct tevent_req);
1851 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1852 req, struct cli_smb2_query_info_fnum_state);
1853 DATA_BLOB outbuf;
1854 NTSTATUS status;
1856 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1857 TALLOC_FREE(subreq);
1858 if (tevent_req_nterror(req, status)) {
1859 return;
1863 * We have to dup the memory here because outbuf.data is not
1864 * returned as a talloc object by smb2cli_query_info_recv.
1865 * It's a pointer into the received buffer.
1867 state->outbuf = data_blob_dup_talloc(state, outbuf);
1869 if ((outbuf.length != 0) &&
1870 tevent_req_nomem(state->outbuf.data, req)) {
1871 return;
1873 tevent_req_done(req);
1876 NTSTATUS cli_smb2_query_info_fnum_recv(
1877 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1879 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1880 req, struct cli_smb2_query_info_fnum_state);
1881 NTSTATUS status;
1883 if (tevent_req_is_nterror(req, &status)) {
1884 return status;
1886 *outbuf = (DATA_BLOB) {
1887 .data = talloc_move(mem_ctx, &state->outbuf.data),
1888 .length = state->outbuf.length,
1890 return NT_STATUS_OK;
1893 NTSTATUS cli_smb2_query_info_fnum(
1894 struct cli_state *cli,
1895 uint16_t fnum,
1896 uint8_t in_info_type,
1897 uint8_t in_info_class,
1898 uint32_t in_max_output_length,
1899 const DATA_BLOB *in_input_buffer,
1900 uint32_t in_additional_info,
1901 uint32_t in_flags,
1902 TALLOC_CTX *mem_ctx,
1903 DATA_BLOB *outbuf)
1905 TALLOC_CTX *frame = talloc_stackframe();
1906 struct tevent_context *ev = NULL;
1907 struct tevent_req *req = NULL;
1908 NTSTATUS status = NT_STATUS_NO_MEMORY;
1909 bool ok;
1911 if (smbXcli_conn_has_async_calls(cli->conn)) {
1913 * Can't use sync call while an async call is in flight
1915 status = NT_STATUS_INVALID_PARAMETER;
1916 goto fail;
1918 ev = samba_tevent_context_init(frame);
1919 if (ev == NULL) {
1920 goto fail;
1922 req = cli_smb2_query_info_fnum_send(
1923 frame,
1925 cli,
1926 fnum,
1927 in_info_type,
1928 in_info_class,
1929 in_max_output_length,
1930 in_input_buffer,
1931 in_additional_info,
1932 in_flags);
1933 if (req == NULL) {
1934 goto fail;
1936 ok = tevent_req_poll_ntstatus(req, ev, &status);
1937 if (!ok) {
1938 goto fail;
1940 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1941 fail:
1942 TALLOC_FREE(frame);
1943 return status;
1946 /***************************************************************
1947 Helper function for pathname operations.
1948 ***************************************************************/
1950 struct get_fnum_from_path_state {
1951 struct tevent_context *ev;
1952 struct cli_state *cli;
1953 const char *name;
1954 uint32_t desired_access;
1955 uint16_t fnum;
1958 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1959 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1960 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1962 static struct tevent_req *get_fnum_from_path_send(
1963 TALLOC_CTX *mem_ctx,
1964 struct tevent_context *ev,
1965 struct cli_state *cli,
1966 const char *name,
1967 uint32_t desired_access)
1969 struct tevent_req *req = NULL, *subreq = NULL;
1970 struct get_fnum_from_path_state *state = NULL;
1971 size_t namelen = strlen(name);
1973 req = tevent_req_create(
1974 mem_ctx, &state, struct get_fnum_from_path_state);
1975 if (req == NULL) {
1976 return NULL;
1978 state->ev = ev;
1979 state->cli = cli;
1980 state->name = name;
1981 state->desired_access = desired_access;
1984 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
1985 * '\'
1987 if (namelen > 0 && name[namelen-1] == '\\') {
1988 state->name = talloc_strndup(state, name, namelen-1);
1989 if (tevent_req_nomem(state->name, req)) {
1990 return tevent_req_post(req, ev);
1994 subreq = cli_smb2_create_fnum_send(
1995 state, /* mem_ctx, */
1996 ev, /* ev */
1997 cli, /* cli */
1998 state->name, /* fname */
1999 (struct cli_smb2_create_flags){0}, /* create_flags */
2000 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2001 desired_access, /* desired_access */
2002 0, /* file_attributes */
2003 FILE_SHARE_READ|
2004 FILE_SHARE_WRITE|
2005 FILE_SHARE_DELETE, /* share_access */
2006 FILE_OPEN, /* create_disposition */
2007 0, /* create_options */
2008 NULL); /* in_cblobs */
2009 if (tevent_req_nomem(subreq, req)) {
2010 return tevent_req_post(req, ev);
2012 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2013 return req;
2016 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2018 struct tevent_req *req = tevent_req_callback_data(
2019 subreq, struct tevent_req);
2020 struct get_fnum_from_path_state *state = tevent_req_data(
2021 req, struct get_fnum_from_path_state);
2022 NTSTATUS status;
2024 status = cli_smb2_create_fnum_recv(
2025 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2026 TALLOC_FREE(subreq);
2028 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
2030 * Naive option to match our SMB1 code. Assume the
2031 * symlink path that tripped us up was the last
2032 * component and try again. Eventually we will have to
2033 * deal with the returned path unprocessed component. JRA.
2035 subreq = cli_smb2_create_fnum_send(
2036 state, /* mem_ctx, */
2037 state->ev, /* ev */
2038 state->cli, /* cli */
2039 state->name, /* fname */
2040 (struct cli_smb2_create_flags){0}, /* create_flags */
2041 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2042 state->desired_access, /* desired_access */
2043 0, /* file_attributes */
2044 FILE_SHARE_READ|
2045 FILE_SHARE_WRITE|
2046 FILE_SHARE_DELETE, /* share_access */
2047 FILE_OPEN, /* create_disposition */
2048 FILE_OPEN_REPARSE_POINT, /* create_options */
2049 NULL); /* in_cblobs */
2050 if (tevent_req_nomem(subreq, req)) {
2051 return;
2053 tevent_req_set_callback(
2054 subreq, get_fnum_from_path_opened_reparse, req);
2055 return;
2058 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2059 subreq = cli_smb2_create_fnum_send(
2060 state, /* mem_ctx, */
2061 state->ev, /* ev */
2062 state->cli, /* cli */
2063 state->name, /* fname */
2064 (struct cli_smb2_create_flags){0}, /* create_flags */
2065 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2066 state->desired_access, /* desired_access */
2067 0, /* file_attributes */
2068 FILE_SHARE_READ|
2069 FILE_SHARE_WRITE|
2070 FILE_SHARE_DELETE, /* share_access */
2071 FILE_OPEN, /* create_disposition */
2072 FILE_DIRECTORY_FILE, /* create_options */
2073 NULL); /* in_cblobs */
2074 if (tevent_req_nomem(subreq, req)) {
2075 return;
2077 tevent_req_set_callback(
2078 subreq, get_fnum_from_path_opened_dir, req);
2079 return;
2082 if (tevent_req_nterror(req, status)) {
2083 return;
2085 tevent_req_done(req);
2088 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2090 struct tevent_req *req = tevent_req_callback_data(
2091 subreq, struct tevent_req);
2092 struct get_fnum_from_path_state *state = tevent_req_data(
2093 req, struct get_fnum_from_path_state);
2094 NTSTATUS status = cli_smb2_create_fnum_recv(
2095 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2096 tevent_req_simple_finish_ntstatus(subreq, status);
2099 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2101 /* Abstraction violation, but these two are just the same... */
2102 get_fnum_from_path_opened_reparse(subreq);
2105 static NTSTATUS get_fnum_from_path_recv(
2106 struct tevent_req *req, uint16_t *pfnum)
2108 struct get_fnum_from_path_state *state = tevent_req_data(
2109 req, struct get_fnum_from_path_state);
2110 NTSTATUS status = NT_STATUS_OK;
2112 if (!tevent_req_is_nterror(req, &status)) {
2113 *pfnum = state->fnum;
2115 tevent_req_received(req);
2116 return status;
2119 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2120 const char *name,
2121 uint32_t desired_access,
2122 uint16_t *pfnum)
2124 TALLOC_CTX *frame = talloc_stackframe();
2125 struct tevent_context *ev = NULL;
2126 struct tevent_req *req = NULL;
2127 NTSTATUS status = NT_STATUS_NO_MEMORY;
2129 if (smbXcli_conn_has_async_calls(cli->conn)) {
2130 status = NT_STATUS_INVALID_PARAMETER;
2131 goto fail;
2133 ev = samba_tevent_context_init(frame);
2134 if (ev == NULL) {
2135 goto fail;
2137 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2138 if (req == NULL) {
2139 goto fail;
2141 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2142 goto fail;
2144 status = get_fnum_from_path_recv(req, pfnum);
2145 fail:
2146 TALLOC_FREE(frame);
2147 return status;
2150 /***************************************************************
2151 Wrapper that allows SMB2 to query a path info (ALTNAME level).
2152 Synchronous only.
2153 ***************************************************************/
2155 NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
2156 const char *name,
2157 fstring alt_name)
2159 NTSTATUS status;
2160 DATA_BLOB outbuf = data_blob_null;
2161 uint16_t fnum = 0xffff;
2162 uint32_t altnamelen = 0;
2163 TALLOC_CTX *frame = talloc_stackframe();
2165 if (smbXcli_conn_has_async_calls(cli->conn)) {
2167 * Can't use sync call while an async call is in flight
2169 status = NT_STATUS_INVALID_PARAMETER;
2170 goto fail;
2173 status = get_fnum_from_path(cli,
2174 name,
2175 FILE_READ_ATTRIBUTES,
2176 &fnum);
2178 if (!NT_STATUS_IS_OK(status)) {
2179 goto fail;
2182 status = cli_smb2_query_info_fnum(
2183 cli,
2184 fnum,
2185 1, /* in_info_type */
2186 (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
2187 0xFFFF, /* in_max_output_length */
2188 NULL, /* in_input_buffer */
2189 0, /* in_additional_info */
2190 0, /* in_flags */
2191 frame,
2192 &outbuf);
2194 if (!NT_STATUS_IS_OK(status)) {
2195 goto fail;
2198 /* Parse the reply. */
2199 if (outbuf.length < 4) {
2200 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2201 goto fail;
2204 altnamelen = IVAL(outbuf.data, 0);
2205 if (altnamelen > outbuf.length - 4) {
2206 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2207 goto fail;
2210 if (altnamelen > 0) {
2211 size_t ret = 0;
2212 char *short_name = NULL;
2213 ret = pull_string_talloc(frame,
2214 outbuf.data,
2215 FLAGS2_UNICODE_STRINGS,
2216 &short_name,
2217 outbuf.data + 4,
2218 altnamelen,
2219 STR_UNICODE);
2220 if (ret == (size_t)-1) {
2221 /* Bad conversion. */
2222 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2223 goto fail;
2226 fstrcpy(alt_name, short_name);
2227 } else {
2228 alt_name[0] = '\0';
2231 status = NT_STATUS_OK;
2233 fail:
2235 if (fnum != 0xffff) {
2236 cli_smb2_close_fnum(cli, fnum);
2239 cli->raw_status = status;
2241 TALLOC_FREE(frame);
2242 return status;
2245 /***************************************************************
2246 Wrapper that allows SMB2 to get pathname attributes.
2247 Synchronous only.
2248 ***************************************************************/
2250 NTSTATUS cli_smb2_getatr(struct cli_state *cli,
2251 const char *name,
2252 uint32_t *pattr,
2253 off_t *size,
2254 time_t *write_time)
2256 NTSTATUS status;
2257 uint16_t fnum = 0xffff;
2258 struct timespec write_time_ts;
2259 TALLOC_CTX *frame = talloc_stackframe();
2261 if (smbXcli_conn_has_async_calls(cli->conn)) {
2263 * Can't use sync call while an async call is in flight
2265 status = NT_STATUS_INVALID_PARAMETER;
2266 goto fail;
2269 status = get_fnum_from_path(cli,
2270 name,
2271 FILE_READ_ATTRIBUTES,
2272 &fnum);
2274 if (!NT_STATUS_IS_OK(status)) {
2275 goto fail;
2278 status = cli_qfileinfo_basic(
2279 cli,
2280 fnum,
2281 pattr,
2282 size,
2283 NULL, /* create_time */
2284 NULL, /* access_time */
2285 &write_time_ts,
2286 NULL, /* change_time */
2287 NULL); /* ino */
2288 if (!NT_STATUS_IS_OK(status)) {
2289 goto fail;
2291 if (write_time != NULL) {
2292 *write_time = write_time_ts.tv_sec;
2295 fail:
2297 if (fnum != 0xffff) {
2298 cli_smb2_close_fnum(cli, fnum);
2301 cli->raw_status = status;
2303 TALLOC_FREE(frame);
2304 return status;
2307 struct cli_smb2_qpathinfo2_state {
2308 struct tevent_context *ev;
2309 struct cli_state *cli;
2310 uint16_t fnum;
2312 NTSTATUS queryinfo_status;
2313 struct timespec create_time;
2314 struct timespec access_time;
2315 struct timespec write_time;
2316 struct timespec change_time;
2317 off_t size;
2318 uint32_t attr;
2319 SMB_INO_T ino;
2322 static void cli_smb2_qpathinfo2_opened(struct tevent_req *subreq);
2323 static void cli_smb2_qpathinfo2_done(struct tevent_req *subreq);
2324 static void cli_smb2_qpathinfo2_closed(struct tevent_req *subreq);
2326 struct tevent_req *cli_smb2_qpathinfo2_send(TALLOC_CTX *mem_ctx,
2327 struct tevent_context *ev,
2328 struct cli_state *cli,
2329 const char *fname)
2331 struct tevent_req *req = NULL, *subreq = NULL;
2332 struct cli_smb2_qpathinfo2_state *state = NULL;
2334 req = tevent_req_create(mem_ctx,
2335 &state,
2336 struct cli_smb2_qpathinfo2_state);
2337 if (req == NULL) {
2338 return NULL;
2340 state->ev = ev;
2341 state->cli = cli;
2343 subreq = get_fnum_from_path_send(mem_ctx,
2345 cli,
2346 fname,
2347 FILE_READ_ATTRIBUTES);
2348 if (tevent_req_nomem(subreq, req)) {
2349 return tevent_req_post(req, ev);
2351 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_opened, req);
2352 return req;
2355 static void cli_smb2_qpathinfo2_opened(struct tevent_req *subreq)
2357 struct tevent_req *req =
2358 tevent_req_callback_data(subreq, struct tevent_req);
2359 struct cli_smb2_qpathinfo2_state *state =
2360 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2361 NTSTATUS status;
2363 status = get_fnum_from_path_recv(subreq, &state->fnum);
2364 TALLOC_FREE(subreq);
2365 if (tevent_req_nterror(req, status)) {
2366 return;
2369 subreq = cli_qfileinfo_basic_send(state,
2370 state->ev,
2371 state->cli,
2372 state->fnum);
2373 if (tevent_req_nomem(subreq, req)) {
2374 return;
2376 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_done, req);
2379 static void cli_smb2_qpathinfo2_done(struct tevent_req *subreq)
2381 struct tevent_req *req =
2382 tevent_req_callback_data(subreq, struct tevent_req);
2383 struct cli_smb2_qpathinfo2_state *state =
2384 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2386 state->queryinfo_status = cli_qfileinfo_basic_recv(subreq,
2387 &state->attr,
2388 &state->size,
2389 &state->create_time,
2390 &state->access_time,
2391 &state->write_time,
2392 &state->change_time,
2393 &state->ino);
2394 TALLOC_FREE(subreq);
2396 subreq = cli_smb2_close_fnum_send(state,
2397 state->ev,
2398 state->cli,
2399 state->fnum);
2400 if (tevent_req_nomem(subreq, req)) {
2401 return;
2403 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_closed, req);
2406 static void cli_smb2_qpathinfo2_closed(struct tevent_req *subreq)
2408 struct tevent_req *req =
2409 tevent_req_callback_data(subreq, struct tevent_req);
2410 struct cli_smb2_qpathinfo2_state *state =
2411 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2412 NTSTATUS status;
2414 status = cli_smb2_close_fnum_recv(subreq);
2415 TALLOC_FREE(subreq);
2416 if (tevent_req_nterror(req, status)) {
2417 return;
2419 if (tevent_req_nterror(req, state->queryinfo_status)) {
2420 return;
2422 tevent_req_done(req);
2425 NTSTATUS cli_smb2_qpathinfo2_recv(struct tevent_req *req,
2426 struct timespec *create_time,
2427 struct timespec *access_time,
2428 struct timespec *write_time,
2429 struct timespec *change_time,
2430 off_t *size,
2431 uint32_t *attr,
2432 SMB_INO_T *ino)
2434 struct cli_smb2_qpathinfo2_state *state =
2435 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2436 NTSTATUS status;
2438 if (tevent_req_is_nterror(req, &status)) {
2439 return status;
2442 if (create_time != NULL) {
2443 *create_time = state->create_time;
2445 if (access_time != NULL) {
2446 *access_time = state->access_time;
2448 if (write_time != NULL) {
2449 *write_time = state->write_time;
2451 if (change_time != NULL) {
2452 *change_time = state->change_time;
2454 if (attr != NULL) {
2455 *attr = state->attr;
2457 if (size != NULL) {
2458 *size = state->size;
2460 if (ino) {
2461 *ino = state->ino;
2464 return NT_STATUS_OK;
2467 /***************************************************************
2468 Wrapper that allows SMB2 to query pathname streams.
2469 Synchronous only.
2470 ***************************************************************/
2472 NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
2473 const char *name,
2474 TALLOC_CTX *mem_ctx,
2475 unsigned int *pnum_streams,
2476 struct stream_struct **pstreams)
2478 NTSTATUS status;
2479 uint16_t fnum = 0xffff;
2480 DATA_BLOB outbuf = data_blob_null;
2481 TALLOC_CTX *frame = talloc_stackframe();
2483 if (smbXcli_conn_has_async_calls(cli->conn)) {
2485 * Can't use sync call while an async call is in flight
2487 status = NT_STATUS_INVALID_PARAMETER;
2488 goto fail;
2491 status = get_fnum_from_path(cli,
2492 name,
2493 FILE_READ_ATTRIBUTES,
2494 &fnum);
2496 if (!NT_STATUS_IS_OK(status)) {
2497 goto fail;
2500 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
2501 level 22 (SMB2_FILE_STREAM_INFORMATION). */
2503 status = cli_smb2_query_info_fnum(
2504 cli,
2505 fnum,
2506 1, /* in_info_type */
2507 (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */
2508 0xFFFF, /* in_max_output_length */
2509 NULL, /* in_input_buffer */
2510 0, /* in_additional_info */
2511 0, /* in_flags */
2512 frame,
2513 &outbuf);
2515 if (!NT_STATUS_IS_OK(status)) {
2516 goto fail;
2519 /* Parse the reply. */
2520 if (!parse_streams_blob(mem_ctx,
2521 outbuf.data,
2522 outbuf.length,
2523 pnum_streams,
2524 pstreams)) {
2525 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2526 goto fail;
2529 fail:
2531 if (fnum != 0xffff) {
2532 cli_smb2_close_fnum(cli, fnum);
2535 cli->raw_status = status;
2537 TALLOC_FREE(frame);
2538 return status;
2541 /***************************************************************
2542 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2543 a pathname.
2544 Synchronous only.
2545 ***************************************************************/
2547 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2548 const char *name,
2549 uint8_t in_info_type,
2550 uint8_t in_file_info_class,
2551 const DATA_BLOB *p_in_data)
2553 NTSTATUS status;
2554 uint16_t fnum = 0xffff;
2555 TALLOC_CTX *frame = talloc_stackframe();
2557 if (smbXcli_conn_has_async_calls(cli->conn)) {
2559 * Can't use sync call while an async call is in flight
2561 status = NT_STATUS_INVALID_PARAMETER;
2562 goto fail;
2565 status = get_fnum_from_path(cli,
2566 name,
2567 FILE_WRITE_ATTRIBUTES,
2568 &fnum);
2570 if (!NT_STATUS_IS_OK(status)) {
2571 goto fail;
2574 status = cli_smb2_set_info_fnum(
2575 cli,
2576 fnum,
2577 in_info_type,
2578 in_file_info_class,
2579 p_in_data, /* in_input_buffer */
2580 0); /* in_additional_info */
2581 fail:
2583 if (fnum != 0xffff) {
2584 cli_smb2_close_fnum(cli, fnum);
2587 cli->raw_status = status;
2589 TALLOC_FREE(frame);
2590 return status;
2594 /***************************************************************
2595 Wrapper that allows SMB2 to set pathname attributes.
2596 Synchronous only.
2597 ***************************************************************/
2599 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2600 const char *name,
2601 uint32_t attr,
2602 time_t mtime)
2604 uint8_t inbuf_store[40];
2605 DATA_BLOB inbuf = data_blob_null;
2607 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2608 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2610 inbuf.data = inbuf_store;
2611 inbuf.length = sizeof(inbuf_store);
2612 data_blob_clear(&inbuf);
2615 * SMB1 uses attr == 0 to clear all attributes
2616 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2617 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2618 * request attribute change.
2620 * SMB2 uses exactly the reverse. Unfortunately as the
2621 * cli_setatr() ABI is exposed inside libsmbclient,
2622 * we must make the SMB2 cli_smb2_setatr() call
2623 * export the same ABI as the SMB1 cli_setatr()
2624 * which calls it. This means reversing the sense
2625 * of the requested attr argument if it's zero
2626 * or FILE_ATTRIBUTE_NORMAL.
2628 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2631 if (attr == 0) {
2632 attr = FILE_ATTRIBUTE_NORMAL;
2633 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2634 attr = 0;
2637 SIVAL(inbuf.data, 32, attr);
2638 if (mtime != 0) {
2639 put_long_date((char *)inbuf.data + 16,mtime);
2641 /* Set all the other times to -1. */
2642 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2643 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2644 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2646 return cli_smb2_setpathinfo(cli,
2647 name,
2648 1, /* in_info_type */
2649 /* in_file_info_class */
2650 SMB_FILE_BASIC_INFORMATION - 1000,
2651 &inbuf);
2655 /***************************************************************
2656 Wrapper that allows SMB2 to set file handle times.
2657 Synchronous only.
2658 ***************************************************************/
2660 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2661 uint16_t fnum,
2662 time_t change_time,
2663 time_t access_time,
2664 time_t write_time)
2666 uint8_t inbuf_store[40];
2667 DATA_BLOB inbuf = data_blob_null;
2669 if (smbXcli_conn_has_async_calls(cli->conn)) {
2671 * Can't use sync call while an async call is in flight
2673 return NT_STATUS_INVALID_PARAMETER;
2676 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2677 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2679 inbuf.data = inbuf_store;
2680 inbuf.length = sizeof(inbuf_store);
2681 data_blob_clear(&inbuf);
2683 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2684 if (change_time != 0) {
2685 put_long_date((char *)inbuf.data + 24, change_time);
2687 if (access_time != 0) {
2688 put_long_date((char *)inbuf.data + 8, access_time);
2690 if (write_time != 0) {
2691 put_long_date((char *)inbuf.data + 16, write_time);
2694 cli->raw_status = cli_smb2_set_info_fnum(
2695 cli,
2696 fnum,
2697 1, /* in_info_type */
2698 SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
2699 &inbuf, /* in_input_buffer */
2700 0); /* in_additional_info */
2702 return cli->raw_status;
2705 /***************************************************************
2706 Wrapper that allows SMB2 to query disk attributes (size).
2707 Synchronous only.
2708 ***************************************************************/
2710 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2711 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2713 NTSTATUS status;
2714 uint16_t fnum = 0xffff;
2715 DATA_BLOB outbuf = data_blob_null;
2716 uint32_t sectors_per_unit = 0;
2717 uint32_t bytes_per_sector = 0;
2718 uint64_t total_size = 0;
2719 uint64_t size_free = 0;
2720 TALLOC_CTX *frame = talloc_stackframe();
2722 if (smbXcli_conn_has_async_calls(cli->conn)) {
2724 * Can't use sync call while an async call is in flight
2726 status = NT_STATUS_INVALID_PARAMETER;
2727 goto fail;
2730 /* First open the top level directory. */
2731 status = cli_smb2_create_fnum(cli,
2732 path,
2733 (struct cli_smb2_create_flags){0},
2734 SMB2_IMPERSONATION_IMPERSONATION,
2735 FILE_READ_ATTRIBUTES, /* desired_access */
2736 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2737 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2738 FILE_OPEN, /* create_disposition */
2739 FILE_DIRECTORY_FILE, /* create_options */
2740 NULL,
2741 &fnum,
2742 NULL,
2743 NULL,
2744 NULL);
2746 if (!NT_STATUS_IS_OK(status)) {
2747 goto fail;
2750 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2751 level 3 (SMB_FS_SIZE_INFORMATION). */
2753 status = cli_smb2_query_info_fnum(
2754 cli,
2755 fnum,
2756 2, /* in_info_type */
2757 3, /* in_file_info_class */
2758 0xFFFF, /* in_max_output_length */
2759 NULL, /* in_input_buffer */
2760 0, /* in_additional_info */
2761 0, /* in_flags */
2762 frame,
2763 &outbuf);
2764 if (!NT_STATUS_IS_OK(status)) {
2765 goto fail;
2768 /* Parse the reply. */
2769 if (outbuf.length != 24) {
2770 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2771 goto fail;
2774 total_size = BVAL(outbuf.data, 0);
2775 size_free = BVAL(outbuf.data, 8);
2776 sectors_per_unit = IVAL(outbuf.data, 16);
2777 bytes_per_sector = IVAL(outbuf.data, 20);
2779 if (bsize) {
2780 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2782 if (total) {
2783 *total = total_size;
2785 if (avail) {
2786 *avail = size_free;
2789 status = NT_STATUS_OK;
2791 fail:
2793 if (fnum != 0xffff) {
2794 cli_smb2_close_fnum(cli, fnum);
2797 cli->raw_status = status;
2799 TALLOC_FREE(frame);
2800 return status;
2803 /***************************************************************
2804 Wrapper that allows SMB2 to query file system sizes.
2805 Synchronous only.
2806 ***************************************************************/
2808 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2809 uint64_t *total_allocation_units,
2810 uint64_t *caller_allocation_units,
2811 uint64_t *actual_allocation_units,
2812 uint64_t *sectors_per_allocation_unit,
2813 uint64_t *bytes_per_sector)
2815 NTSTATUS status;
2816 uint16_t fnum = 0xffff;
2817 DATA_BLOB outbuf = data_blob_null;
2818 TALLOC_CTX *frame = talloc_stackframe();
2820 if (smbXcli_conn_has_async_calls(cli->conn)) {
2822 * Can't use sync call while an async call is in flight
2824 status = NT_STATUS_INVALID_PARAMETER;
2825 goto fail;
2828 /* First open the top level directory. */
2829 status =
2830 cli_smb2_create_fnum(cli, "",
2831 (struct cli_smb2_create_flags){0},
2832 SMB2_IMPERSONATION_IMPERSONATION,
2833 FILE_READ_ATTRIBUTES, /* desired_access */
2834 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2835 FILE_SHARE_READ | FILE_SHARE_WRITE |
2836 FILE_SHARE_DELETE, /* share_access */
2837 FILE_OPEN, /* create_disposition */
2838 FILE_DIRECTORY_FILE, /* create_options */
2839 NULL,
2840 &fnum,
2841 NULL,
2842 NULL,
2843 NULL);
2845 if (!NT_STATUS_IS_OK(status)) {
2846 goto fail;
2849 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2850 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2852 status = cli_smb2_query_info_fnum(
2853 cli,
2854 fnum,
2855 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2856 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2857 0xFFFF, /* in_max_output_length */
2858 NULL, /* in_input_buffer */
2859 0, /* in_additional_info */
2860 0, /* in_flags */
2861 frame,
2862 &outbuf);
2863 if (!NT_STATUS_IS_OK(status)) {
2864 goto fail;
2867 if (outbuf.length < 32) {
2868 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2869 goto fail;
2872 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2873 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2874 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2875 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2876 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2878 fail:
2880 if (fnum != 0xffff) {
2881 cli_smb2_close_fnum(cli, fnum);
2884 cli->raw_status = status;
2886 TALLOC_FREE(frame);
2887 return status;
2890 /***************************************************************
2891 Wrapper that allows SMB2 to query file system attributes.
2892 Synchronous only.
2893 ***************************************************************/
2895 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2897 NTSTATUS status;
2898 uint16_t fnum = 0xffff;
2899 DATA_BLOB outbuf = data_blob_null;
2900 TALLOC_CTX *frame = talloc_stackframe();
2902 if (smbXcli_conn_has_async_calls(cli->conn)) {
2904 * Can't use sync call while an async call is in flight
2906 status = NT_STATUS_INVALID_PARAMETER;
2907 goto fail;
2910 /* First open the top level directory. */
2911 status =
2912 cli_smb2_create_fnum(cli, "",
2913 (struct cli_smb2_create_flags){0},
2914 SMB2_IMPERSONATION_IMPERSONATION,
2915 FILE_READ_ATTRIBUTES, /* desired_access */
2916 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2917 FILE_SHARE_READ | FILE_SHARE_WRITE |
2918 FILE_SHARE_DELETE, /* share_access */
2919 FILE_OPEN, /* create_disposition */
2920 FILE_DIRECTORY_FILE, /* create_options */
2921 NULL,
2922 &fnum,
2923 NULL,
2924 NULL,
2925 NULL);
2927 if (!NT_STATUS_IS_OK(status)) {
2928 goto fail;
2931 status = cli_smb2_query_info_fnum(
2932 cli,
2933 fnum,
2934 2, /* in_info_type */
2935 5, /* in_file_info_class */
2936 0xFFFF, /* in_max_output_length */
2937 NULL, /* in_input_buffer */
2938 0, /* in_additional_info */
2939 0, /* in_flags */
2940 frame,
2941 &outbuf);
2942 if (!NT_STATUS_IS_OK(status)) {
2943 goto fail;
2946 if (outbuf.length < 12) {
2947 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2948 goto fail;
2951 *fs_attr = IVAL(outbuf.data, 0);
2953 fail:
2955 if (fnum != 0xffff) {
2956 cli_smb2_close_fnum(cli, fnum);
2959 cli->raw_status = status;
2961 TALLOC_FREE(frame);
2962 return status;
2965 /***************************************************************
2966 Wrapper that allows SMB2 to query file system volume info.
2967 Synchronous only.
2968 ***************************************************************/
2970 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2971 TALLOC_CTX *mem_ctx,
2972 char **_volume_name,
2973 uint32_t *pserial_number,
2974 time_t *pdate)
2976 NTSTATUS status;
2977 uint16_t fnum = 0xffff;
2978 DATA_BLOB outbuf = data_blob_null;
2979 uint32_t nlen;
2980 char *volume_name = NULL;
2981 TALLOC_CTX *frame = talloc_stackframe();
2983 if (smbXcli_conn_has_async_calls(cli->conn)) {
2985 * Can't use sync call while an async call is in flight
2987 status = NT_STATUS_INVALID_PARAMETER;
2988 goto fail;
2991 /* First open the top level directory. */
2992 status =
2993 cli_smb2_create_fnum(cli, "",
2994 (struct cli_smb2_create_flags){0},
2995 SMB2_IMPERSONATION_IMPERSONATION,
2996 FILE_READ_ATTRIBUTES, /* desired_access */
2997 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2998 FILE_SHARE_READ | FILE_SHARE_WRITE |
2999 FILE_SHARE_DELETE, /* share_access */
3000 FILE_OPEN, /* create_disposition */
3001 FILE_DIRECTORY_FILE, /* create_options */
3002 NULL,
3003 &fnum,
3004 NULL,
3005 NULL,
3006 NULL);
3008 if (!NT_STATUS_IS_OK(status)) {
3009 goto fail;
3012 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
3013 level 1 (SMB_FS_VOLUME_INFORMATION). */
3015 status = cli_smb2_query_info_fnum(
3016 cli,
3017 fnum,
3018 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3019 /* in_file_info_class */
3020 SMB_FS_VOLUME_INFORMATION - 1000,
3021 0xFFFF, /* in_max_output_length */
3022 NULL, /* in_input_buffer */
3023 0, /* in_additional_info */
3024 0, /* in_flags */
3025 frame,
3026 &outbuf);
3027 if (!NT_STATUS_IS_OK(status)) {
3028 goto fail;
3031 if (outbuf.length < 24) {
3032 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3033 goto fail;
3036 if (pdate) {
3037 struct timespec ts;
3038 ts = interpret_long_date((char *)outbuf.data);
3039 *pdate = ts.tv_sec;
3041 if (pserial_number) {
3042 *pserial_number = IVAL(outbuf.data,8);
3044 nlen = IVAL(outbuf.data,12);
3045 if (nlen + 18 < 18) {
3046 /* Integer wrap. */
3047 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3048 goto fail;
3051 * The next check is safe as we know outbuf.length >= 24
3052 * from above.
3054 if (nlen > (outbuf.length - 18)) {
3055 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3056 goto fail;
3059 pull_string_talloc(mem_ctx,
3060 (const char *)outbuf.data,
3062 &volume_name,
3063 outbuf.data + 18,
3064 nlen,
3065 STR_UNICODE);
3066 if (volume_name == NULL) {
3067 status = map_nt_error_from_unix(errno);
3068 goto fail;
3071 *_volume_name = volume_name;
3073 fail:
3075 if (fnum != 0xffff) {
3076 cli_smb2_close_fnum(cli, fnum);
3079 cli->raw_status = status;
3081 TALLOC_FREE(frame);
3082 return status;
3085 struct cli_smb2_mxac_state {
3086 struct tevent_context *ev;
3087 struct cli_state *cli;
3088 const char *fname;
3089 struct smb2_create_blobs in_cblobs;
3090 uint16_t fnum;
3091 NTSTATUS status;
3092 uint32_t mxac;
3095 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
3096 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
3098 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
3099 struct tevent_context *ev,
3100 struct cli_state *cli,
3101 const char *fname)
3103 struct tevent_req *req = NULL, *subreq = NULL;
3104 struct cli_smb2_mxac_state *state = NULL;
3105 NTSTATUS status;
3107 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
3108 if (req == NULL) {
3109 return NULL;
3111 *state = (struct cli_smb2_mxac_state) {
3112 .ev = ev,
3113 .cli = cli,
3114 .fname = fname,
3117 status = smb2_create_blob_add(state,
3118 &state->in_cblobs,
3119 SMB2_CREATE_TAG_MXAC,
3120 data_blob(NULL, 0));
3121 if (tevent_req_nterror(req, status)) {
3122 return tevent_req_post(req, ev);
3125 subreq = cli_smb2_create_fnum_send(
3126 state,
3127 state->ev,
3128 state->cli,
3129 state->fname,
3130 (struct cli_smb2_create_flags){0},
3131 SMB2_IMPERSONATION_IMPERSONATION,
3132 FILE_READ_ATTRIBUTES,
3133 0, /* file attributes */
3134 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
3135 FILE_OPEN,
3136 0, /* create_options */
3137 &state->in_cblobs);
3138 if (tevent_req_nomem(subreq, req)) {
3139 return tevent_req_post(req, ev);
3141 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
3142 return req;
3145 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
3147 struct tevent_req *req = tevent_req_callback_data(
3148 subreq, struct tevent_req);
3149 struct cli_smb2_mxac_state *state = tevent_req_data(
3150 req, struct cli_smb2_mxac_state);
3151 struct smb2_create_blobs out_cblobs = {0};
3152 struct smb2_create_blob *mxac_blob = NULL;
3153 NTSTATUS status;
3155 status = cli_smb2_create_fnum_recv(
3156 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
3157 TALLOC_FREE(subreq);
3159 if (tevent_req_nterror(req, status)) {
3160 return;
3163 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
3164 if (mxac_blob == NULL) {
3165 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3166 goto close;
3168 if (mxac_blob->data.length != 8) {
3169 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3170 goto close;
3173 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
3174 state->mxac = IVAL(mxac_blob->data.data, 4);
3176 close:
3177 subreq = cli_smb2_close_fnum_send(
3178 state, state->ev, state->cli, state->fnum);
3179 if (tevent_req_nomem(subreq, req)) {
3180 return;
3182 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
3184 return;
3187 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
3189 struct tevent_req *req = tevent_req_callback_data(
3190 subreq, struct tevent_req);
3191 NTSTATUS status;
3193 status = cli_smb2_close_fnum_recv(subreq);
3194 if (tevent_req_nterror(req, status)) {
3195 return;
3198 tevent_req_done(req);
3201 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3203 struct cli_smb2_mxac_state *state = tevent_req_data(
3204 req, struct cli_smb2_mxac_state);
3205 NTSTATUS status;
3207 if (tevent_req_is_nterror(req, &status)) {
3208 return status;
3211 if (!NT_STATUS_IS_OK(state->status)) {
3212 return state->status;
3215 *mxac = state->mxac;
3216 return NT_STATUS_OK;
3219 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3220 const char *fname,
3221 uint32_t *_mxac)
3223 TALLOC_CTX *frame = talloc_stackframe();
3224 struct tevent_context *ev = NULL;
3225 struct tevent_req *req = NULL;
3226 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3227 bool ok;
3229 if (smbXcli_conn_has_async_calls(cli->conn)) {
3231 * Can't use sync call while an async call is in flight
3233 status = NT_STATUS_INVALID_PARAMETER;
3234 goto fail;
3237 ev = samba_tevent_context_init(frame);
3238 if (ev == NULL) {
3239 goto fail;
3241 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3242 if (req == NULL) {
3243 goto fail;
3245 ok = tevent_req_poll_ntstatus(req, ev, &status);
3246 if (!ok) {
3247 goto fail;
3249 status = cli_smb2_query_mxac_recv(req, _mxac);
3251 fail:
3252 cli->raw_status = status;
3253 TALLOC_FREE(frame);
3254 return status;
3257 struct cli_smb2_rename_fnum_state {
3258 DATA_BLOB inbuf;
3261 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3263 static struct tevent_req *cli_smb2_rename_fnum_send(
3264 TALLOC_CTX *mem_ctx,
3265 struct tevent_context *ev,
3266 struct cli_state *cli,
3267 uint16_t fnum,
3268 const char *fname_dst,
3269 bool replace)
3271 struct tevent_req *req = NULL, *subreq = NULL;
3272 struct cli_smb2_rename_fnum_state *state = NULL;
3273 size_t namelen = strlen(fname_dst);
3274 smb_ucs2_t *converted_str = NULL;
3275 size_t converted_size_bytes = 0;
3276 size_t inbuf_size;
3277 bool ok;
3279 req = tevent_req_create(
3280 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3281 if (req == NULL) {
3282 return NULL;
3286 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3287 * a '\'
3289 if (*fname_dst == '\\') {
3290 fname_dst++;
3294 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3295 * '\'
3297 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3298 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3299 if (tevent_req_nomem(fname_dst, req)) {
3300 return tevent_req_post(req, ev);
3304 ok = push_ucs2_talloc(
3305 state, &converted_str, fname_dst, &converted_size_bytes);
3306 if (!ok) {
3307 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3308 return tevent_req_post(req, ev);
3312 * W2K8 insists the dest name is not null terminated. Remove
3313 * the last 2 zero bytes and reduce the name length.
3315 if (converted_size_bytes < 2) {
3316 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3317 return tevent_req_post(req, ev);
3319 converted_size_bytes -= 2;
3321 inbuf_size = 20 + converted_size_bytes;
3322 if (inbuf_size < 20) {
3323 /* Integer wrap check. */
3324 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3325 return tevent_req_post(req, ev);
3329 * The Windows 10 SMB2 server has a minimum length
3330 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3331 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3332 * if the length is less. This isn't an alignment
3333 * issue as Windows client happily 2-byte align
3334 * for larget target name sizes. Also the Windows 10
3335 * SMB1 server doesn't have this restriction.
3337 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3339 inbuf_size = MAX(inbuf_size, 24);
3341 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3342 if (tevent_req_nomem(state->inbuf.data, req)) {
3343 return tevent_req_post(req, ev);
3346 if (replace) {
3347 SCVAL(state->inbuf.data, 0, 1);
3350 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3351 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3353 TALLOC_FREE(converted_str);
3355 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3356 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3358 subreq = cli_smb2_set_info_fnum_send(
3359 state, /* mem_ctx */
3360 ev, /* ev */
3361 cli, /* cli */
3362 fnum, /* fnum */
3363 1, /* in_info_type */
3364 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3365 &state->inbuf, /* in_input_buffer */
3366 0); /* in_additional_info */
3367 if (tevent_req_nomem(subreq, req)) {
3368 return tevent_req_post(req, ev);
3370 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3371 return req;
3374 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3376 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3377 tevent_req_simple_finish_ntstatus(subreq, status);
3380 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3382 return tevent_req_simple_recv_ntstatus(req);
3385 /***************************************************************
3386 Wrapper that allows SMB2 to rename a file.
3387 ***************************************************************/
3389 struct cli_smb2_rename_state {
3390 struct tevent_context *ev;
3391 struct cli_state *cli;
3392 const char *fname_dst;
3393 bool replace;
3394 uint16_t fnum;
3396 NTSTATUS rename_status;
3399 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3400 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3401 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3403 struct tevent_req *cli_smb2_rename_send(
3404 TALLOC_CTX *mem_ctx,
3405 struct tevent_context *ev,
3406 struct cli_state *cli,
3407 const char *fname_src,
3408 const char *fname_dst,
3409 bool replace)
3411 struct tevent_req *req = NULL, *subreq = NULL;
3412 struct cli_smb2_rename_state *state = NULL;
3413 NTSTATUS status;
3415 req = tevent_req_create(
3416 mem_ctx, &state, struct cli_smb2_rename_state);
3417 if (req == NULL) {
3418 return NULL;
3422 * Strip a MSDFS path from fname_dst if we were given one.
3424 status = cli_dfs_target_check(state,
3425 cli,
3426 fname_dst,
3427 &fname_dst);
3428 if (tevent_req_nterror(req, status)) {
3429 return tevent_req_post(req, ev);
3432 state->ev = ev;
3433 state->cli = cli;
3434 state->fname_dst = fname_dst;
3435 state->replace = replace;
3437 subreq = get_fnum_from_path_send(
3438 state, ev, cli, fname_src, DELETE_ACCESS);
3439 if (tevent_req_nomem(subreq, req)) {
3440 return tevent_req_post(req, ev);
3442 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3443 return req;
3446 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3448 struct tevent_req *req = tevent_req_callback_data(
3449 subreq, struct tevent_req);
3450 struct cli_smb2_rename_state *state = tevent_req_data(
3451 req, struct cli_smb2_rename_state);
3452 NTSTATUS status;
3454 status = get_fnum_from_path_recv(subreq, &state->fnum);
3455 TALLOC_FREE(subreq);
3456 if (tevent_req_nterror(req, status)) {
3457 return;
3460 subreq = cli_smb2_rename_fnum_send(
3461 state,
3462 state->ev,
3463 state->cli,
3464 state->fnum,
3465 state->fname_dst,
3466 state->replace);
3467 if (tevent_req_nomem(subreq, req)) {
3468 return;
3470 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3473 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3475 struct tevent_req *req = tevent_req_callback_data(
3476 subreq, struct tevent_req);
3477 struct cli_smb2_rename_state *state = tevent_req_data(
3478 req, struct cli_smb2_rename_state);
3480 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3481 TALLOC_FREE(subreq);
3483 subreq = cli_smb2_close_fnum_send(
3484 state, state->ev, state->cli, state->fnum);
3485 if (tevent_req_nomem(subreq, req)) {
3486 return;
3488 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3491 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3493 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3494 tevent_req_simple_finish_ntstatus(subreq, status);
3497 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3499 struct cli_smb2_rename_state *state = tevent_req_data(
3500 req, struct cli_smb2_rename_state);
3501 NTSTATUS status = NT_STATUS_OK;
3503 if (!tevent_req_is_nterror(req, &status)) {
3504 status = state->rename_status;
3506 tevent_req_received(req);
3507 return status;
3510 /***************************************************************
3511 Wrapper that allows SMB2 to set an EA on a fnum.
3512 Synchronous only.
3513 ***************************************************************/
3515 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3516 uint16_t fnum,
3517 const char *ea_name,
3518 const char *ea_val,
3519 size_t ea_len)
3521 NTSTATUS status;
3522 DATA_BLOB inbuf = data_blob_null;
3523 size_t bloblen = 0;
3524 char *ea_name_ascii = NULL;
3525 size_t namelen = 0;
3526 TALLOC_CTX *frame = talloc_stackframe();
3528 if (smbXcli_conn_has_async_calls(cli->conn)) {
3530 * Can't use sync call while an async call is in flight
3532 status = NT_STATUS_INVALID_PARAMETER;
3533 goto fail;
3536 /* Marshall the SMB2 EA data. */
3537 if (ea_len > 0xFFFF) {
3538 status = NT_STATUS_INVALID_PARAMETER;
3539 goto fail;
3542 if (!push_ascii_talloc(frame,
3543 &ea_name_ascii,
3544 ea_name,
3545 &namelen)) {
3546 status = NT_STATUS_INVALID_PARAMETER;
3547 goto fail;
3550 if (namelen < 2 || namelen > 0xFF) {
3551 status = NT_STATUS_INVALID_PARAMETER;
3552 goto fail;
3555 bloblen = 8 + ea_len + namelen;
3556 /* Round up to a 4 byte boundary. */
3557 bloblen = ((bloblen + 3)&~3);
3559 inbuf = data_blob_talloc_zero(frame, bloblen);
3560 if (inbuf.data == NULL) {
3561 status = NT_STATUS_NO_MEMORY;
3562 goto fail;
3564 /* namelen doesn't include the NULL byte. */
3565 SCVAL(inbuf.data, 5, namelen - 1);
3566 SSVAL(inbuf.data, 6, ea_len);
3567 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3568 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3570 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3571 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3573 status = cli_smb2_set_info_fnum(
3574 cli,
3575 fnum,
3576 1, /* in_info_type */
3577 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3578 &inbuf, /* in_input_buffer */
3579 0); /* in_additional_info */
3581 fail:
3583 cli->raw_status = status;
3585 TALLOC_FREE(frame);
3586 return status;
3589 /***************************************************************
3590 Wrapper that allows SMB2 to set an EA on a pathname.
3591 Synchronous only.
3592 ***************************************************************/
3594 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3595 const char *name,
3596 const char *ea_name,
3597 const char *ea_val,
3598 size_t ea_len)
3600 NTSTATUS status;
3601 uint16_t fnum = 0xffff;
3603 if (smbXcli_conn_has_async_calls(cli->conn)) {
3605 * Can't use sync call while an async call is in flight
3607 status = NT_STATUS_INVALID_PARAMETER;
3608 goto fail;
3611 status = get_fnum_from_path(cli,
3612 name,
3613 FILE_WRITE_EA,
3614 &fnum);
3616 if (!NT_STATUS_IS_OK(status)) {
3617 goto fail;
3620 status = cli_set_ea_fnum(cli,
3621 fnum,
3622 ea_name,
3623 ea_val,
3624 ea_len);
3625 if (!NT_STATUS_IS_OK(status)) {
3626 goto fail;
3629 fail:
3631 if (fnum != 0xffff) {
3632 cli_smb2_close_fnum(cli, fnum);
3635 cli->raw_status = status;
3637 return status;
3640 /***************************************************************
3641 Wrapper that allows SMB2 to get an EA list on a pathname.
3642 Synchronous only.
3643 ***************************************************************/
3645 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3646 const char *name,
3647 TALLOC_CTX *ctx,
3648 size_t *pnum_eas,
3649 struct ea_struct **pea_array)
3651 NTSTATUS status;
3652 uint16_t fnum = 0xffff;
3653 DATA_BLOB outbuf = data_blob_null;
3654 struct ea_list *ea_list = NULL;
3655 struct ea_list *eal = NULL;
3656 size_t ea_count = 0;
3657 TALLOC_CTX *frame = talloc_stackframe();
3659 *pnum_eas = 0;
3660 *pea_array = NULL;
3662 if (smbXcli_conn_has_async_calls(cli->conn)) {
3664 * Can't use sync call while an async call is in flight
3666 status = NT_STATUS_INVALID_PARAMETER;
3667 goto fail;
3670 status = get_fnum_from_path(cli,
3671 name,
3672 FILE_READ_EA,
3673 &fnum);
3675 if (!NT_STATUS_IS_OK(status)) {
3676 goto fail;
3679 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3680 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3682 status = cli_smb2_query_info_fnum(
3683 cli,
3684 fnum,
3685 1, /* in_info_type */
3686 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3687 0xFFFF, /* in_max_output_length */
3688 NULL, /* in_input_buffer */
3689 0, /* in_additional_info */
3690 0, /* in_flags */
3691 frame,
3692 &outbuf);
3694 if (!NT_STATUS_IS_OK(status)) {
3695 goto fail;
3698 /* Parse the reply. */
3699 ea_list = read_nttrans_ea_list(ctx,
3700 (const char *)outbuf.data,
3701 outbuf.length);
3702 if (ea_list == NULL) {
3703 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3704 goto fail;
3707 /* Convert to an array. */
3708 for (eal = ea_list; eal; eal = eal->next) {
3709 ea_count++;
3712 if (ea_count) {
3713 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3714 if (*pea_array == NULL) {
3715 status = NT_STATUS_NO_MEMORY;
3716 goto fail;
3718 ea_count = 0;
3719 for (eal = ea_list; eal; eal = eal->next) {
3720 (*pea_array)[ea_count++] = eal->ea;
3722 *pnum_eas = ea_count;
3725 fail:
3727 if (fnum != 0xffff) {
3728 cli_smb2_close_fnum(cli, fnum);
3731 cli->raw_status = status;
3733 TALLOC_FREE(frame);
3734 return status;
3737 /***************************************************************
3738 Wrapper that allows SMB2 to get user quota.
3739 Synchronous only.
3740 ***************************************************************/
3742 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3743 int quota_fnum,
3744 SMB_NTQUOTA_STRUCT *pqt)
3746 NTSTATUS status;
3747 DATA_BLOB inbuf = data_blob_null;
3748 DATA_BLOB info_blob = data_blob_null;
3749 DATA_BLOB outbuf = data_blob_null;
3750 TALLOC_CTX *frame = talloc_stackframe();
3751 unsigned sid_len;
3752 unsigned int offset;
3753 struct smb2_query_quota_info query = {0};
3754 struct file_get_quota_info info = {0};
3755 enum ndr_err_code err;
3756 struct ndr_push *ndr_push = NULL;
3758 if (smbXcli_conn_has_async_calls(cli->conn)) {
3760 * Can't use sync call while an async call is in flight
3762 status = NT_STATUS_INVALID_PARAMETER;
3763 goto fail;
3766 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3768 query.return_single = 1;
3770 info.next_entry_offset = 0;
3771 info.sid_length = sid_len;
3772 info.sid = pqt->sid;
3774 err = ndr_push_struct_blob(
3775 &info_blob,
3776 frame,
3777 &info,
3778 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3780 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3781 status = NT_STATUS_INTERNAL_ERROR;
3782 goto fail;
3785 query.sid_list_length = info_blob.length;
3786 ndr_push = ndr_push_init_ctx(frame);
3787 if (!ndr_push) {
3788 status = NT_STATUS_NO_MEMORY;
3789 goto fail;
3792 err = ndr_push_smb2_query_quota_info(ndr_push,
3793 NDR_SCALARS | NDR_BUFFERS,
3794 &query);
3796 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3797 status = NT_STATUS_INTERNAL_ERROR;
3798 goto fail;
3801 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3802 info_blob.length);
3804 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3805 status = NT_STATUS_INTERNAL_ERROR;
3806 goto fail;
3808 inbuf.data = ndr_push->data;
3809 inbuf.length = ndr_push->offset;
3811 status = cli_smb2_query_info_fnum(
3812 cli,
3813 quota_fnum,
3814 4, /* in_info_type */
3815 0, /* in_file_info_class */
3816 0xFFFF, /* in_max_output_length */
3817 &inbuf, /* in_input_buffer */
3818 0, /* in_additional_info */
3819 0, /* in_flags */
3820 frame,
3821 &outbuf);
3823 if (!NT_STATUS_IS_OK(status)) {
3824 goto fail;
3827 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3828 pqt)) {
3829 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3830 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3833 fail:
3834 cli->raw_status = status;
3836 TALLOC_FREE(frame);
3837 return status;
3840 /***************************************************************
3841 Wrapper that allows SMB2 to list user quota.
3842 Synchronous only.
3843 ***************************************************************/
3845 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3846 TALLOC_CTX *mem_ctx,
3847 int quota_fnum,
3848 SMB_NTQUOTA_LIST **pqt_list,
3849 bool first)
3851 NTSTATUS status;
3852 DATA_BLOB inbuf = data_blob_null;
3853 DATA_BLOB outbuf = data_blob_null;
3854 TALLOC_CTX *frame = talloc_stackframe();
3855 struct smb2_query_quota_info info = {0};
3856 enum ndr_err_code err;
3858 if (smbXcli_conn_has_async_calls(cli->conn)) {
3860 * Can't use sync call while an async call is in flight
3862 status = NT_STATUS_INVALID_PARAMETER;
3863 goto cleanup;
3866 info.restart_scan = first ? 1 : 0;
3868 err = ndr_push_struct_blob(
3869 &inbuf,
3870 frame,
3871 &info,
3872 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3874 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3875 status = NT_STATUS_INTERNAL_ERROR;
3876 goto cleanup;
3879 status = cli_smb2_query_info_fnum(
3880 cli,
3881 quota_fnum,
3882 4, /* in_info_type */
3883 0, /* in_file_info_class */
3884 0xFFFF, /* in_max_output_length */
3885 &inbuf, /* in_input_buffer */
3886 0, /* in_additional_info */
3887 0, /* in_flags */
3888 frame,
3889 &outbuf);
3892 * safeguard against panic from calling parse_user_quota_list with
3893 * NULL buffer
3895 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3896 status = NT_STATUS_NO_MORE_ENTRIES;
3899 if (!NT_STATUS_IS_OK(status)) {
3900 goto cleanup;
3903 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3904 pqt_list);
3906 cleanup:
3907 cli->raw_status = status;
3909 TALLOC_FREE(frame);
3910 return status;
3913 /***************************************************************
3914 Wrapper that allows SMB2 to get file system quota.
3915 Synchronous only.
3916 ***************************************************************/
3918 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3919 int quota_fnum,
3920 SMB_NTQUOTA_STRUCT *pqt)
3922 NTSTATUS status;
3923 DATA_BLOB outbuf = data_blob_null;
3924 TALLOC_CTX *frame = talloc_stackframe();
3926 if (smbXcli_conn_has_async_calls(cli->conn)) {
3928 * Can't use sync call while an async call is in flight
3930 status = NT_STATUS_INVALID_PARAMETER;
3931 goto cleanup;
3934 status = cli_smb2_query_info_fnum(
3935 cli,
3936 quota_fnum,
3937 2, /* in_info_type */
3938 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3939 0xFFFF, /* in_max_output_length */
3940 NULL, /* in_input_buffer */
3941 0, /* in_additional_info */
3942 0, /* in_flags */
3943 frame,
3944 &outbuf);
3946 if (!NT_STATUS_IS_OK(status)) {
3947 goto cleanup;
3950 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3952 cleanup:
3953 cli->raw_status = status;
3955 TALLOC_FREE(frame);
3956 return status;
3959 /***************************************************************
3960 Wrapper that allows SMB2 to set user quota.
3961 Synchronous only.
3962 ***************************************************************/
3964 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3965 int quota_fnum,
3966 SMB_NTQUOTA_LIST *qtl)
3968 NTSTATUS status;
3969 DATA_BLOB inbuf = data_blob_null;
3970 TALLOC_CTX *frame = talloc_stackframe();
3972 if (smbXcli_conn_has_async_calls(cli->conn)) {
3974 * Can't use sync call while an async call is in flight
3976 status = NT_STATUS_INVALID_PARAMETER;
3977 goto cleanup;
3980 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3981 if (!NT_STATUS_IS_OK(status)) {
3982 goto cleanup;
3985 status = cli_smb2_set_info_fnum(
3986 cli,
3987 quota_fnum,
3988 4, /* in_info_type */
3989 0, /* in_file_info_class */
3990 &inbuf, /* in_input_buffer */
3991 0); /* in_additional_info */
3992 cleanup:
3994 cli->raw_status = status;
3996 TALLOC_FREE(frame);
3998 return status;
4001 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
4002 int quota_fnum,
4003 SMB_NTQUOTA_STRUCT *pqt)
4005 NTSTATUS status;
4006 DATA_BLOB inbuf = data_blob_null;
4007 TALLOC_CTX *frame = talloc_stackframe();
4009 if (smbXcli_conn_has_async_calls(cli->conn)) {
4011 * Can't use sync call while an async call is in flight
4013 status = NT_STATUS_INVALID_PARAMETER;
4014 goto cleanup;
4017 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
4018 if (!NT_STATUS_IS_OK(status)) {
4019 goto cleanup;
4022 status = cli_smb2_set_info_fnum(
4023 cli,
4024 quota_fnum,
4025 2, /* in_info_type */
4026 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
4027 &inbuf, /* in_input_buffer */
4028 0); /* in_additional_info */
4029 cleanup:
4030 cli->raw_status = status;
4032 TALLOC_FREE(frame);
4033 return status;
4036 struct cli_smb2_read_state {
4037 struct tevent_context *ev;
4038 struct cli_state *cli;
4039 struct smb2_hnd *ph;
4040 uint64_t start_offset;
4041 uint32_t size;
4042 uint32_t received;
4043 uint8_t *buf;
4046 static void cli_smb2_read_done(struct tevent_req *subreq);
4048 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
4049 struct tevent_context *ev,
4050 struct cli_state *cli,
4051 uint16_t fnum,
4052 off_t offset,
4053 size_t size)
4055 NTSTATUS status;
4056 struct tevent_req *req, *subreq;
4057 struct cli_smb2_read_state *state;
4059 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
4060 if (req == NULL) {
4061 return NULL;
4063 state->ev = ev;
4064 state->cli = cli;
4065 state->start_offset = (uint64_t)offset;
4066 state->size = (uint32_t)size;
4067 state->received = 0;
4068 state->buf = NULL;
4070 status = map_fnum_to_smb2_handle(cli,
4071 fnum,
4072 &state->ph);
4073 if (tevent_req_nterror(req, status)) {
4074 return tevent_req_post(req, ev);
4077 subreq = smb2cli_read_send(state,
4078 state->ev,
4079 state->cli->conn,
4080 state->cli->timeout,
4081 state->cli->smb2.session,
4082 state->cli->smb2.tcon,
4083 state->size,
4084 state->start_offset,
4085 state->ph->fid_persistent,
4086 state->ph->fid_volatile,
4087 0, /* minimum_count */
4088 0); /* remaining_bytes */
4090 if (tevent_req_nomem(subreq, req)) {
4091 return tevent_req_post(req, ev);
4093 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
4094 return req;
4097 static void cli_smb2_read_done(struct tevent_req *subreq)
4099 struct tevent_req *req = tevent_req_callback_data(
4100 subreq, struct tevent_req);
4101 struct cli_smb2_read_state *state = tevent_req_data(
4102 req, struct cli_smb2_read_state);
4103 NTSTATUS status;
4105 status = smb2cli_read_recv(subreq, state,
4106 &state->buf, &state->received);
4107 if (tevent_req_nterror(req, status)) {
4108 return;
4111 if (state->received > state->size) {
4112 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4113 return;
4116 tevent_req_done(req);
4119 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
4120 ssize_t *received,
4121 uint8_t **rcvbuf)
4123 NTSTATUS status;
4124 struct cli_smb2_read_state *state = tevent_req_data(
4125 req, struct cli_smb2_read_state);
4127 if (tevent_req_is_nterror(req, &status)) {
4128 state->cli->raw_status = status;
4129 return status;
4132 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
4133 * better make sure that you copy it away before you talloc_free(req).
4134 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
4136 *received = (ssize_t)state->received;
4137 *rcvbuf = state->buf;
4138 state->cli->raw_status = NT_STATUS_OK;
4139 return NT_STATUS_OK;
4142 struct cli_smb2_write_state {
4143 struct tevent_context *ev;
4144 struct cli_state *cli;
4145 struct smb2_hnd *ph;
4146 uint32_t flags;
4147 const uint8_t *buf;
4148 uint64_t offset;
4149 uint32_t size;
4150 uint32_t written;
4153 static void cli_smb2_write_written(struct tevent_req *req);
4155 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
4156 struct tevent_context *ev,
4157 struct cli_state *cli,
4158 uint16_t fnum,
4159 uint16_t mode,
4160 const uint8_t *buf,
4161 off_t offset,
4162 size_t size)
4164 NTSTATUS status;
4165 struct tevent_req *req, *subreq = NULL;
4166 struct cli_smb2_write_state *state = NULL;
4168 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
4169 if (req == NULL) {
4170 return NULL;
4172 state->ev = ev;
4173 state->cli = cli;
4174 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4175 state->flags = (uint32_t)mode;
4176 state->buf = buf;
4177 state->offset = (uint64_t)offset;
4178 state->size = (uint32_t)size;
4179 state->written = 0;
4181 status = map_fnum_to_smb2_handle(cli,
4182 fnum,
4183 &state->ph);
4184 if (tevent_req_nterror(req, status)) {
4185 return tevent_req_post(req, ev);
4188 subreq = smb2cli_write_send(state,
4189 state->ev,
4190 state->cli->conn,
4191 state->cli->timeout,
4192 state->cli->smb2.session,
4193 state->cli->smb2.tcon,
4194 state->size,
4195 state->offset,
4196 state->ph->fid_persistent,
4197 state->ph->fid_volatile,
4198 0, /* remaining_bytes */
4199 state->flags, /* flags */
4200 state->buf);
4202 if (tevent_req_nomem(subreq, req)) {
4203 return tevent_req_post(req, ev);
4205 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4206 return req;
4209 static void cli_smb2_write_written(struct tevent_req *subreq)
4211 struct tevent_req *req = tevent_req_callback_data(
4212 subreq, struct tevent_req);
4213 struct cli_smb2_write_state *state = tevent_req_data(
4214 req, struct cli_smb2_write_state);
4215 NTSTATUS status;
4216 uint32_t written;
4218 status = smb2cli_write_recv(subreq, &written);
4219 TALLOC_FREE(subreq);
4220 if (tevent_req_nterror(req, status)) {
4221 return;
4224 state->written = written;
4226 tevent_req_done(req);
4229 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4230 size_t *pwritten)
4232 struct cli_smb2_write_state *state = tevent_req_data(
4233 req, struct cli_smb2_write_state);
4234 NTSTATUS status;
4236 if (tevent_req_is_nterror(req, &status)) {
4237 state->cli->raw_status = status;
4238 tevent_req_received(req);
4239 return status;
4242 if (pwritten != NULL) {
4243 *pwritten = (size_t)state->written;
4245 state->cli->raw_status = NT_STATUS_OK;
4246 tevent_req_received(req);
4247 return NT_STATUS_OK;
4250 /***************************************************************
4251 Wrapper that allows SMB2 async write using an fnum.
4252 This is mostly cut-and-paste from Volker's code inside
4253 source3/libsmb/clireadwrite.c, adapted for SMB2.
4255 Done this way so I can reuse all the logic inside cli_push()
4256 for free :-).
4257 ***************************************************************/
4259 struct cli_smb2_writeall_state {
4260 struct tevent_context *ev;
4261 struct cli_state *cli;
4262 struct smb2_hnd *ph;
4263 uint32_t flags;
4264 const uint8_t *buf;
4265 uint64_t offset;
4266 uint32_t size;
4267 uint32_t written;
4270 static void cli_smb2_writeall_written(struct tevent_req *req);
4272 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4273 struct tevent_context *ev,
4274 struct cli_state *cli,
4275 uint16_t fnum,
4276 uint16_t mode,
4277 const uint8_t *buf,
4278 off_t offset,
4279 size_t size)
4281 NTSTATUS status;
4282 struct tevent_req *req, *subreq = NULL;
4283 struct cli_smb2_writeall_state *state = NULL;
4284 uint32_t to_write;
4285 uint32_t max_size;
4286 bool ok;
4288 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4289 if (req == NULL) {
4290 return NULL;
4292 state->ev = ev;
4293 state->cli = cli;
4294 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4295 state->flags = (uint32_t)mode;
4296 state->buf = buf;
4297 state->offset = (uint64_t)offset;
4298 state->size = (uint32_t)size;
4299 state->written = 0;
4301 status = map_fnum_to_smb2_handle(cli,
4302 fnum,
4303 &state->ph);
4304 if (tevent_req_nterror(req, status)) {
4305 return tevent_req_post(req, ev);
4308 to_write = state->size;
4309 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4310 to_write = MIN(max_size, to_write);
4311 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4312 if (ok) {
4313 to_write = MIN(max_size, to_write);
4316 subreq = smb2cli_write_send(state,
4317 state->ev,
4318 state->cli->conn,
4319 state->cli->timeout,
4320 state->cli->smb2.session,
4321 state->cli->smb2.tcon,
4322 to_write,
4323 state->offset,
4324 state->ph->fid_persistent,
4325 state->ph->fid_volatile,
4326 0, /* remaining_bytes */
4327 state->flags, /* flags */
4328 state->buf + state->written);
4330 if (tevent_req_nomem(subreq, req)) {
4331 return tevent_req_post(req, ev);
4333 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4334 return req;
4337 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4339 struct tevent_req *req = tevent_req_callback_data(
4340 subreq, struct tevent_req);
4341 struct cli_smb2_writeall_state *state = tevent_req_data(
4342 req, struct cli_smb2_writeall_state);
4343 NTSTATUS status;
4344 uint32_t written, to_write;
4345 uint32_t max_size;
4346 bool ok;
4348 status = smb2cli_write_recv(subreq, &written);
4349 TALLOC_FREE(subreq);
4350 if (tevent_req_nterror(req, status)) {
4351 return;
4354 state->written += written;
4356 if (state->written > state->size) {
4357 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4358 return;
4361 to_write = state->size - state->written;
4363 if (to_write == 0) {
4364 tevent_req_done(req);
4365 return;
4368 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4369 to_write = MIN(max_size, to_write);
4370 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4371 if (ok) {
4372 to_write = MIN(max_size, to_write);
4375 subreq = smb2cli_write_send(state,
4376 state->ev,
4377 state->cli->conn,
4378 state->cli->timeout,
4379 state->cli->smb2.session,
4380 state->cli->smb2.tcon,
4381 to_write,
4382 state->offset + state->written,
4383 state->ph->fid_persistent,
4384 state->ph->fid_volatile,
4385 0, /* remaining_bytes */
4386 state->flags, /* flags */
4387 state->buf + state->written);
4389 if (tevent_req_nomem(subreq, req)) {
4390 return;
4392 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4395 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4396 size_t *pwritten)
4398 struct cli_smb2_writeall_state *state = tevent_req_data(
4399 req, struct cli_smb2_writeall_state);
4400 NTSTATUS status;
4402 if (tevent_req_is_nterror(req, &status)) {
4403 state->cli->raw_status = status;
4404 return status;
4406 if (pwritten != NULL) {
4407 *pwritten = (size_t)state->written;
4409 state->cli->raw_status = NT_STATUS_OK;
4410 return NT_STATUS_OK;
4413 struct cli_smb2_splice_state {
4414 struct tevent_context *ev;
4415 struct cli_state *cli;
4416 struct smb2_hnd *src_ph;
4417 struct smb2_hnd *dst_ph;
4418 int (*splice_cb)(off_t n, void *priv);
4419 void *priv;
4420 off_t written;
4421 off_t size;
4422 off_t src_offset;
4423 off_t dst_offset;
4424 bool resized;
4425 struct req_resume_key_rsp resume_rsp;
4426 struct srv_copychunk_copy cc_copy;
4429 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4430 struct tevent_req *req);
4432 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4434 struct tevent_req *req = tevent_req_callback_data(
4435 subreq, struct tevent_req);
4436 struct cli_smb2_splice_state *state =
4437 tevent_req_data(req,
4438 struct cli_smb2_splice_state);
4439 struct smbXcli_conn *conn = state->cli->conn;
4440 DATA_BLOB out_input_buffer = data_blob_null;
4441 DATA_BLOB out_output_buffer = data_blob_null;
4442 struct srv_copychunk_rsp cc_copy_rsp;
4443 enum ndr_err_code ndr_ret;
4444 NTSTATUS status;
4446 status = smb2cli_ioctl_recv(subreq, state,
4447 &out_input_buffer,
4448 &out_output_buffer);
4449 TALLOC_FREE(subreq);
4450 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4451 state->resized) && tevent_req_nterror(req, status)) {
4452 return;
4455 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4456 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4457 if (ndr_ret != NDR_ERR_SUCCESS) {
4458 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4459 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4460 return;
4463 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4464 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4465 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4466 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4467 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4468 tevent_req_nterror(req, status)) {
4469 return;
4472 state->resized = true;
4473 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4474 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4475 } else {
4476 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4477 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4478 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4479 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4480 return;
4482 state->src_offset += cc_copy_rsp.total_bytes_written;
4483 state->dst_offset += cc_copy_rsp.total_bytes_written;
4484 state->written += cc_copy_rsp.total_bytes_written;
4485 if (!state->splice_cb(state->written, state->priv)) {
4486 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4487 return;
4491 cli_splice_copychunk_send(state, req);
4494 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4495 struct tevent_req *req)
4497 struct tevent_req *subreq;
4498 enum ndr_err_code ndr_ret;
4499 struct smbXcli_conn *conn = state->cli->conn;
4500 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4501 off_t src_offset = state->src_offset;
4502 off_t dst_offset = state->dst_offset;
4503 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4504 state->size - state->written);
4505 DATA_BLOB in_input_buffer = data_blob_null;
4506 DATA_BLOB in_output_buffer = data_blob_null;
4508 if (state->size - state->written == 0) {
4509 tevent_req_done(req);
4510 return;
4513 cc_copy->chunk_count = 0;
4514 while (req_len) {
4515 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4516 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4517 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4518 smb2cli_conn_cc_chunk_len(conn));
4519 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4520 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4521 return;
4523 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4524 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4525 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4526 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4527 return;
4529 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4530 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4531 cc_copy->chunk_count++;
4534 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4535 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4536 if (ndr_ret != NDR_ERR_SUCCESS) {
4537 DEBUG(0, ("failed to marshall copy chunk req\n"));
4538 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4539 return;
4542 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4543 state->cli->timeout,
4544 state->cli->smb2.session,
4545 state->cli->smb2.tcon,
4546 state->dst_ph->fid_persistent, /* in_fid_persistent */
4547 state->dst_ph->fid_volatile, /* in_fid_volatile */
4548 FSCTL_SRV_COPYCHUNK_WRITE,
4549 0, /* in_max_input_length */
4550 &in_input_buffer,
4551 12, /* in_max_output_length */
4552 &in_output_buffer,
4553 SMB2_IOCTL_FLAG_IS_FSCTL);
4554 if (tevent_req_nomem(subreq, req)) {
4555 return;
4557 tevent_req_set_callback(subreq,
4558 cli_splice_copychunk_done,
4559 req);
4562 static void cli_splice_key_done(struct tevent_req *subreq)
4564 struct tevent_req *req = tevent_req_callback_data(
4565 subreq, struct tevent_req);
4566 struct cli_smb2_splice_state *state =
4567 tevent_req_data(req,
4568 struct cli_smb2_splice_state);
4569 enum ndr_err_code ndr_ret;
4570 NTSTATUS status;
4572 DATA_BLOB out_input_buffer = data_blob_null;
4573 DATA_BLOB out_output_buffer = data_blob_null;
4575 status = smb2cli_ioctl_recv(subreq, state,
4576 &out_input_buffer,
4577 &out_output_buffer);
4578 TALLOC_FREE(subreq);
4579 if (tevent_req_nterror(req, status)) {
4580 return;
4583 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4584 state, &state->resume_rsp,
4585 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4586 if (ndr_ret != NDR_ERR_SUCCESS) {
4587 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4588 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4589 return;
4592 memcpy(&state->cc_copy.source_key,
4593 &state->resume_rsp.resume_key,
4594 sizeof state->resume_rsp.resume_key);
4596 cli_splice_copychunk_send(state, req);
4599 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4600 struct tevent_context *ev,
4601 struct cli_state *cli,
4602 uint16_t src_fnum, uint16_t dst_fnum,
4603 off_t size, off_t src_offset, off_t dst_offset,
4604 int (*splice_cb)(off_t n, void *priv),
4605 void *priv)
4607 struct tevent_req *req;
4608 struct tevent_req *subreq;
4609 struct cli_smb2_splice_state *state;
4610 NTSTATUS status;
4611 DATA_BLOB in_input_buffer = data_blob_null;
4612 DATA_BLOB in_output_buffer = data_blob_null;
4614 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4615 if (req == NULL) {
4616 return NULL;
4618 state->cli = cli;
4619 state->ev = ev;
4620 state->splice_cb = splice_cb;
4621 state->priv = priv;
4622 state->size = size;
4623 state->written = 0;
4624 state->src_offset = src_offset;
4625 state->dst_offset = dst_offset;
4626 state->cc_copy.chunks = talloc_array(state,
4627 struct srv_copychunk,
4628 smb2cli_conn_cc_max_chunks(cli->conn));
4629 if (state->cc_copy.chunks == NULL) {
4630 return NULL;
4633 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4634 if (tevent_req_nterror(req, status))
4635 return tevent_req_post(req, ev);
4637 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4638 if (tevent_req_nterror(req, status))
4639 return tevent_req_post(req, ev);
4641 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4642 cli->timeout,
4643 cli->smb2.session,
4644 cli->smb2.tcon,
4645 state->src_ph->fid_persistent, /* in_fid_persistent */
4646 state->src_ph->fid_volatile, /* in_fid_volatile */
4647 FSCTL_SRV_REQUEST_RESUME_KEY,
4648 0, /* in_max_input_length */
4649 &in_input_buffer,
4650 32, /* in_max_output_length */
4651 &in_output_buffer,
4652 SMB2_IOCTL_FLAG_IS_FSCTL);
4653 if (tevent_req_nomem(subreq, req)) {
4654 return NULL;
4656 tevent_req_set_callback(subreq,
4657 cli_splice_key_done,
4658 req);
4660 return req;
4663 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4665 struct cli_smb2_splice_state *state = tevent_req_data(
4666 req, struct cli_smb2_splice_state);
4667 NTSTATUS status;
4669 if (tevent_req_is_nterror(req, &status)) {
4670 state->cli->raw_status = status;
4671 tevent_req_received(req);
4672 return status;
4674 if (written != NULL) {
4675 *written = state->written;
4677 state->cli->raw_status = NT_STATUS_OK;
4678 tevent_req_received(req);
4679 return NT_STATUS_OK;
4682 /***************************************************************
4683 SMB2 enum shadow copy data.
4684 ***************************************************************/
4686 struct cli_smb2_shadow_copy_data_fnum_state {
4687 struct cli_state *cli;
4688 uint16_t fnum;
4689 struct smb2_hnd *ph;
4690 DATA_BLOB out_input_buffer;
4691 DATA_BLOB out_output_buffer;
4694 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4696 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4697 TALLOC_CTX *mem_ctx,
4698 struct tevent_context *ev,
4699 struct cli_state *cli,
4700 uint16_t fnum,
4701 bool get_names)
4703 struct tevent_req *req, *subreq;
4704 struct cli_smb2_shadow_copy_data_fnum_state *state;
4705 NTSTATUS status;
4707 req = tevent_req_create(mem_ctx, &state,
4708 struct cli_smb2_shadow_copy_data_fnum_state);
4709 if (req == NULL) {
4710 return NULL;
4713 state->cli = cli;
4714 state->fnum = fnum;
4716 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4717 if (tevent_req_nterror(req, status)) {
4718 return tevent_req_post(req, ev);
4722 * TODO. Under SMB2 we should send a zero max_output_length
4723 * ioctl to get the required size, then send another ioctl
4724 * to get the data, but the current SMB1 implementation just
4725 * does one roundtrip with a 64K buffer size. Do the same
4726 * for now. JRA.
4729 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4730 state->cli->timeout,
4731 state->cli->smb2.session,
4732 state->cli->smb2.tcon,
4733 state->ph->fid_persistent, /* in_fid_persistent */
4734 state->ph->fid_volatile, /* in_fid_volatile */
4735 FSCTL_GET_SHADOW_COPY_DATA,
4736 0, /* in_max_input_length */
4737 NULL, /* in_input_buffer */
4738 get_names ?
4739 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4740 NULL, /* in_output_buffer */
4741 SMB2_IOCTL_FLAG_IS_FSCTL);
4743 if (tevent_req_nomem(subreq, req)) {
4744 return tevent_req_post(req, ev);
4746 tevent_req_set_callback(subreq,
4747 cli_smb2_shadow_copy_data_fnum_done,
4748 req);
4750 return req;
4753 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4755 struct tevent_req *req = tevent_req_callback_data(
4756 subreq, struct tevent_req);
4757 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4758 req, struct cli_smb2_shadow_copy_data_fnum_state);
4759 NTSTATUS status;
4761 status = smb2cli_ioctl_recv(subreq, state,
4762 &state->out_input_buffer,
4763 &state->out_output_buffer);
4764 tevent_req_simple_finish_ntstatus(subreq, status);
4767 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4768 TALLOC_CTX *mem_ctx,
4769 bool get_names,
4770 char ***pnames,
4771 int *pnum_names)
4773 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4774 req, struct cli_smb2_shadow_copy_data_fnum_state);
4775 char **names = NULL;
4776 uint32_t num_names = 0;
4777 uint32_t num_names_returned = 0;
4778 uint32_t dlength = 0;
4779 uint32_t i;
4780 uint8_t *endp = NULL;
4781 NTSTATUS status;
4783 if (tevent_req_is_nterror(req, &status)) {
4784 return status;
4787 if (state->out_output_buffer.length < 16) {
4788 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4791 num_names = IVAL(state->out_output_buffer.data, 0);
4792 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4793 dlength = IVAL(state->out_output_buffer.data, 8);
4795 if (num_names > 0x7FFFFFFF) {
4796 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4799 if (get_names == false) {
4800 *pnum_names = (int)num_names;
4801 return NT_STATUS_OK;
4803 if (num_names != num_names_returned) {
4804 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4806 if (dlength + 12 < 12) {
4807 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4810 * NB. The below is an allowable return if there are
4811 * more snapshots than the buffer size we told the
4812 * server we can receive. We currently don't support
4813 * this.
4815 if (dlength + 12 > state->out_output_buffer.length) {
4816 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4818 if (state->out_output_buffer.length +
4819 (2 * sizeof(SHADOW_COPY_LABEL)) <
4820 state->out_output_buffer.length) {
4821 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4824 names = talloc_array(mem_ctx, char *, num_names_returned);
4825 if (names == NULL) {
4826 return NT_STATUS_NO_MEMORY;
4829 endp = state->out_output_buffer.data +
4830 state->out_output_buffer.length;
4832 for (i=0; i<num_names_returned; i++) {
4833 bool ret;
4834 uint8_t *src;
4835 size_t converted_size;
4837 src = state->out_output_buffer.data + 12 +
4838 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4840 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4841 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4843 ret = convert_string_talloc(
4844 names, CH_UTF16LE, CH_UNIX,
4845 src, 2 * sizeof(SHADOW_COPY_LABEL),
4846 &names[i], &converted_size);
4847 if (!ret) {
4848 TALLOC_FREE(names);
4849 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4852 *pnum_names = num_names;
4853 *pnames = names;
4854 return NT_STATUS_OK;
4857 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4858 struct cli_state *cli,
4859 uint16_t fnum,
4860 bool get_names,
4861 char ***pnames,
4862 int *pnum_names)
4864 TALLOC_CTX *frame = talloc_stackframe();
4865 struct tevent_context *ev;
4866 struct tevent_req *req;
4867 NTSTATUS status = NT_STATUS_NO_MEMORY;
4869 if (smbXcli_conn_has_async_calls(cli->conn)) {
4871 * Can't use sync call while an async call is in flight
4873 status = NT_STATUS_INVALID_PARAMETER;
4874 goto fail;
4876 ev = samba_tevent_context_init(frame);
4877 if (ev == NULL) {
4878 goto fail;
4880 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4882 cli,
4883 fnum,
4884 get_names);
4885 if (req == NULL) {
4886 goto fail;
4888 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4889 goto fail;
4891 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4892 mem_ctx,
4893 get_names,
4894 pnames,
4895 pnum_names);
4896 fail:
4897 cli->raw_status = status;
4899 TALLOC_FREE(frame);
4900 return status;
4903 /***************************************************************
4904 Wrapper that allows SMB2 to truncate a file.
4905 Synchronous only.
4906 ***************************************************************/
4908 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4909 uint16_t fnum,
4910 uint64_t newsize)
4912 NTSTATUS status;
4913 uint8_t buf[8] = {0};
4914 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4915 TALLOC_CTX *frame = talloc_stackframe();
4917 if (smbXcli_conn_has_async_calls(cli->conn)) {
4919 * Can't use sync call while an async call is in flight
4921 status = NT_STATUS_INVALID_PARAMETER;
4922 goto fail;
4925 SBVAL(buf, 0, newsize);
4927 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4928 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4930 status = cli_smb2_set_info_fnum(
4931 cli,
4932 fnum,
4933 1, /* in_info_type */
4934 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4935 &inbuf, /* in_input_buffer */
4938 fail:
4940 cli->raw_status = status;
4942 TALLOC_FREE(frame);
4943 return status;
4946 struct cli_smb2_notify_state {
4947 struct tevent_req *subreq;
4948 struct notify_change *changes;
4949 size_t num_changes;
4952 static void cli_smb2_notify_done(struct tevent_req *subreq);
4953 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4955 struct tevent_req *cli_smb2_notify_send(
4956 TALLOC_CTX *mem_ctx,
4957 struct tevent_context *ev,
4958 struct cli_state *cli,
4959 uint16_t fnum,
4960 uint32_t buffer_size,
4961 uint32_t completion_filter,
4962 bool recursive)
4964 struct tevent_req *req = NULL;
4965 struct cli_smb2_notify_state *state = NULL;
4966 struct smb2_hnd *ph = NULL;
4967 NTSTATUS status;
4969 req = tevent_req_create(mem_ctx, &state,
4970 struct cli_smb2_notify_state);
4971 if (req == NULL) {
4972 return NULL;
4975 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4976 if (tevent_req_nterror(req, status)) {
4977 return tevent_req_post(req, ev);
4980 state->subreq = smb2cli_notify_send(
4981 state,
4983 cli->conn,
4984 cli->timeout,
4985 cli->smb2.session,
4986 cli->smb2.tcon,
4987 buffer_size,
4988 ph->fid_persistent,
4989 ph->fid_volatile,
4990 completion_filter,
4991 recursive);
4992 if (tevent_req_nomem(state->subreq, req)) {
4993 return tevent_req_post(req, ev);
4995 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4996 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4997 return req;
5000 static bool cli_smb2_notify_cancel(struct tevent_req *req)
5002 struct cli_smb2_notify_state *state = tevent_req_data(
5003 req, struct cli_smb2_notify_state);
5004 bool ok;
5006 ok = tevent_req_cancel(state->subreq);
5007 return ok;
5010 static void cli_smb2_notify_done(struct tevent_req *subreq)
5012 struct tevent_req *req = tevent_req_callback_data(
5013 subreq, struct tevent_req);
5014 struct cli_smb2_notify_state *state = tevent_req_data(
5015 req, struct cli_smb2_notify_state);
5016 uint8_t *base;
5017 uint32_t len;
5018 uint32_t ofs;
5019 NTSTATUS status;
5021 status = smb2cli_notify_recv(subreq, state, &base, &len);
5022 TALLOC_FREE(subreq);
5024 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
5025 tevent_req_done(req);
5026 return;
5028 if (tevent_req_nterror(req, status)) {
5029 return;
5032 ofs = 0;
5034 while (len - ofs >= 12) {
5035 struct notify_change *tmp;
5036 struct notify_change *c;
5037 uint32_t next_ofs = IVAL(base, ofs);
5038 uint32_t file_name_length = IVAL(base, ofs+8);
5039 size_t namelen;
5040 bool ok;
5042 tmp = talloc_realloc(
5043 state,
5044 state->changes,
5045 struct notify_change,
5046 state->num_changes + 1);
5047 if (tevent_req_nomem(tmp, req)) {
5048 return;
5050 state->changes = tmp;
5051 c = &state->changes[state->num_changes];
5052 state->num_changes += 1;
5054 if (smb_buffer_oob(len, ofs, next_ofs) ||
5055 smb_buffer_oob(len, ofs+12, file_name_length)) {
5056 tevent_req_nterror(
5057 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
5058 return;
5061 c->action = IVAL(base, ofs+4);
5063 ok = convert_string_talloc(
5064 state->changes,
5065 CH_UTF16LE,
5066 CH_UNIX,
5067 base + ofs + 12,
5068 file_name_length,
5069 &c->name,
5070 &namelen);
5071 if (!ok) {
5072 tevent_req_nterror(
5073 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
5074 return;
5077 if (next_ofs == 0) {
5078 break;
5080 ofs += next_ofs;
5083 tevent_req_done(req);
5086 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
5087 TALLOC_CTX *mem_ctx,
5088 struct notify_change **pchanges,
5089 uint32_t *pnum_changes)
5091 struct cli_smb2_notify_state *state = tevent_req_data(
5092 req, struct cli_smb2_notify_state);
5093 NTSTATUS status;
5095 if (tevent_req_is_nterror(req, &status)) {
5096 return status;
5098 *pchanges = talloc_move(mem_ctx, &state->changes);
5099 *pnum_changes = state->num_changes;
5100 return NT_STATUS_OK;
5103 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
5104 uint32_t buffer_size, uint32_t completion_filter,
5105 bool recursive, TALLOC_CTX *mem_ctx,
5106 struct notify_change **pchanges,
5107 uint32_t *pnum_changes)
5109 TALLOC_CTX *frame = talloc_stackframe();
5110 struct tevent_context *ev;
5111 struct tevent_req *req;
5112 NTSTATUS status = NT_STATUS_NO_MEMORY;
5114 if (smbXcli_conn_has_async_calls(cli->conn)) {
5116 * Can't use sync call while an async call is in flight
5118 status = NT_STATUS_INVALID_PARAMETER;
5119 goto fail;
5121 ev = samba_tevent_context_init(frame);
5122 if (ev == NULL) {
5123 goto fail;
5125 req = cli_smb2_notify_send(
5126 frame,
5128 cli,
5129 fnum,
5130 buffer_size,
5131 completion_filter,
5132 recursive);
5133 if (req == NULL) {
5134 goto fail;
5136 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
5137 goto fail;
5139 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
5140 fail:
5141 TALLOC_FREE(frame);
5142 return status;
5145 struct cli_smb2_fsctl_state {
5146 DATA_BLOB out;
5149 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
5151 struct tevent_req *cli_smb2_fsctl_send(
5152 TALLOC_CTX *mem_ctx,
5153 struct tevent_context *ev,
5154 struct cli_state *cli,
5155 uint16_t fnum,
5156 uint32_t ctl_code,
5157 const DATA_BLOB *in,
5158 uint32_t max_out)
5160 struct tevent_req *req = NULL, *subreq = NULL;
5161 struct cli_smb2_fsctl_state *state = NULL;
5162 struct smb2_hnd *ph = NULL;
5163 NTSTATUS status;
5165 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
5166 if (req == NULL) {
5167 return NULL;
5170 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
5171 if (tevent_req_nterror(req, status)) {
5172 return tevent_req_post(req, ev);
5175 subreq = smb2cli_ioctl_send(
5176 state,
5178 cli->conn,
5179 cli->timeout,
5180 cli->smb2.session,
5181 cli->smb2.tcon,
5182 ph->fid_persistent,
5183 ph->fid_volatile,
5184 ctl_code,
5185 0, /* in_max_input_length */
5187 max_out,
5188 NULL,
5189 SMB2_IOCTL_FLAG_IS_FSCTL);
5191 if (tevent_req_nomem(subreq, req)) {
5192 return tevent_req_post(req, ev);
5194 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
5195 return req;
5198 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5200 struct tevent_req *req = tevent_req_callback_data(
5201 subreq, struct tevent_req);
5202 struct cli_smb2_fsctl_state *state = tevent_req_data(
5203 req, struct cli_smb2_fsctl_state);
5204 NTSTATUS status;
5206 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5207 tevent_req_simple_finish_ntstatus(subreq, status);
5210 NTSTATUS cli_smb2_fsctl_recv(
5211 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5213 struct cli_smb2_fsctl_state *state = tevent_req_data(
5214 req, struct cli_smb2_fsctl_state);
5215 NTSTATUS status = NT_STATUS_OK;
5217 if (tevent_req_is_nterror(req, &status)) {
5218 tevent_req_received(req);
5219 return status;
5222 if (state->out.length == 0) {
5223 *out = (DATA_BLOB) { .data = NULL, };
5224 } else {
5226 * Can't use talloc_move() here, the outblobs from
5227 * smb2cli_ioctl_recv() are not standalone talloc
5228 * objects but just peek into the larger buffers
5229 * received, hanging off "state".
5231 *out = data_blob_talloc(
5232 mem_ctx, state->out.data, state->out.length);
5233 if (out->data == NULL) {
5234 status = NT_STATUS_NO_MEMORY;
5238 tevent_req_received(req);
5239 return NT_STATUS_OK;