smbd: Remove message_to_share_mode_entry and vice versa
[Samba.git] / source3 / libsmb / cli_smb2_fnum.c
blob08d95cf35798bc0647613efa6ebd3adfeed5ed1a
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 "librpc/gen_ndr/ndr_smb3posix.h"
46 #include "lib/util/string_wrappers.h"
47 #include "lib/util/idtree.h"
49 struct smb2_hnd {
50 uint64_t fid_persistent;
51 uint64_t fid_volatile;
55 * Handle mapping code.
58 /***************************************************************
59 Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
60 Ensures handle is owned by cli struct.
61 ***************************************************************/
63 static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
64 const struct smb2_hnd *ph, /* In */
65 uint16_t *pfnum) /* Out */
67 int ret;
68 struct idr_context *idp = cli->smb2.open_handles;
69 struct smb2_hnd *owned_h = talloc_memdup(cli,
70 ph,
71 sizeof(struct smb2_hnd));
73 if (owned_h == NULL) {
74 return NT_STATUS_NO_MEMORY;
77 if (idp == NULL) {
78 /* Lazy init */
79 cli->smb2.open_handles = idr_init(cli);
80 if (cli->smb2.open_handles == NULL) {
81 TALLOC_FREE(owned_h);
82 return NT_STATUS_NO_MEMORY;
84 idp = cli->smb2.open_handles;
87 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
88 if (ret == -1) {
89 TALLOC_FREE(owned_h);
90 return NT_STATUS_NO_MEMORY;
93 *pfnum = (uint16_t)ret;
94 return NT_STATUS_OK;
97 /***************************************************************
98 Return the smb2_hnd pointer associated with the given fnum.
99 ***************************************************************/
101 static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
102 uint16_t fnum, /* In */
103 struct smb2_hnd **pph) /* Out */
105 struct idr_context *idp = cli->smb2.open_handles;
107 if (idp == NULL) {
108 return NT_STATUS_INVALID_PARAMETER;
110 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
111 if (*pph == NULL) {
112 return NT_STATUS_INVALID_HANDLE;
114 return NT_STATUS_OK;
117 /***************************************************************
118 Delete the fnum to smb2_hnd mapping. Zeros out handle on
119 successful return.
120 ***************************************************************/
122 static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
123 struct smb2_hnd **pph, /* In */
124 uint16_t fnum) /* In */
126 struct idr_context *idp = cli->smb2.open_handles;
127 struct smb2_hnd *ph;
129 if (idp == NULL) {
130 return NT_STATUS_INVALID_PARAMETER;
133 ph = (struct smb2_hnd *)idr_find(idp, fnum);
134 if (ph != *pph) {
135 return NT_STATUS_INVALID_PARAMETER;
137 idr_remove(idp, fnum);
138 TALLOC_FREE(*pph);
139 return NT_STATUS_OK;
142 /***************************************************************
143 Oplock mapping code.
144 ***************************************************************/
146 static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
148 if (create_flags.batch_oplock) {
149 return SMB2_OPLOCK_LEVEL_BATCH;
150 } else if (create_flags.exclusive_oplock) {
151 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
154 /* create_flags doesn't do a level2 request. */
155 return SMB2_OPLOCK_LEVEL_NONE;
158 /***************************************************************
159 If we're on a DFS share, ensure we convert to a full DFS path
160 if this hasn't already been done.
161 ***************************************************************/
163 static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
164 struct cli_state *cli,
165 char *path)
167 bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
168 smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
169 bool is_already_dfs_path = false;
171 if (!is_dfs) {
172 return path;
174 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
175 if (is_already_dfs_path) {
176 return path;
178 if (path[0] == '\0') {
179 return talloc_asprintf(ctx,
180 "%s\\%s",
181 smbXcli_conn_remote_name(cli->conn),
182 cli->share);
184 while (*path == '\\') {
185 path++;
187 return talloc_asprintf(ctx,
188 "%s\\%s\\%s",
189 smbXcli_conn_remote_name(cli->conn),
190 cli->share,
191 path);
194 /***************************************************************
195 Small wrapper that allows SMB2 create to return a uint16_t fnum.
196 ***************************************************************/
198 struct cli_smb2_create_fnum_state {
199 struct cli_state *cli;
200 struct smb2_create_blobs in_cblobs;
201 struct smb2_create_blobs out_cblobs;
202 struct smb_create_returns cr;
203 struct symlink_reparse_struct *symlink;
204 uint16_t fnum;
205 struct tevent_req *subreq;
208 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
209 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
211 struct tevent_req *cli_smb2_create_fnum_send(
212 TALLOC_CTX *mem_ctx,
213 struct tevent_context *ev,
214 struct cli_state *cli,
215 const char *fname_in,
216 struct cli_smb2_create_flags create_flags,
217 uint32_t impersonation_level,
218 uint32_t desired_access,
219 uint32_t file_attributes,
220 uint32_t share_access,
221 uint32_t create_disposition,
222 uint32_t create_options,
223 const struct smb2_create_blobs *in_cblobs)
225 struct tevent_req *req, *subreq;
226 struct cli_smb2_create_fnum_state *state;
227 char *fname = NULL;
228 size_t fname_len = 0;
229 bool have_twrp;
230 NTTIME ntt;
231 NTSTATUS status;
233 req = tevent_req_create(mem_ctx, &state,
234 struct cli_smb2_create_fnum_state);
235 if (req == NULL) {
236 return NULL;
238 state->cli = cli;
240 fname = talloc_strdup(state, fname_in);
241 if (tevent_req_nomem(fname, req)) {
242 return tevent_req_post(req, ev);
245 if (cli->backup_intent) {
246 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
249 if (cli->smb2.client_smb311_posix) {
250 uint8_t modebuf[4] = {
254 status =
255 smb2_create_blob_add(state,
256 &state->in_cblobs,
257 SMB2_CREATE_TAG_POSIX,
258 (DATA_BLOB){
259 .data = modebuf,
260 .length = sizeof(modebuf),
262 if (tevent_req_nterror(req, status)) {
263 return tevent_req_post(req, ev);
267 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
268 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
269 if (have_twrp) {
270 status = smb2_create_blob_add(
271 state,
272 &state->in_cblobs,
273 SMB2_CREATE_TAG_TWRP,
274 (DATA_BLOB) {
275 .data = (uint8_t *)&ntt,
276 .length = sizeof(ntt),
278 if (tevent_req_nterror(req, status)) {
279 return tevent_req_post(req, ev);
283 if (in_cblobs != NULL) {
284 uint32_t i;
285 for (i=0; i<in_cblobs->num_blobs; i++) {
286 struct smb2_create_blob *b = &in_cblobs->blobs[i];
287 status = smb2_create_blob_add(
288 state, &state->in_cblobs, b->tag, b->data);
289 if (!NT_STATUS_IS_OK(status)) {
290 tevent_req_nterror(req, status);
291 return tevent_req_post(req, ev);
296 fname = smb2_dfs_share_path(state, cli, fname);
297 if (tevent_req_nomem(fname, req)) {
298 return tevent_req_post(req, ev);
300 fname_len = strlen(fname);
302 /* SMB2 is pickier about pathnames. Ensure it doesn't
303 start in a '\' */
304 if (*fname == '\\') {
305 fname++;
306 fname_len--;
309 /* Or end in a '\' */
310 if (fname_len > 0 && fname[fname_len-1] == '\\') {
311 fname[fname_len-1] = '\0';
314 subreq = smb2cli_create_send(state, ev,
315 cli->conn,
316 cli->timeout,
317 cli->smb2.session,
318 cli->smb2.tcon,
319 fname,
320 flags_to_smb2_oplock(create_flags),
321 impersonation_level,
322 desired_access,
323 file_attributes,
324 share_access,
325 create_disposition,
326 create_options,
327 &state->in_cblobs);
328 if (tevent_req_nomem(subreq, req)) {
329 return tevent_req_post(req, ev);
331 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
333 state->subreq = subreq;
334 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
336 return req;
339 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
341 struct tevent_req *req = tevent_req_callback_data(
342 subreq, struct tevent_req);
343 struct cli_smb2_create_fnum_state *state = tevent_req_data(
344 req, struct cli_smb2_create_fnum_state);
345 struct smb2_hnd h;
346 NTSTATUS status;
348 status = smb2cli_create_recv(
349 subreq,
350 &h.fid_persistent,
351 &h.fid_volatile, &state->cr,
352 state,
353 &state->out_cblobs,
354 &state->symlink);
355 TALLOC_FREE(subreq);
356 if (tevent_req_nterror(req, status)) {
357 return;
360 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
361 if (tevent_req_nterror(req, status)) {
362 return;
364 tevent_req_done(req);
367 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
369 struct cli_smb2_create_fnum_state *state = tevent_req_data(
370 req, struct cli_smb2_create_fnum_state);
371 return tevent_req_cancel(state->subreq);
374 NTSTATUS cli_smb2_create_fnum_recv(
375 struct tevent_req *req,
376 uint16_t *pfnum,
377 struct smb_create_returns *cr,
378 TALLOC_CTX *mem_ctx,
379 struct smb2_create_blobs *out_cblobs,
380 struct symlink_reparse_struct **symlink)
382 struct cli_smb2_create_fnum_state *state = tevent_req_data(
383 req, struct cli_smb2_create_fnum_state);
384 NTSTATUS status;
386 if (tevent_req_is_nterror(req, &status)) {
387 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
388 (symlink != NULL)) {
389 *symlink = talloc_move(mem_ctx, &state->symlink);
391 state->cli->raw_status = status;
392 return status;
394 if (pfnum != NULL) {
395 *pfnum = state->fnum;
397 if (cr != NULL) {
398 *cr = state->cr;
400 if (out_cblobs != NULL) {
401 *out_cblobs = (struct smb2_create_blobs) {
402 .num_blobs = state->out_cblobs.num_blobs,
403 .blobs = talloc_move(
404 mem_ctx, &state->out_cblobs.blobs),
407 state->cli->raw_status = NT_STATUS_OK;
408 return NT_STATUS_OK;
411 NTSTATUS cli_smb2_create_fnum(
412 struct cli_state *cli,
413 const char *fname,
414 struct cli_smb2_create_flags create_flags,
415 uint32_t impersonation_level,
416 uint32_t desired_access,
417 uint32_t file_attributes,
418 uint32_t share_access,
419 uint32_t create_disposition,
420 uint32_t create_options,
421 const struct smb2_create_blobs *in_cblobs,
422 uint16_t *pfid,
423 struct smb_create_returns *cr,
424 TALLOC_CTX *mem_ctx,
425 struct smb2_create_blobs *out_cblobs)
427 TALLOC_CTX *frame = talloc_stackframe();
428 struct tevent_context *ev;
429 struct tevent_req *req;
430 NTSTATUS status = NT_STATUS_NO_MEMORY;
432 if (smbXcli_conn_has_async_calls(cli->conn)) {
434 * Can't use sync call while an async call is in flight
436 status = NT_STATUS_INVALID_PARAMETER;
437 goto fail;
439 ev = samba_tevent_context_init(frame);
440 if (ev == NULL) {
441 goto fail;
443 req = cli_smb2_create_fnum_send(
444 frame,
446 cli,
447 fname,
448 create_flags,
449 impersonation_level,
450 desired_access,
451 file_attributes,
452 share_access,
453 create_disposition,
454 create_options,
455 in_cblobs);
456 if (req == NULL) {
457 goto fail;
459 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
460 goto fail;
462 status = cli_smb2_create_fnum_recv(
463 req, pfid, cr, mem_ctx, out_cblobs, NULL);
464 fail:
465 TALLOC_FREE(frame);
466 return status;
469 /***************************************************************
470 Small wrapper that allows SMB2 close to use a uint16_t fnum.
471 ***************************************************************/
473 struct cli_smb2_close_fnum_state {
474 struct cli_state *cli;
475 uint16_t fnum;
476 struct smb2_hnd *ph;
479 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
481 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
482 struct tevent_context *ev,
483 struct cli_state *cli,
484 uint16_t fnum,
485 uint16_t flags)
487 struct tevent_req *req, *subreq;
488 struct cli_smb2_close_fnum_state *state;
489 NTSTATUS status;
491 req = tevent_req_create(mem_ctx, &state,
492 struct cli_smb2_close_fnum_state);
493 if (req == NULL) {
494 return NULL;
496 state->cli = cli;
497 state->fnum = fnum;
499 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
500 if (tevent_req_nterror(req, status)) {
501 return tevent_req_post(req, ev);
504 subreq = smb2cli_close_send(state,
506 cli->conn,
507 cli->timeout,
508 cli->smb2.session,
509 cli->smb2.tcon,
510 flags,
511 state->ph->fid_persistent,
512 state->ph->fid_volatile);
513 if (tevent_req_nomem(subreq, req)) {
514 return tevent_req_post(req, ev);
516 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
517 return req;
520 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
522 struct tevent_req *req = tevent_req_callback_data(
523 subreq, struct tevent_req);
524 struct cli_smb2_close_fnum_state *state = tevent_req_data(
525 req, struct cli_smb2_close_fnum_state);
526 NTSTATUS status;
528 status = smb2cli_close_recv(subreq);
529 if (tevent_req_nterror(req, status)) {
530 return;
533 /* Delete the fnum -> handle mapping. */
534 status = delete_smb2_handle_mapping(state->cli, &state->ph,
535 state->fnum);
536 if (tevent_req_nterror(req, status)) {
537 return;
539 tevent_req_done(req);
542 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
544 struct cli_smb2_close_fnum_state *state = tevent_req_data(
545 req, struct cli_smb2_close_fnum_state);
546 NTSTATUS status = NT_STATUS_OK;
548 if (tevent_req_is_nterror(req, &status)) {
549 state->cli->raw_status = status;
551 tevent_req_received(req);
552 return status;
555 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
557 TALLOC_CTX *frame = talloc_stackframe();
558 struct tevent_context *ev;
559 struct tevent_req *req;
560 NTSTATUS status = NT_STATUS_NO_MEMORY;
562 if (smbXcli_conn_has_async_calls(cli->conn)) {
564 * Can't use sync call while an async call is in flight
566 status = NT_STATUS_INVALID_PARAMETER;
567 goto fail;
569 ev = samba_tevent_context_init(frame);
570 if (ev == NULL) {
571 goto fail;
573 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
574 if (req == NULL) {
575 goto fail;
577 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
578 goto fail;
580 status = cli_smb2_close_fnum_recv(req);
581 fail:
582 TALLOC_FREE(frame);
583 return status;
586 struct cli_smb2_set_info_fnum_state {
587 uint8_t dummy;
590 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
592 struct tevent_req *cli_smb2_set_info_fnum_send(
593 TALLOC_CTX *mem_ctx,
594 struct tevent_context *ev,
595 struct cli_state *cli,
596 uint16_t fnum,
597 uint8_t in_info_type,
598 uint8_t in_info_class,
599 const DATA_BLOB *in_input_buffer,
600 uint32_t in_additional_info)
602 struct tevent_req *req = NULL, *subreq = NULL;
603 struct cli_smb2_set_info_fnum_state *state = NULL;
604 struct smb2_hnd *ph = NULL;
605 NTSTATUS status;
607 req = tevent_req_create(
608 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
609 if (req == NULL) {
610 return NULL;
613 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
614 if (tevent_req_nterror(req, status)) {
615 return tevent_req_post(req, ev);
618 subreq = smb2cli_set_info_send(
619 state,
621 cli->conn,
622 cli->timeout,
623 cli->smb2.session,
624 cli->smb2.tcon,
625 in_info_type,
626 in_info_class,
627 in_input_buffer,
628 in_additional_info,
629 ph->fid_persistent,
630 ph->fid_volatile);
631 if (tevent_req_nomem(subreq, req)) {
632 return tevent_req_post(req, ev);
634 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
635 return req;
638 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
640 NTSTATUS status = smb2cli_set_info_recv(subreq);
641 tevent_req_simple_finish_ntstatus(subreq, status);
644 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
646 return tevent_req_simple_recv_ntstatus(req);
649 NTSTATUS cli_smb2_set_info_fnum(
650 struct cli_state *cli,
651 uint16_t fnum,
652 uint8_t in_info_type,
653 uint8_t in_info_class,
654 const DATA_BLOB *in_input_buffer,
655 uint32_t in_additional_info)
657 TALLOC_CTX *frame = talloc_stackframe();
658 struct tevent_context *ev = NULL;
659 struct tevent_req *req = NULL;
660 NTSTATUS status = NT_STATUS_NO_MEMORY;
661 bool ok;
663 if (smbXcli_conn_has_async_calls(cli->conn)) {
665 * Can't use sync call while an async call is in flight
667 status = NT_STATUS_INVALID_PARAMETER;
668 goto fail;
670 ev = samba_tevent_context_init(frame);
671 if (ev == NULL) {
672 goto fail;
674 req = cli_smb2_set_info_fnum_send(
675 frame,
677 cli,
678 fnum,
679 in_info_type,
680 in_info_class,
681 in_input_buffer,
682 in_additional_info);
683 if (req == NULL) {
684 goto fail;
686 ok = tevent_req_poll_ntstatus(req, ev, &status);
687 if (!ok) {
688 goto fail;
690 status = cli_smb2_set_info_fnum_recv(req);
691 fail:
692 TALLOC_FREE(frame);
693 return status;
696 struct cli_smb2_delete_on_close_state {
697 struct cli_state *cli;
698 uint8_t data[1];
699 DATA_BLOB inbuf;
702 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
704 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
705 struct tevent_context *ev,
706 struct cli_state *cli,
707 uint16_t fnum,
708 bool flag)
710 struct tevent_req *req = NULL;
711 struct cli_smb2_delete_on_close_state *state = NULL;
712 struct tevent_req *subreq = NULL;
713 uint8_t in_info_type;
714 uint8_t in_file_info_class;
716 req = tevent_req_create(mem_ctx, &state,
717 struct cli_smb2_delete_on_close_state);
718 if (req == NULL) {
719 return NULL;
721 state->cli = cli;
724 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
725 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
727 in_info_type = 1;
728 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
729 /* Setup data array. */
730 SCVAL(&state->data[0], 0, flag ? 1 : 0);
731 state->inbuf.data = &state->data[0];
732 state->inbuf.length = 1;
734 subreq = cli_smb2_set_info_fnum_send(
735 state,
737 cli,
738 fnum,
739 in_info_type,
740 in_file_info_class,
741 &state->inbuf,
743 if (tevent_req_nomem(subreq, req)) {
744 return tevent_req_post(req, ev);
746 tevent_req_set_callback(subreq,
747 cli_smb2_delete_on_close_done,
748 req);
749 return req;
752 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
754 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
755 tevent_req_simple_finish_ntstatus(subreq, status);
758 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
760 struct cli_smb2_delete_on_close_state *state =
761 tevent_req_data(req,
762 struct cli_smb2_delete_on_close_state);
763 NTSTATUS status;
765 if (tevent_req_is_nterror(req, &status)) {
766 state->cli->raw_status = status;
767 tevent_req_received(req);
768 return status;
771 state->cli->raw_status = NT_STATUS_OK;
772 tevent_req_received(req);
773 return NT_STATUS_OK;
776 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
778 TALLOC_CTX *frame = talloc_stackframe();
779 struct tevent_context *ev;
780 struct tevent_req *req;
781 NTSTATUS status = NT_STATUS_NO_MEMORY;
783 if (smbXcli_conn_has_async_calls(cli->conn)) {
785 * Can't use sync call while an async call is in flight
787 status = NT_STATUS_INVALID_PARAMETER;
788 goto fail;
790 ev = samba_tevent_context_init(frame);
791 if (ev == NULL) {
792 goto fail;
794 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
795 if (req == NULL) {
796 goto fail;
798 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
799 goto fail;
801 status = cli_smb2_delete_on_close_recv(req);
802 fail:
803 TALLOC_FREE(frame);
804 return status;
807 struct cli_smb2_mkdir_state {
808 struct tevent_context *ev;
809 struct cli_state *cli;
812 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
813 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
815 struct tevent_req *cli_smb2_mkdir_send(
816 TALLOC_CTX *mem_ctx,
817 struct tevent_context *ev,
818 struct cli_state *cli,
819 const char *dname)
821 struct tevent_req *req = NULL, *subreq = NULL;
822 struct cli_smb2_mkdir_state *state = NULL;
824 req = tevent_req_create(
825 mem_ctx, &state, struct cli_smb2_mkdir_state);
826 if (req == NULL) {
827 return NULL;
829 state->ev = ev;
830 state->cli = cli;
832 /* Ensure this is a directory. */
833 subreq = cli_smb2_create_fnum_send(
834 state, /* mem_ctx */
835 ev, /* ev */
836 cli, /* cli */
837 dname, /* fname */
838 (struct cli_smb2_create_flags){0}, /* create_flags */
839 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
840 FILE_READ_ATTRIBUTES, /* desired_access */
841 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
842 FILE_SHARE_READ|
843 FILE_SHARE_WRITE, /* share_access */
844 FILE_CREATE, /* create_disposition */
845 FILE_DIRECTORY_FILE, /* create_options */
846 NULL); /* in_cblobs */
847 if (tevent_req_nomem(subreq, req)) {
848 return tevent_req_post(req, ev);
850 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
851 return req;
854 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
856 struct tevent_req *req = tevent_req_callback_data(
857 subreq, struct tevent_req);
858 struct cli_smb2_mkdir_state *state = tevent_req_data(
859 req, struct cli_smb2_mkdir_state);
860 NTSTATUS status;
861 uint16_t fnum = 0xffff;
863 status = cli_smb2_create_fnum_recv(
864 subreq, &fnum, NULL, NULL, NULL, NULL);
865 TALLOC_FREE(subreq);
866 if (tevent_req_nterror(req, status)) {
867 return;
870 subreq =
871 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
872 if (tevent_req_nomem(subreq, req)) {
873 return;
875 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
878 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
880 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
881 tevent_req_simple_finish_ntstatus(subreq, status);
884 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
886 return tevent_req_simple_recv_ntstatus(req);
889 struct cli_smb2_rmdir_state {
890 struct tevent_context *ev;
891 struct cli_state *cli;
892 const char *dname;
893 const struct smb2_create_blobs *in_cblobs;
894 uint16_t fnum;
895 NTSTATUS status;
898 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
899 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
900 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
901 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
903 struct tevent_req *cli_smb2_rmdir_send(
904 TALLOC_CTX *mem_ctx,
905 struct tevent_context *ev,
906 struct cli_state *cli,
907 const char *dname,
908 const struct smb2_create_blobs *in_cblobs)
910 struct tevent_req *req = NULL, *subreq = NULL;
911 struct cli_smb2_rmdir_state *state = NULL;
913 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
914 if (req == NULL) {
915 return NULL;
917 state->ev = ev;
918 state->cli = cli;
919 state->dname = dname;
920 state->in_cblobs = in_cblobs;
922 subreq = cli_smb2_create_fnum_send(
923 state,
924 state->ev,
925 state->cli,
926 state->dname,
927 (struct cli_smb2_create_flags){0},
928 SMB2_IMPERSONATION_IMPERSONATION,
929 DELETE_ACCESS, /* desired_access */
930 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
931 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
932 FILE_OPEN, /* create_disposition */
933 FILE_DIRECTORY_FILE, /* create_options */
934 state->in_cblobs); /* in_cblobs */
935 if (tevent_req_nomem(subreq, req)) {
936 return tevent_req_post(req, ev);
938 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
939 return req;
942 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
944 struct tevent_req *req = tevent_req_callback_data(
945 subreq, struct tevent_req);
946 struct cli_smb2_rmdir_state *state = tevent_req_data(
947 req, struct cli_smb2_rmdir_state);
948 NTSTATUS status;
950 status = cli_smb2_create_fnum_recv(
951 subreq, &state->fnum, NULL, NULL, NULL, NULL);
952 TALLOC_FREE(subreq);
954 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
956 * Naive option to match our SMB1 code. Assume the
957 * symlink path that tripped us up was the last
958 * component and try again. Eventually we will have to
959 * deal with the returned path unprocessed component. JRA.
961 subreq = cli_smb2_create_fnum_send(
962 state,
963 state->ev,
964 state->cli,
965 state->dname,
966 (struct cli_smb2_create_flags){0},
967 SMB2_IMPERSONATION_IMPERSONATION,
968 DELETE_ACCESS, /* desired_access */
969 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
970 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
971 FILE_OPEN, /* create_disposition */
972 FILE_DIRECTORY_FILE|
973 FILE_DELETE_ON_CLOSE|
974 FILE_OPEN_REPARSE_POINT, /* create_options */
975 state->in_cblobs); /* in_cblobs */
976 if (tevent_req_nomem(subreq, req)) {
977 return;
979 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
980 return;
983 if (tevent_req_nterror(req, status)) {
984 return;
987 subreq = cli_smb2_delete_on_close_send(
988 state, state->ev, state->cli, state->fnum, true);
989 if (tevent_req_nomem(subreq, req)) {
990 return;
992 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
995 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
997 struct tevent_req *req = tevent_req_callback_data(
998 subreq, struct tevent_req);
999 struct cli_smb2_rmdir_state *state = tevent_req_data(
1000 req, struct cli_smb2_rmdir_state);
1001 NTSTATUS status;
1003 status = cli_smb2_create_fnum_recv(
1004 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1005 TALLOC_FREE(subreq);
1006 if (tevent_req_nterror(req, status)) {
1007 return;
1010 subreq = cli_smb2_delete_on_close_send(
1011 state, state->ev, state->cli, state->fnum, true);
1012 if (tevent_req_nomem(subreq, req)) {
1013 return;
1015 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1018 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1020 struct tevent_req *req = tevent_req_callback_data(
1021 subreq, struct tevent_req);
1022 struct cli_smb2_rmdir_state *state = tevent_req_data(
1023 req, struct cli_smb2_rmdir_state);
1025 state->status = cli_smb2_delete_on_close_recv(subreq);
1026 TALLOC_FREE(subreq);
1029 * Close the fd even if the set_disp failed
1032 subreq = cli_smb2_close_fnum_send(state,
1033 state->ev,
1034 state->cli,
1035 state->fnum,
1037 if (tevent_req_nomem(subreq, req)) {
1038 return;
1040 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1043 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1045 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1046 tevent_req_simple_finish_ntstatus(subreq, status);
1049 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1051 struct cli_smb2_rmdir_state *state = tevent_req_data(
1052 req, struct cli_smb2_rmdir_state);
1053 NTSTATUS status;
1055 if (tevent_req_is_nterror(req, &status)) {
1056 return status;
1058 return state->status;
1061 /***************************************************************
1062 Small wrapper that allows SMB2 to unlink a pathname.
1063 ***************************************************************/
1065 struct cli_smb2_unlink_state {
1066 struct tevent_context *ev;
1067 struct cli_state *cli;
1068 const char *fname;
1069 const struct smb2_create_blobs *in_cblobs;
1072 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1073 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1074 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1076 struct tevent_req *cli_smb2_unlink_send(
1077 TALLOC_CTX *mem_ctx,
1078 struct tevent_context *ev,
1079 struct cli_state *cli,
1080 const char *fname,
1081 const struct smb2_create_blobs *in_cblobs)
1083 struct tevent_req *req = NULL, *subreq = NULL;
1084 struct cli_smb2_unlink_state *state = NULL;
1086 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1087 if (req == NULL) {
1088 return NULL;
1090 state->ev = ev;
1091 state->cli = cli;
1092 state->fname = fname;
1093 state->in_cblobs = in_cblobs;
1095 subreq = cli_smb2_create_fnum_send(
1096 state, /* mem_ctx */
1097 state->ev, /* tevent_context */
1098 state->cli, /* cli_struct */
1099 state->fname, /* filename */
1100 (struct cli_smb2_create_flags){0},
1101 SMB2_IMPERSONATION_IMPERSONATION,
1102 DELETE_ACCESS, /* desired_access */
1103 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1104 FILE_SHARE_READ|
1105 FILE_SHARE_WRITE|
1106 FILE_SHARE_DELETE, /* share_access */
1107 FILE_OPEN, /* create_disposition */
1108 FILE_DELETE_ON_CLOSE, /* create_options */
1109 state->in_cblobs); /* in_cblobs */
1110 if (tevent_req_nomem(subreq, req)) {
1111 return tevent_req_post(req, ev);
1113 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1114 return req;
1117 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1119 struct tevent_req *req = tevent_req_callback_data(
1120 subreq, struct tevent_req);
1121 struct cli_smb2_unlink_state *state = tevent_req_data(
1122 req, struct cli_smb2_unlink_state);
1123 uint16_t fnum = 0xffff;
1124 NTSTATUS status;
1126 status = cli_smb2_create_fnum_recv(
1127 subreq, &fnum, NULL, NULL, NULL, NULL);
1128 TALLOC_FREE(subreq);
1130 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1131 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1133 * Naive option to match our SMB1 code. Assume the
1134 * symlink path that tripped us up was the last
1135 * component and try again. Eventually we will have to
1136 * deal with the returned path unprocessed component. JRA.
1138 subreq = cli_smb2_create_fnum_send(
1139 state, /* mem_ctx */
1140 state->ev, /* tevent_context */
1141 state->cli, /* cli_struct */
1142 state->fname, /* filename */
1143 (struct cli_smb2_create_flags){0},
1144 SMB2_IMPERSONATION_IMPERSONATION,
1145 DELETE_ACCESS, /* desired_access */
1146 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1147 FILE_SHARE_READ|
1148 FILE_SHARE_WRITE|
1149 FILE_SHARE_DELETE, /* share_access */
1150 FILE_OPEN, /* create_disposition */
1151 FILE_DELETE_ON_CLOSE|
1152 FILE_OPEN_REPARSE_POINT, /* create_options */
1153 state->in_cblobs); /* in_cblobs */
1154 if (tevent_req_nomem(subreq, req)) {
1155 return;
1157 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1158 return;
1161 if (tevent_req_nterror(req, status)) {
1162 return;
1165 subreq =
1166 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1167 if (tevent_req_nomem(subreq, req)) {
1168 return;
1170 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1173 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1175 struct tevent_req *req = tevent_req_callback_data(
1176 subreq, struct tevent_req);
1177 struct cli_smb2_unlink_state *state = tevent_req_data(
1178 req, struct cli_smb2_unlink_state);
1179 uint16_t fnum = 0xffff;
1180 NTSTATUS status;
1182 status = cli_smb2_create_fnum_recv(
1183 subreq, &fnum, NULL, NULL, NULL, NULL);
1184 TALLOC_FREE(subreq);
1185 if (tevent_req_nterror(req, status)) {
1186 return;
1189 subreq =
1190 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1191 if (tevent_req_nomem(subreq, req)) {
1192 return;
1194 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1197 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1199 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1200 tevent_req_simple_finish_ntstatus(subreq, status);
1203 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1205 return tevent_req_simple_recv_ntstatus(req);
1208 /***************************************************************
1209 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1210 ***************************************************************/
1212 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1213 uint32_t dir_data_length,
1214 struct file_info *finfo,
1215 uint32_t *next_offset)
1217 struct smb3_file_posix_information info = {};
1218 size_t consumed;
1219 enum ndr_err_code ndr_err;
1220 size_t namelen = 0;
1221 size_t ret = 0;
1222 uint32_t _next_offset = 0;
1224 if (dir_data_length < 4) {
1225 return NT_STATUS_INFO_LENGTH_MISMATCH;
1228 _next_offset = IVAL(dir_data, 0);
1230 if (_next_offset > dir_data_length) {
1231 return NT_STATUS_INFO_LENGTH_MISMATCH;
1234 if (_next_offset != 0) {
1235 /* Ensure we only read what in this record. */
1236 dir_data_length = _next_offset;
1240 * Skip NextEntryOffset and FileIndex
1242 if (dir_data_length < 8) {
1243 return NT_STATUS_INFO_LENGTH_MISMATCH;
1245 dir_data += 8;
1246 dir_data_length -= 8;
1248 ndr_err = ndr_pull_struct_blob_noalloc(
1249 dir_data,
1250 dir_data_length,
1251 &info,
1252 (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1253 &consumed);
1254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1255 return ndr_map_error2ntstatus(ndr_err);
1257 if (consumed > dir_data_length) {
1258 return NT_STATUS_INFO_LENGTH_MISMATCH;
1260 dir_data += consumed;
1261 dir_data_length -= consumed;
1263 finfo->btime_ts = interpret_long_date(info.creation_time);
1264 finfo->atime_ts = interpret_long_date(info.last_access_time);
1265 finfo->mtime_ts = interpret_long_date(info.last_write_time);
1266 finfo->ctime_ts = interpret_long_date(info.change_time);
1267 finfo->allocated_size = info.allocation_size;
1268 finfo->size = info.end_of_file;
1269 finfo->attr = info.file_attributes;
1270 finfo->ino = info.inode;
1271 finfo->st_ex_dev = info.device;
1272 finfo->st_ex_nlink = info.cc.nlinks;
1273 finfo->reparse_tag = info.cc.reparse_tag;
1274 finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
1275 sid_copy(&finfo->owner_sid, &info.cc.owner);
1276 sid_copy(&finfo->group_sid, &info.cc.group);
1278 if (dir_data_length < 4) {
1279 return NT_STATUS_INFO_LENGTH_MISMATCH;
1281 namelen = PULL_LE_U32(dir_data, 0);
1283 dir_data += 4;
1284 dir_data_length -= 4;
1286 if (namelen > dir_data_length) {
1287 return NT_STATUS_INFO_LENGTH_MISMATCH;
1290 ret = pull_string_talloc(finfo,
1291 dir_data,
1292 FLAGS2_UNICODE_STRINGS,
1293 &finfo->name,
1294 dir_data,
1295 namelen,
1296 STR_UNICODE);
1297 if (ret == (size_t)-1) {
1298 /* Bad conversion. */
1299 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1302 if (finfo->name == NULL) {
1303 /* Bad conversion. */
1304 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1307 *next_offset = _next_offset;
1308 return NT_STATUS_OK;
1311 /***************************************************************
1312 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1313 ***************************************************************/
1315 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1316 uint32_t dir_data_length,
1317 struct file_info *finfo,
1318 uint32_t *next_offset)
1320 size_t namelen = 0;
1321 size_t slen = 0;
1322 size_t ret = 0;
1324 if (dir_data_length < 4) {
1325 return NT_STATUS_INFO_LENGTH_MISMATCH;
1328 *next_offset = IVAL(dir_data, 0);
1330 if (*next_offset > dir_data_length) {
1331 return NT_STATUS_INFO_LENGTH_MISMATCH;
1334 if (*next_offset != 0) {
1335 /* Ensure we only read what in this record. */
1336 dir_data_length = *next_offset;
1339 if (dir_data_length < 105) {
1340 return NT_STATUS_INFO_LENGTH_MISMATCH;
1343 finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1344 finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1345 finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1346 finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1347 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1348 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1349 finfo->attr = IVAL(dir_data + 56, 0);
1350 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1351 namelen = IVAL(dir_data + 60,0);
1352 if (namelen > (dir_data_length - 104)) {
1353 return NT_STATUS_INFO_LENGTH_MISMATCH;
1355 finfo->reparse_tag = IVAL(dir_data + 64, 0);
1356 slen = CVAL(dir_data + 68, 0);
1357 if (slen > 24) {
1358 return NT_STATUS_INFO_LENGTH_MISMATCH;
1360 ret = pull_string_talloc(finfo,
1361 dir_data,
1362 FLAGS2_UNICODE_STRINGS,
1363 &finfo->short_name,
1364 dir_data + 70,
1365 slen,
1366 STR_UNICODE);
1367 if (ret == (size_t)-1) {
1368 /* Bad conversion. */
1369 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1372 ret = pull_string_talloc(finfo,
1373 dir_data,
1374 FLAGS2_UNICODE_STRINGS,
1375 &finfo->name,
1376 dir_data + 104,
1377 namelen,
1378 STR_UNICODE);
1379 if (ret == (size_t)-1) {
1380 /* Bad conversion. */
1381 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1384 if (finfo->name == NULL) {
1385 /* Bad conversion. */
1386 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1389 return NT_STATUS_OK;
1392 /*******************************************************************
1393 Given a filename - get its directory name
1394 ********************************************************************/
1396 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1397 const char *dir,
1398 char **parent,
1399 const char **name)
1401 char *p;
1402 ptrdiff_t len;
1404 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1406 if (p == NULL) {
1407 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1408 return false;
1410 if (name) {
1411 *name = dir;
1413 return true;
1416 len = p-dir;
1418 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1419 return false;
1421 (*parent)[len] = '\0';
1423 if (name) {
1424 *name = p+1;
1426 return true;
1429 struct cli_smb2_list_dir_data {
1430 uint8_t *data;
1431 uint32_t length;
1434 struct cli_smb2_list_state {
1435 struct tevent_context *ev;
1436 struct cli_state *cli;
1437 const char *mask;
1439 uint16_t fnum;
1441 NTSTATUS status;
1442 struct cli_smb2_list_dir_data *response;
1443 uint32_t offset;
1444 unsigned int info_level;
1447 static void cli_smb2_list_opened(struct tevent_req *subreq);
1448 static void cli_smb2_list_done(struct tevent_req *subreq);
1449 static void cli_smb2_list_closed(struct tevent_req *subreq);
1451 struct tevent_req *cli_smb2_list_send(
1452 TALLOC_CTX *mem_ctx,
1453 struct tevent_context *ev,
1454 struct cli_state *cli,
1455 const char *pathname,
1456 unsigned int info_level)
1458 struct tevent_req *req = NULL, *subreq = NULL;
1459 struct cli_smb2_list_state *state = NULL;
1460 char *parent = NULL;
1461 bool ok;
1462 struct smb2_create_blobs *in_cblobs = NULL;
1464 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1465 if (req == NULL) {
1466 return NULL;
1468 state->ev = ev;
1469 state->cli = cli;
1470 state->status = NT_STATUS_OK;
1471 state->info_level = info_level;
1473 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1474 if (!ok) {
1475 tevent_req_oom(req);
1476 return tevent_req_post(req, ev);
1479 if (smbXcli_conn_have_posix(cli->conn) &&
1480 info_level == SMB2_FIND_POSIX_INFORMATION)
1482 NTSTATUS status;
1484 /* The mode MUST be 0 when opening an existing file/dir, and
1485 * will be ignored by the server.
1487 uint8_t linear_mode[4] = { 0 };
1488 DATA_BLOB blob = { .data=linear_mode,
1489 .length=sizeof(linear_mode) };
1491 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1492 if (in_cblobs == NULL) {
1493 return NULL;
1496 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1497 SMB2_CREATE_TAG_POSIX, blob);
1498 if (tevent_req_nterror(req, status)) {
1499 tevent_req_nterror(req, status);
1500 return tevent_req_post(req, ev);
1504 subreq = cli_smb2_create_fnum_send(
1505 state, /* mem_ctx */
1506 ev, /* ev */
1507 cli, /* cli */
1508 parent, /* fname */
1509 (struct cli_smb2_create_flags){0}, /* create_flags */
1510 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1511 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1512 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1513 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1514 FILE_OPEN, /* create_disposition */
1515 FILE_DIRECTORY_FILE, /* create_options */
1516 in_cblobs); /* in_cblobs */
1517 TALLOC_FREE(in_cblobs);
1518 if (tevent_req_nomem(subreq, req)) {
1519 return tevent_req_post(req, ev);
1521 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1522 return req;
1525 static void cli_smb2_list_opened(struct tevent_req *subreq)
1527 struct tevent_req *req = tevent_req_callback_data(
1528 subreq, struct tevent_req);
1529 struct cli_smb2_list_state *state = tevent_req_data(
1530 req, struct cli_smb2_list_state);
1531 NTSTATUS status;
1533 status = cli_smb2_create_fnum_recv(
1534 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1535 TALLOC_FREE(subreq);
1536 if (tevent_req_nterror(req, status)) {
1537 return;
1541 * Make our caller get back to us via cli_smb2_list_recv(),
1542 * triggering the smb2_query_directory_send()
1544 tevent_req_defer_callback(req, state->ev);
1545 tevent_req_notify_callback(req);
1548 static void cli_smb2_list_done(struct tevent_req *subreq)
1550 struct tevent_req *req = tevent_req_callback_data(
1551 subreq, struct tevent_req);
1552 struct cli_smb2_list_state *state = tevent_req_data(
1553 req, struct cli_smb2_list_state);
1554 struct cli_smb2_list_dir_data *response = NULL;
1556 response = talloc(state, struct cli_smb2_list_dir_data);
1557 if (tevent_req_nomem(response, req)) {
1558 return;
1561 state->status = smb2cli_query_directory_recv(
1562 subreq, response, &response->data, &response->length);
1563 TALLOC_FREE(subreq);
1565 if (NT_STATUS_IS_OK(state->status)) {
1566 state->response = response;
1567 state->offset = 0;
1569 tevent_req_defer_callback(req, state->ev);
1570 tevent_req_notify_callback(req);
1571 return;
1574 TALLOC_FREE(response);
1576 subreq = cli_smb2_close_fnum_send(state,
1577 state->ev,
1578 state->cli,
1579 state->fnum,
1581 if (tevent_req_nomem(subreq, req)) {
1582 return;
1584 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1587 static void cli_smb2_list_closed(struct tevent_req *subreq)
1589 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1590 tevent_req_simple_finish_ntstatus(subreq, status);
1594 * Return the next finfo directory.
1596 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1597 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1598 * NT_STATUS_RETRY, which will then trigger the caller again when the
1599 * QUERY_DIRECTORY has returned with another buffer. This way we
1600 * guarantee that no asynchronous request is open after this call
1601 * returns an entry, so that other synchronous requests can be issued
1602 * on the same connection while the directory listing proceeds.
1604 NTSTATUS cli_smb2_list_recv(
1605 struct tevent_req *req,
1606 TALLOC_CTX *mem_ctx,
1607 struct file_info **pfinfo)
1609 struct cli_smb2_list_state *state = tevent_req_data(
1610 req, struct cli_smb2_list_state);
1611 struct cli_smb2_list_dir_data *response = NULL;
1612 struct file_info *finfo = NULL;
1613 NTSTATUS status;
1614 uint32_t next_offset = 0;
1615 bool in_progress;
1617 in_progress = tevent_req_is_in_progress(req);
1619 if (!in_progress) {
1620 if (!tevent_req_is_nterror(req, &status)) {
1621 status = NT_STATUS_NO_MORE_FILES;
1623 goto fail;
1626 response = state->response;
1627 if (response == NULL) {
1628 struct tevent_req *subreq = NULL;
1629 struct cli_state *cli = state->cli;
1630 struct smb2_hnd *ph = NULL;
1631 uint32_t max_trans, max_avail_len;
1632 bool ok;
1634 if (!NT_STATUS_IS_OK(state->status)) {
1635 status = state->status;
1636 goto fail;
1639 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1640 if (!NT_STATUS_IS_OK(status)) {
1641 goto fail;
1644 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1645 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1646 if (ok) {
1647 max_trans = MIN(max_trans, max_avail_len);
1650 subreq = smb2cli_query_directory_send(
1651 state, /* mem_ctx */
1652 state->ev, /* ev */
1653 cli->conn, /* conn */
1654 cli->timeout, /* timeout_msec */
1655 cli->smb2.session, /* session */
1656 cli->smb2.tcon, /* tcon */
1657 state->info_level, /* level */
1658 0, /* flags */
1659 0, /* file_index */
1660 ph->fid_persistent, /* fid_persistent */
1661 ph->fid_volatile, /* fid_volatile */
1662 state->mask, /* mask */
1663 max_trans); /* outbuf_len */
1664 if (subreq == NULL) {
1665 status = NT_STATUS_NO_MEMORY;
1666 goto fail;
1668 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1669 return NT_STATUS_RETRY;
1672 SMB_ASSERT(response->length > state->offset);
1674 finfo = talloc_zero(mem_ctx, struct file_info);
1675 if (finfo == NULL) {
1676 status = NT_STATUS_NO_MEMORY;
1677 goto fail;
1680 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1681 status = parse_finfo_posix_info(
1682 response->data + state->offset,
1683 response->length - state->offset,
1684 finfo,
1685 &next_offset);
1686 } else {
1687 status = parse_finfo_id_both_directory_info(
1688 response->data + state->offset,
1689 response->length - state->offset,
1690 finfo,
1691 &next_offset);
1693 if (!NT_STATUS_IS_OK(status)) {
1694 goto fail;
1697 status = is_bad_finfo_name(state->cli, finfo);
1698 if (!NT_STATUS_IS_OK(status)) {
1699 goto fail;
1703 * parse_finfo_id_both_directory_info() checks for overflow,
1704 * no need to check again here.
1706 state->offset += next_offset;
1708 if (next_offset == 0) {
1709 TALLOC_FREE(state->response);
1712 tevent_req_defer_callback(req, state->ev);
1713 tevent_req_notify_callback(req);
1715 *pfinfo = finfo;
1716 return NT_STATUS_OK;
1718 fail:
1719 TALLOC_FREE(finfo);
1720 tevent_req_received(req);
1721 return status;
1724 /***************************************************************
1725 Wrapper that allows SMB2 to query a path info (basic level).
1726 Synchronous only.
1727 ***************************************************************/
1729 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1730 const char *name,
1731 SMB_STRUCT_STAT *sbuf,
1732 uint32_t *attributes)
1734 NTSTATUS status;
1735 struct smb_create_returns cr;
1736 uint16_t fnum = 0xffff;
1737 size_t namelen = strlen(name);
1739 if (smbXcli_conn_has_async_calls(cli->conn)) {
1741 * Can't use sync call while an async call is in flight
1743 return NT_STATUS_INVALID_PARAMETER;
1746 /* SMB2 is pickier about pathnames. Ensure it doesn't
1747 end in a '\' */
1748 if (namelen > 0 && name[namelen-1] == '\\') {
1749 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1750 if (modname == NULL) {
1751 return NT_STATUS_NO_MEMORY;
1753 name = modname;
1756 /* This is commonly used as a 'cd'. Try qpathinfo on
1757 a directory handle first. */
1759 status = cli_smb2_create_fnum(cli,
1760 name,
1761 (struct cli_smb2_create_flags){0},
1762 SMB2_IMPERSONATION_IMPERSONATION,
1763 FILE_READ_ATTRIBUTES, /* desired_access */
1764 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1765 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1766 FILE_OPEN, /* create_disposition */
1767 FILE_DIRECTORY_FILE, /* create_options */
1768 NULL,
1769 &fnum,
1770 &cr,
1771 NULL,
1772 NULL);
1774 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1775 /* Maybe a file ? */
1776 status = cli_smb2_create_fnum(cli,
1777 name,
1778 (struct cli_smb2_create_flags){0},
1779 SMB2_IMPERSONATION_IMPERSONATION,
1780 FILE_READ_ATTRIBUTES, /* desired_access */
1781 0, /* file attributes */
1782 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1783 FILE_OPEN, /* create_disposition */
1784 0, /* create_options */
1785 NULL,
1786 &fnum,
1787 &cr,
1788 NULL,
1789 NULL);
1792 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1793 /* Maybe a reparse point ? */
1794 status = cli_smb2_create_fnum(cli,
1795 name,
1796 (struct cli_smb2_create_flags){0},
1797 SMB2_IMPERSONATION_IMPERSONATION,
1798 FILE_READ_ATTRIBUTES, /* desired_access */
1799 0, /* file attributes */
1800 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1801 FILE_OPEN, /* create_disposition */
1802 FILE_OPEN_REPARSE_POINT, /* create_options */
1803 NULL,
1804 &fnum,
1805 &cr,
1806 NULL,
1807 NULL);
1810 if (!NT_STATUS_IS_OK(status)) {
1811 return status;
1814 status = cli_smb2_close_fnum(cli, fnum);
1816 ZERO_STRUCTP(sbuf);
1818 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1819 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1820 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1821 sbuf->st_ex_size = cr.end_of_file;
1822 *attributes = cr.file_attributes;
1824 return status;
1827 struct cli_smb2_query_info_fnum_state {
1828 DATA_BLOB outbuf;
1831 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1833 struct tevent_req *cli_smb2_query_info_fnum_send(
1834 TALLOC_CTX *mem_ctx,
1835 struct tevent_context *ev,
1836 struct cli_state *cli,
1837 uint16_t fnum,
1838 uint8_t in_info_type,
1839 uint8_t in_info_class,
1840 uint32_t in_max_output_length,
1841 const DATA_BLOB *in_input_buffer,
1842 uint32_t in_additional_info,
1843 uint32_t in_flags)
1845 struct tevent_req *req = NULL, *subreq = NULL;
1846 struct cli_smb2_query_info_fnum_state *state = NULL;
1847 struct smb2_hnd *ph = NULL;
1848 NTSTATUS status;
1850 req = tevent_req_create(
1851 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1852 if (req == NULL) {
1853 return req;
1856 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1857 if (tevent_req_nterror(req, status)) {
1858 return tevent_req_post(req, ev);
1861 subreq = smb2cli_query_info_send(
1862 state,
1864 cli->conn,
1865 cli->timeout,
1866 cli->smb2.session,
1867 cli->smb2.tcon,
1868 in_info_type,
1869 in_info_class,
1870 in_max_output_length,
1871 in_input_buffer,
1872 in_additional_info,
1873 in_flags,
1874 ph->fid_persistent,
1875 ph->fid_volatile);
1876 if (tevent_req_nomem(subreq, req)) {
1877 return tevent_req_post(req, ev);
1879 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1880 return req;
1883 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1885 struct tevent_req *req = tevent_req_callback_data(
1886 subreq, struct tevent_req);
1887 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1888 req, struct cli_smb2_query_info_fnum_state);
1889 DATA_BLOB outbuf;
1890 NTSTATUS status;
1892 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1893 TALLOC_FREE(subreq);
1894 if (tevent_req_nterror(req, status)) {
1895 return;
1899 * We have to dup the memory here because outbuf.data is not
1900 * returned as a talloc object by smb2cli_query_info_recv.
1901 * It's a pointer into the received buffer.
1903 state->outbuf = data_blob_dup_talloc(state, outbuf);
1905 if ((outbuf.length != 0) &&
1906 tevent_req_nomem(state->outbuf.data, req)) {
1907 return;
1909 tevent_req_done(req);
1912 NTSTATUS cli_smb2_query_info_fnum_recv(
1913 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1915 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1916 req, struct cli_smb2_query_info_fnum_state);
1917 NTSTATUS status;
1919 if (tevent_req_is_nterror(req, &status)) {
1920 return status;
1922 *outbuf = (DATA_BLOB) {
1923 .data = talloc_move(mem_ctx, &state->outbuf.data),
1924 .length = state->outbuf.length,
1926 return NT_STATUS_OK;
1929 NTSTATUS cli_smb2_query_info_fnum(
1930 struct cli_state *cli,
1931 uint16_t fnum,
1932 uint8_t in_info_type,
1933 uint8_t in_info_class,
1934 uint32_t in_max_output_length,
1935 const DATA_BLOB *in_input_buffer,
1936 uint32_t in_additional_info,
1937 uint32_t in_flags,
1938 TALLOC_CTX *mem_ctx,
1939 DATA_BLOB *outbuf)
1941 TALLOC_CTX *frame = talloc_stackframe();
1942 struct tevent_context *ev = NULL;
1943 struct tevent_req *req = NULL;
1944 NTSTATUS status = NT_STATUS_NO_MEMORY;
1945 bool ok;
1947 if (smbXcli_conn_has_async_calls(cli->conn)) {
1949 * Can't use sync call while an async call is in flight
1951 status = NT_STATUS_INVALID_PARAMETER;
1952 goto fail;
1954 ev = samba_tevent_context_init(frame);
1955 if (ev == NULL) {
1956 goto fail;
1958 req = cli_smb2_query_info_fnum_send(
1959 frame,
1961 cli,
1962 fnum,
1963 in_info_type,
1964 in_info_class,
1965 in_max_output_length,
1966 in_input_buffer,
1967 in_additional_info,
1968 in_flags);
1969 if (req == NULL) {
1970 goto fail;
1972 ok = tevent_req_poll_ntstatus(req, ev, &status);
1973 if (!ok) {
1974 goto fail;
1976 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1977 fail:
1978 TALLOC_FREE(frame);
1979 return status;
1982 /***************************************************************
1983 Helper function for pathname operations.
1984 ***************************************************************/
1986 struct get_fnum_from_path_state {
1987 struct tevent_context *ev;
1988 struct cli_state *cli;
1989 const char *name;
1990 uint32_t desired_access;
1991 uint16_t fnum;
1994 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1995 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1996 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1998 static struct tevent_req *get_fnum_from_path_send(
1999 TALLOC_CTX *mem_ctx,
2000 struct tevent_context *ev,
2001 struct cli_state *cli,
2002 const char *name,
2003 uint32_t desired_access)
2005 struct tevent_req *req = NULL, *subreq = NULL;
2006 struct get_fnum_from_path_state *state = NULL;
2007 size_t namelen = strlen(name);
2009 req = tevent_req_create(
2010 mem_ctx, &state, struct get_fnum_from_path_state);
2011 if (req == NULL) {
2012 return NULL;
2014 state->ev = ev;
2015 state->cli = cli;
2016 state->name = name;
2017 state->desired_access = desired_access;
2020 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2021 * '\'
2023 if (namelen > 0 && name[namelen-1] == '\\') {
2024 state->name = talloc_strndup(state, name, namelen-1);
2025 if (tevent_req_nomem(state->name, req)) {
2026 return tevent_req_post(req, ev);
2030 subreq = cli_smb2_create_fnum_send(
2031 state, /* mem_ctx, */
2032 ev, /* ev */
2033 cli, /* cli */
2034 state->name, /* fname */
2035 (struct cli_smb2_create_flags){0}, /* create_flags */
2036 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2037 desired_access, /* desired_access */
2038 0, /* file_attributes */
2039 FILE_SHARE_READ|
2040 FILE_SHARE_WRITE|
2041 FILE_SHARE_DELETE, /* share_access */
2042 FILE_OPEN, /* create_disposition */
2043 0, /* create_options */
2044 NULL); /* in_cblobs */
2045 if (tevent_req_nomem(subreq, req)) {
2046 return tevent_req_post(req, ev);
2048 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2049 return req;
2052 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2054 struct tevent_req *req = tevent_req_callback_data(
2055 subreq, struct tevent_req);
2056 struct get_fnum_from_path_state *state = tevent_req_data(
2057 req, struct get_fnum_from_path_state);
2058 NTSTATUS status;
2060 status = cli_smb2_create_fnum_recv(
2061 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2062 TALLOC_FREE(subreq);
2064 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2065 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2067 * Naive option to match our SMB1 code. Assume the
2068 * symlink path that tripped us up was the last
2069 * component and try again. Eventually we will have to
2070 * deal with the returned path unprocessed component. JRA.
2072 subreq = cli_smb2_create_fnum_send(
2073 state, /* mem_ctx, */
2074 state->ev, /* ev */
2075 state->cli, /* cli */
2076 state->name, /* fname */
2077 (struct cli_smb2_create_flags){0}, /* create_flags */
2078 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2079 state->desired_access, /* desired_access */
2080 0, /* file_attributes */
2081 FILE_SHARE_READ|
2082 FILE_SHARE_WRITE|
2083 FILE_SHARE_DELETE, /* share_access */
2084 FILE_OPEN, /* create_disposition */
2085 FILE_OPEN_REPARSE_POINT, /* create_options */
2086 NULL); /* in_cblobs */
2087 if (tevent_req_nomem(subreq, req)) {
2088 return;
2090 tevent_req_set_callback(
2091 subreq, get_fnum_from_path_opened_reparse, req);
2092 return;
2095 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2096 subreq = cli_smb2_create_fnum_send(
2097 state, /* mem_ctx, */
2098 state->ev, /* ev */
2099 state->cli, /* cli */
2100 state->name, /* fname */
2101 (struct cli_smb2_create_flags){0}, /* create_flags */
2102 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2103 state->desired_access, /* desired_access */
2104 0, /* file_attributes */
2105 FILE_SHARE_READ|
2106 FILE_SHARE_WRITE|
2107 FILE_SHARE_DELETE, /* share_access */
2108 FILE_OPEN, /* create_disposition */
2109 FILE_DIRECTORY_FILE, /* create_options */
2110 NULL); /* in_cblobs */
2111 if (tevent_req_nomem(subreq, req)) {
2112 return;
2114 tevent_req_set_callback(
2115 subreq, get_fnum_from_path_opened_dir, req);
2116 return;
2119 if (tevent_req_nterror(req, status)) {
2120 return;
2122 tevent_req_done(req);
2125 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2127 struct tevent_req *req = tevent_req_callback_data(
2128 subreq, struct tevent_req);
2129 struct get_fnum_from_path_state *state = tevent_req_data(
2130 req, struct get_fnum_from_path_state);
2131 NTSTATUS status = cli_smb2_create_fnum_recv(
2132 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2133 tevent_req_simple_finish_ntstatus(subreq, status);
2136 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2138 /* Abstraction violation, but these two are just the same... */
2139 get_fnum_from_path_opened_reparse(subreq);
2142 static NTSTATUS get_fnum_from_path_recv(
2143 struct tevent_req *req, uint16_t *pfnum)
2145 struct get_fnum_from_path_state *state = tevent_req_data(
2146 req, struct get_fnum_from_path_state);
2147 NTSTATUS status = NT_STATUS_OK;
2149 if (!tevent_req_is_nterror(req, &status)) {
2150 *pfnum = state->fnum;
2152 tevent_req_received(req);
2153 return status;
2156 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2157 const char *name,
2158 uint32_t desired_access,
2159 uint16_t *pfnum)
2161 TALLOC_CTX *frame = talloc_stackframe();
2162 struct tevent_context *ev = NULL;
2163 struct tevent_req *req = NULL;
2164 NTSTATUS status = NT_STATUS_NO_MEMORY;
2166 if (smbXcli_conn_has_async_calls(cli->conn)) {
2167 status = NT_STATUS_INVALID_PARAMETER;
2168 goto fail;
2170 ev = samba_tevent_context_init(frame);
2171 if (ev == NULL) {
2172 goto fail;
2174 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2175 if (req == NULL) {
2176 goto fail;
2178 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2179 goto fail;
2181 status = get_fnum_from_path_recv(req, pfnum);
2182 fail:
2183 TALLOC_FREE(frame);
2184 return status;
2187 struct cli_smb2_qpathinfo_state {
2188 struct tevent_context *ev;
2189 struct cli_state *cli;
2190 const char *fname;
2191 uint16_t fnum;
2192 uint16_t level;
2193 uint32_t min_rdata;
2194 uint32_t max_rdata;
2196 NTSTATUS status;
2197 DATA_BLOB out;
2200 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2201 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2202 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2204 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2205 struct tevent_context *ev,
2206 struct cli_state *cli,
2207 const char *fname,
2208 uint16_t level,
2209 uint32_t min_rdata,
2210 uint32_t max_rdata)
2212 struct tevent_req *req = NULL, *subreq = NULL;
2213 struct cli_smb2_qpathinfo_state *state = NULL;
2215 req = tevent_req_create(mem_ctx,
2216 &state,
2217 struct cli_smb2_qpathinfo_state);
2218 if (req == NULL) {
2219 return NULL;
2221 state->ev = ev;
2222 state->cli = cli;
2223 state->level = level;
2224 state->min_rdata = min_rdata;
2225 state->max_rdata = max_rdata;
2227 subreq = get_fnum_from_path_send(state,
2229 cli,
2230 fname,
2231 FILE_READ_ATTRIBUTES);
2232 if (tevent_req_nomem(subreq, req)) {
2233 return tevent_req_post(req, ev);
2235 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2236 return req;
2239 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2241 struct tevent_req *req =
2242 tevent_req_callback_data(subreq, struct tevent_req);
2243 struct cli_smb2_qpathinfo_state *state =
2244 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2245 NTSTATUS status;
2247 status = get_fnum_from_path_recv(subreq, &state->fnum);
2248 TALLOC_FREE(subreq);
2249 if (tevent_req_nterror(req, status)) {
2250 return;
2253 subreq = cli_smb2_query_info_fnum_send(state,
2254 state->ev,
2255 state->cli,
2256 state->fnum,
2257 1, /* in_info_type */
2258 state->level,
2259 state->max_rdata,
2260 NULL, /* in_input_buffer */
2261 0, /* in_additional_info */
2262 0); /* in_flags */
2263 if (tevent_req_nomem(subreq, req)) {
2264 return;
2266 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2269 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2271 struct tevent_req *req =
2272 tevent_req_callback_data(subreq, struct tevent_req);
2273 struct cli_smb2_qpathinfo_state *state =
2274 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2276 state->status =
2277 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2278 TALLOC_FREE(subreq);
2280 if (NT_STATUS_IS_OK(state->status) &&
2281 (state->out.length < state->min_rdata)) {
2282 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2285 subreq = cli_smb2_close_fnum_send(state,
2286 state->ev,
2287 state->cli,
2288 state->fnum,
2290 if (tevent_req_nomem(subreq, req)) {
2291 return;
2293 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2296 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2298 struct tevent_req *req =
2299 tevent_req_callback_data(subreq, struct tevent_req);
2300 struct cli_smb2_qpathinfo_state *state =
2301 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2302 NTSTATUS status;
2304 status = cli_smb2_close_fnum_recv(subreq);
2305 TALLOC_FREE(subreq);
2306 if (tevent_req_nterror(req, status)) {
2307 return;
2309 if (tevent_req_nterror(req, state->status)) {
2310 return;
2312 tevent_req_done(req);
2315 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2316 TALLOC_CTX *mem_ctx,
2317 uint8_t **rdata,
2318 uint32_t *num_rdata)
2320 struct cli_smb2_qpathinfo_state *state =
2321 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2322 NTSTATUS status;
2324 if (tevent_req_is_nterror(req, &status)) {
2325 return status;
2328 *rdata = talloc_move(mem_ctx, &state->out.data);
2329 *num_rdata = state->out.length;
2330 tevent_req_received(req);
2331 return NT_STATUS_OK;
2334 /***************************************************************
2335 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2336 a pathname.
2337 Synchronous only.
2338 ***************************************************************/
2340 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2341 const char *name,
2342 uint8_t in_info_type,
2343 uint8_t in_file_info_class,
2344 const DATA_BLOB *p_in_data)
2346 NTSTATUS status;
2347 uint16_t fnum = 0xffff;
2348 TALLOC_CTX *frame = talloc_stackframe();
2350 if (smbXcli_conn_has_async_calls(cli->conn)) {
2352 * Can't use sync call while an async call is in flight
2354 status = NT_STATUS_INVALID_PARAMETER;
2355 goto fail;
2358 status = get_fnum_from_path(cli,
2359 name,
2360 FILE_WRITE_ATTRIBUTES,
2361 &fnum);
2363 if (!NT_STATUS_IS_OK(status)) {
2364 goto fail;
2367 status = cli_smb2_set_info_fnum(
2368 cli,
2369 fnum,
2370 in_info_type,
2371 in_file_info_class,
2372 p_in_data, /* in_input_buffer */
2373 0); /* in_additional_info */
2374 fail:
2376 if (fnum != 0xffff) {
2377 cli_smb2_close_fnum(cli, fnum);
2380 cli->raw_status = status;
2382 TALLOC_FREE(frame);
2383 return status;
2387 /***************************************************************
2388 Wrapper that allows SMB2 to set pathname attributes.
2389 Synchronous only.
2390 ***************************************************************/
2392 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2393 const char *name,
2394 uint32_t attr,
2395 time_t mtime)
2397 uint8_t inbuf_store[40];
2398 DATA_BLOB inbuf = data_blob_null;
2400 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2401 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2403 inbuf.data = inbuf_store;
2404 inbuf.length = sizeof(inbuf_store);
2405 data_blob_clear(&inbuf);
2408 * SMB1 uses attr == 0 to clear all attributes
2409 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2410 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2411 * request attribute change.
2413 * SMB2 uses exactly the reverse. Unfortunately as the
2414 * cli_setatr() ABI is exposed inside libsmbclient,
2415 * we must make the SMB2 cli_smb2_setatr() call
2416 * export the same ABI as the SMB1 cli_setatr()
2417 * which calls it. This means reversing the sense
2418 * of the requested attr argument if it's zero
2419 * or FILE_ATTRIBUTE_NORMAL.
2421 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2424 if (attr == 0) {
2425 attr = FILE_ATTRIBUTE_NORMAL;
2426 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2427 attr = 0;
2430 SIVAL(inbuf.data, 32, attr);
2431 if (mtime != 0) {
2432 put_long_date((char *)inbuf.data + 16,mtime);
2434 /* Set all the other times to -1. */
2435 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2436 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2437 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2439 return cli_smb2_setpathinfo(cli,
2440 name,
2441 1, /* in_info_type */
2442 /* in_file_info_class */
2443 SMB_FILE_BASIC_INFORMATION - 1000,
2444 &inbuf);
2448 /***************************************************************
2449 Wrapper that allows SMB2 to set file handle times.
2450 Synchronous only.
2451 ***************************************************************/
2453 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2454 uint16_t fnum,
2455 time_t change_time,
2456 time_t access_time,
2457 time_t write_time)
2459 uint8_t inbuf_store[40];
2460 DATA_BLOB inbuf = data_blob_null;
2461 NTSTATUS status;
2463 if (smbXcli_conn_has_async_calls(cli->conn)) {
2465 * Can't use sync call while an async call is in flight
2467 return NT_STATUS_INVALID_PARAMETER;
2470 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2471 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2473 inbuf.data = inbuf_store;
2474 inbuf.length = sizeof(inbuf_store);
2475 data_blob_clear(&inbuf);
2477 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2478 if (change_time != 0) {
2479 put_long_date((char *)inbuf.data + 24, change_time);
2481 if (access_time != 0) {
2482 put_long_date((char *)inbuf.data + 8, access_time);
2484 if (write_time != 0) {
2485 put_long_date((char *)inbuf.data + 16, write_time);
2488 status = cli_smb2_set_info_fnum(cli,
2489 fnum,
2490 1, /* in_info_type */
2491 SMB_FILE_BASIC_INFORMATION -
2492 1000, /* in_file_info_class */
2493 &inbuf, /* in_input_buffer */
2494 0); /* in_additional_info */
2495 cli->raw_status = status;
2496 return status;
2499 /***************************************************************
2500 Wrapper that allows SMB2 to query disk attributes (size).
2501 Synchronous only.
2502 ***************************************************************/
2504 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2505 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2507 NTSTATUS status;
2508 uint16_t fnum = 0xffff;
2509 DATA_BLOB outbuf = data_blob_null;
2510 uint32_t sectors_per_unit = 0;
2511 uint32_t bytes_per_sector = 0;
2512 uint64_t total_size = 0;
2513 uint64_t size_free = 0;
2514 TALLOC_CTX *frame = talloc_stackframe();
2516 if (smbXcli_conn_has_async_calls(cli->conn)) {
2518 * Can't use sync call while an async call is in flight
2520 status = NT_STATUS_INVALID_PARAMETER;
2521 goto fail;
2524 /* First open the top level directory. */
2525 status = cli_smb2_create_fnum(cli,
2526 path,
2527 (struct cli_smb2_create_flags){0},
2528 SMB2_IMPERSONATION_IMPERSONATION,
2529 FILE_READ_ATTRIBUTES, /* desired_access */
2530 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2531 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2532 FILE_OPEN, /* create_disposition */
2533 FILE_DIRECTORY_FILE, /* create_options */
2534 NULL,
2535 &fnum,
2536 NULL,
2537 NULL,
2538 NULL);
2540 if (!NT_STATUS_IS_OK(status)) {
2541 goto fail;
2544 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2545 level 3 (SMB_FS_SIZE_INFORMATION). */
2547 status = cli_smb2_query_info_fnum(
2548 cli,
2549 fnum,
2550 2, /* in_info_type */
2551 3, /* in_file_info_class */
2552 0xFFFF, /* in_max_output_length */
2553 NULL, /* in_input_buffer */
2554 0, /* in_additional_info */
2555 0, /* in_flags */
2556 frame,
2557 &outbuf);
2558 if (!NT_STATUS_IS_OK(status)) {
2559 goto fail;
2562 /* Parse the reply. */
2563 if (outbuf.length != 24) {
2564 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2565 goto fail;
2568 total_size = BVAL(outbuf.data, 0);
2569 size_free = BVAL(outbuf.data, 8);
2570 sectors_per_unit = IVAL(outbuf.data, 16);
2571 bytes_per_sector = IVAL(outbuf.data, 20);
2573 if (bsize) {
2574 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2576 if (total) {
2577 *total = total_size;
2579 if (avail) {
2580 *avail = size_free;
2583 status = NT_STATUS_OK;
2585 fail:
2587 if (fnum != 0xffff) {
2588 cli_smb2_close_fnum(cli, fnum);
2591 cli->raw_status = status;
2593 TALLOC_FREE(frame);
2594 return status;
2597 /***************************************************************
2598 Wrapper that allows SMB2 to query file system sizes.
2599 Synchronous only.
2600 ***************************************************************/
2602 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2603 uint64_t *total_allocation_units,
2604 uint64_t *caller_allocation_units,
2605 uint64_t *actual_allocation_units,
2606 uint64_t *sectors_per_allocation_unit,
2607 uint64_t *bytes_per_sector)
2609 NTSTATUS status;
2610 uint16_t fnum = 0xffff;
2611 DATA_BLOB outbuf = data_blob_null;
2612 TALLOC_CTX *frame = talloc_stackframe();
2614 if (smbXcli_conn_has_async_calls(cli->conn)) {
2616 * Can't use sync call while an async call is in flight
2618 status = NT_STATUS_INVALID_PARAMETER;
2619 goto fail;
2622 /* First open the top level directory. */
2623 status =
2624 cli_smb2_create_fnum(cli, "",
2625 (struct cli_smb2_create_flags){0},
2626 SMB2_IMPERSONATION_IMPERSONATION,
2627 FILE_READ_ATTRIBUTES, /* desired_access */
2628 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2629 FILE_SHARE_READ | FILE_SHARE_WRITE |
2630 FILE_SHARE_DELETE, /* share_access */
2631 FILE_OPEN, /* create_disposition */
2632 FILE_DIRECTORY_FILE, /* create_options */
2633 NULL,
2634 &fnum,
2635 NULL,
2636 NULL,
2637 NULL);
2639 if (!NT_STATUS_IS_OK(status)) {
2640 goto fail;
2643 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2644 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2646 status = cli_smb2_query_info_fnum(
2647 cli,
2648 fnum,
2649 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2650 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2651 0xFFFF, /* in_max_output_length */
2652 NULL, /* in_input_buffer */
2653 0, /* in_additional_info */
2654 0, /* in_flags */
2655 frame,
2656 &outbuf);
2657 if (!NT_STATUS_IS_OK(status)) {
2658 goto fail;
2661 if (outbuf.length < 32) {
2662 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2663 goto fail;
2666 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2667 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2668 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2669 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2670 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2672 fail:
2674 if (fnum != 0xffff) {
2675 cli_smb2_close_fnum(cli, fnum);
2678 cli->raw_status = status;
2680 TALLOC_FREE(frame);
2681 return status;
2684 /***************************************************************
2685 Wrapper that allows SMB2 to query file system attributes.
2686 Synchronous only.
2687 ***************************************************************/
2689 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2691 NTSTATUS status;
2692 uint16_t fnum = 0xffff;
2693 DATA_BLOB outbuf = data_blob_null;
2694 TALLOC_CTX *frame = talloc_stackframe();
2696 if (smbXcli_conn_has_async_calls(cli->conn)) {
2698 * Can't use sync call while an async call is in flight
2700 status = NT_STATUS_INVALID_PARAMETER;
2701 goto fail;
2704 /* First open the top level directory. */
2705 status =
2706 cli_smb2_create_fnum(cli, "",
2707 (struct cli_smb2_create_flags){0},
2708 SMB2_IMPERSONATION_IMPERSONATION,
2709 FILE_READ_ATTRIBUTES, /* desired_access */
2710 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2711 FILE_SHARE_READ | FILE_SHARE_WRITE |
2712 FILE_SHARE_DELETE, /* share_access */
2713 FILE_OPEN, /* create_disposition */
2714 FILE_DIRECTORY_FILE, /* create_options */
2715 NULL,
2716 &fnum,
2717 NULL,
2718 NULL,
2719 NULL);
2721 if (!NT_STATUS_IS_OK(status)) {
2722 goto fail;
2725 status = cli_smb2_query_info_fnum(
2726 cli,
2727 fnum,
2728 2, /* in_info_type */
2729 5, /* in_file_info_class */
2730 0xFFFF, /* in_max_output_length */
2731 NULL, /* in_input_buffer */
2732 0, /* in_additional_info */
2733 0, /* in_flags */
2734 frame,
2735 &outbuf);
2736 if (!NT_STATUS_IS_OK(status)) {
2737 goto fail;
2740 if (outbuf.length < 12) {
2741 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2742 goto fail;
2745 *fs_attr = IVAL(outbuf.data, 0);
2747 fail:
2749 if (fnum != 0xffff) {
2750 cli_smb2_close_fnum(cli, fnum);
2753 cli->raw_status = status;
2755 TALLOC_FREE(frame);
2756 return status;
2759 /***************************************************************
2760 Wrapper that allows SMB2 to query file system volume info.
2761 Synchronous only.
2762 ***************************************************************/
2764 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2765 TALLOC_CTX *mem_ctx,
2766 char **_volume_name,
2767 uint32_t *pserial_number,
2768 time_t *pdate)
2770 NTSTATUS status;
2771 uint16_t fnum = 0xffff;
2772 DATA_BLOB outbuf = data_blob_null;
2773 uint32_t nlen;
2774 char *volume_name = NULL;
2775 TALLOC_CTX *frame = talloc_stackframe();
2777 if (smbXcli_conn_has_async_calls(cli->conn)) {
2779 * Can't use sync call while an async call is in flight
2781 status = NT_STATUS_INVALID_PARAMETER;
2782 goto fail;
2785 /* First open the top level directory. */
2786 status =
2787 cli_smb2_create_fnum(cli, "",
2788 (struct cli_smb2_create_flags){0},
2789 SMB2_IMPERSONATION_IMPERSONATION,
2790 FILE_READ_ATTRIBUTES, /* desired_access */
2791 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2792 FILE_SHARE_READ | FILE_SHARE_WRITE |
2793 FILE_SHARE_DELETE, /* share_access */
2794 FILE_OPEN, /* create_disposition */
2795 FILE_DIRECTORY_FILE, /* create_options */
2796 NULL,
2797 &fnum,
2798 NULL,
2799 NULL,
2800 NULL);
2802 if (!NT_STATUS_IS_OK(status)) {
2803 goto fail;
2806 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2807 level 1 (SMB_FS_VOLUME_INFORMATION). */
2809 status = cli_smb2_query_info_fnum(
2810 cli,
2811 fnum,
2812 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2813 /* in_file_info_class */
2814 SMB_FS_VOLUME_INFORMATION - 1000,
2815 0xFFFF, /* in_max_output_length */
2816 NULL, /* in_input_buffer */
2817 0, /* in_additional_info */
2818 0, /* in_flags */
2819 frame,
2820 &outbuf);
2821 if (!NT_STATUS_IS_OK(status)) {
2822 goto fail;
2825 if (outbuf.length < 24) {
2826 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2827 goto fail;
2830 if (pdate) {
2831 struct timespec ts;
2832 ts = interpret_long_date(BVAL(outbuf.data, 0));
2833 *pdate = ts.tv_sec;
2835 if (pserial_number) {
2836 *pserial_number = IVAL(outbuf.data,8);
2838 nlen = IVAL(outbuf.data,12);
2839 if (nlen + 18 < 18) {
2840 /* Integer wrap. */
2841 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2842 goto fail;
2845 * The next check is safe as we know outbuf.length >= 24
2846 * from above.
2848 if (nlen > (outbuf.length - 18)) {
2849 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2850 goto fail;
2853 pull_string_talloc(mem_ctx,
2854 (const char *)outbuf.data,
2856 &volume_name,
2857 outbuf.data + 18,
2858 nlen,
2859 STR_UNICODE);
2860 if (volume_name == NULL) {
2861 status = map_nt_error_from_unix(errno);
2862 goto fail;
2865 *_volume_name = volume_name;
2867 fail:
2869 if (fnum != 0xffff) {
2870 cli_smb2_close_fnum(cli, fnum);
2873 cli->raw_status = status;
2875 TALLOC_FREE(frame);
2876 return status;
2879 struct cli_smb2_mxac_state {
2880 struct tevent_context *ev;
2881 struct cli_state *cli;
2882 const char *fname;
2883 struct smb2_create_blobs in_cblobs;
2884 uint16_t fnum;
2885 NTSTATUS status;
2886 uint32_t mxac;
2889 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2890 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2892 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2893 struct tevent_context *ev,
2894 struct cli_state *cli,
2895 const char *fname)
2897 struct tevent_req *req = NULL, *subreq = NULL;
2898 struct cli_smb2_mxac_state *state = NULL;
2899 NTSTATUS status;
2901 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2902 if (req == NULL) {
2903 return NULL;
2905 *state = (struct cli_smb2_mxac_state) {
2906 .ev = ev,
2907 .cli = cli,
2908 .fname = fname,
2911 status = smb2_create_blob_add(state,
2912 &state->in_cblobs,
2913 SMB2_CREATE_TAG_MXAC,
2914 data_blob(NULL, 0));
2915 if (tevent_req_nterror(req, status)) {
2916 return tevent_req_post(req, ev);
2919 subreq = cli_smb2_create_fnum_send(
2920 state,
2921 state->ev,
2922 state->cli,
2923 state->fname,
2924 (struct cli_smb2_create_flags){0},
2925 SMB2_IMPERSONATION_IMPERSONATION,
2926 FILE_READ_ATTRIBUTES,
2927 0, /* file attributes */
2928 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2929 FILE_OPEN,
2930 0, /* create_options */
2931 &state->in_cblobs);
2932 if (tevent_req_nomem(subreq, req)) {
2933 return tevent_req_post(req, ev);
2935 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2936 return req;
2939 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2941 struct tevent_req *req = tevent_req_callback_data(
2942 subreq, struct tevent_req);
2943 struct cli_smb2_mxac_state *state = tevent_req_data(
2944 req, struct cli_smb2_mxac_state);
2945 struct smb2_create_blobs out_cblobs = {0};
2946 struct smb2_create_blob *mxac_blob = NULL;
2947 NTSTATUS status;
2949 status = cli_smb2_create_fnum_recv(
2950 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
2951 TALLOC_FREE(subreq);
2953 if (tevent_req_nterror(req, status)) {
2954 return;
2957 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
2958 if (mxac_blob == NULL) {
2959 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2960 goto close;
2962 if (mxac_blob->data.length != 8) {
2963 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2964 goto close;
2967 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
2968 state->mxac = IVAL(mxac_blob->data.data, 4);
2970 close:
2971 subreq = cli_smb2_close_fnum_send(state,
2972 state->ev,
2973 state->cli,
2974 state->fnum,
2976 if (tevent_req_nomem(subreq, req)) {
2977 return;
2979 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
2981 return;
2984 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
2986 struct tevent_req *req = tevent_req_callback_data(
2987 subreq, struct tevent_req);
2988 NTSTATUS status;
2990 status = cli_smb2_close_fnum_recv(subreq);
2991 if (tevent_req_nterror(req, status)) {
2992 return;
2995 tevent_req_done(req);
2998 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3000 struct cli_smb2_mxac_state *state = tevent_req_data(
3001 req, struct cli_smb2_mxac_state);
3002 NTSTATUS status;
3004 if (tevent_req_is_nterror(req, &status)) {
3005 return status;
3008 if (!NT_STATUS_IS_OK(state->status)) {
3009 return state->status;
3012 *mxac = state->mxac;
3013 return NT_STATUS_OK;
3016 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3017 const char *fname,
3018 uint32_t *_mxac)
3020 TALLOC_CTX *frame = talloc_stackframe();
3021 struct tevent_context *ev = NULL;
3022 struct tevent_req *req = NULL;
3023 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3024 bool ok;
3026 if (smbXcli_conn_has_async_calls(cli->conn)) {
3028 * Can't use sync call while an async call is in flight
3030 status = NT_STATUS_INVALID_PARAMETER;
3031 goto fail;
3034 ev = samba_tevent_context_init(frame);
3035 if (ev == NULL) {
3036 goto fail;
3038 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3039 if (req == NULL) {
3040 goto fail;
3042 ok = tevent_req_poll_ntstatus(req, ev, &status);
3043 if (!ok) {
3044 goto fail;
3046 status = cli_smb2_query_mxac_recv(req, _mxac);
3048 fail:
3049 cli->raw_status = status;
3050 TALLOC_FREE(frame);
3051 return status;
3054 struct cli_smb2_rename_fnum_state {
3055 DATA_BLOB inbuf;
3058 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3060 static struct tevent_req *cli_smb2_rename_fnum_send(
3061 TALLOC_CTX *mem_ctx,
3062 struct tevent_context *ev,
3063 struct cli_state *cli,
3064 uint16_t fnum,
3065 const char *fname_dst,
3066 bool replace)
3068 struct tevent_req *req = NULL, *subreq = NULL;
3069 struct cli_smb2_rename_fnum_state *state = NULL;
3070 size_t namelen = strlen(fname_dst);
3071 smb_ucs2_t *converted_str = NULL;
3072 size_t converted_size_bytes = 0;
3073 size_t inbuf_size;
3074 bool ok;
3076 req = tevent_req_create(
3077 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3078 if (req == NULL) {
3079 return NULL;
3083 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3084 * a '\'
3086 if (*fname_dst == '\\') {
3087 fname_dst++;
3091 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3092 * '\'
3094 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3095 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3096 if (tevent_req_nomem(fname_dst, req)) {
3097 return tevent_req_post(req, ev);
3101 ok = push_ucs2_talloc(
3102 state, &converted_str, fname_dst, &converted_size_bytes);
3103 if (!ok) {
3104 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3105 return tevent_req_post(req, ev);
3109 * W2K8 insists the dest name is not null terminated. Remove
3110 * the last 2 zero bytes and reduce the name length.
3112 if (converted_size_bytes < 2) {
3113 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3114 return tevent_req_post(req, ev);
3116 converted_size_bytes -= 2;
3118 inbuf_size = 20 + converted_size_bytes;
3119 if (inbuf_size < 20) {
3120 /* Integer wrap check. */
3121 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3122 return tevent_req_post(req, ev);
3126 * The Windows 10 SMB2 server has a minimum length
3127 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3128 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3129 * if the length is less. This isn't an alignment
3130 * issue as Windows client accepts happily 2-byte align
3131 * for larger target name sizes. Also the Windows 10
3132 * SMB1 server doesn't have this restriction.
3134 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3136 inbuf_size = MAX(inbuf_size, 24);
3138 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3139 if (tevent_req_nomem(state->inbuf.data, req)) {
3140 return tevent_req_post(req, ev);
3143 if (replace) {
3144 SCVAL(state->inbuf.data, 0, 1);
3147 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3148 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3150 TALLOC_FREE(converted_str);
3152 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3153 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3155 subreq = cli_smb2_set_info_fnum_send(
3156 state, /* mem_ctx */
3157 ev, /* ev */
3158 cli, /* cli */
3159 fnum, /* fnum */
3160 1, /* in_info_type */
3161 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3162 &state->inbuf, /* in_input_buffer */
3163 0); /* in_additional_info */
3164 if (tevent_req_nomem(subreq, req)) {
3165 return tevent_req_post(req, ev);
3167 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3168 return req;
3171 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3173 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3174 tevent_req_simple_finish_ntstatus(subreq, status);
3177 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3179 return tevent_req_simple_recv_ntstatus(req);
3182 /***************************************************************
3183 Wrapper that allows SMB2 to rename a file.
3184 ***************************************************************/
3186 struct cli_smb2_rename_state {
3187 struct tevent_context *ev;
3188 struct cli_state *cli;
3189 const char *fname_dst;
3190 bool replace;
3191 uint16_t fnum;
3193 NTSTATUS rename_status;
3196 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3197 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3198 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3200 struct tevent_req *cli_smb2_rename_send(
3201 TALLOC_CTX *mem_ctx,
3202 struct tevent_context *ev,
3203 struct cli_state *cli,
3204 const char *fname_src,
3205 const char *fname_dst,
3206 bool replace)
3208 struct tevent_req *req = NULL, *subreq = NULL;
3209 struct cli_smb2_rename_state *state = NULL;
3210 NTSTATUS status;
3212 req = tevent_req_create(
3213 mem_ctx, &state, struct cli_smb2_rename_state);
3214 if (req == NULL) {
3215 return NULL;
3219 * Strip a MSDFS path from fname_dst if we were given one.
3221 status = cli_dfs_target_check(state,
3222 cli,
3223 fname_dst,
3224 &fname_dst);
3225 if (tevent_req_nterror(req, status)) {
3226 return tevent_req_post(req, ev);
3229 state->ev = ev;
3230 state->cli = cli;
3231 state->fname_dst = fname_dst;
3232 state->replace = replace;
3234 subreq = get_fnum_from_path_send(
3235 state, ev, cli, fname_src, DELETE_ACCESS);
3236 if (tevent_req_nomem(subreq, req)) {
3237 return tevent_req_post(req, ev);
3239 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3240 return req;
3243 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3245 struct tevent_req *req = tevent_req_callback_data(
3246 subreq, struct tevent_req);
3247 struct cli_smb2_rename_state *state = tevent_req_data(
3248 req, struct cli_smb2_rename_state);
3249 NTSTATUS status;
3251 status = get_fnum_from_path_recv(subreq, &state->fnum);
3252 TALLOC_FREE(subreq);
3253 if (tevent_req_nterror(req, status)) {
3254 return;
3257 subreq = cli_smb2_rename_fnum_send(
3258 state,
3259 state->ev,
3260 state->cli,
3261 state->fnum,
3262 state->fname_dst,
3263 state->replace);
3264 if (tevent_req_nomem(subreq, req)) {
3265 return;
3267 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3270 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3272 struct tevent_req *req = tevent_req_callback_data(
3273 subreq, struct tevent_req);
3274 struct cli_smb2_rename_state *state = tevent_req_data(
3275 req, struct cli_smb2_rename_state);
3277 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3278 TALLOC_FREE(subreq);
3280 subreq = cli_smb2_close_fnum_send(state,
3281 state->ev,
3282 state->cli,
3283 state->fnum,
3285 if (tevent_req_nomem(subreq, req)) {
3286 return;
3288 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3291 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3293 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3294 tevent_req_simple_finish_ntstatus(subreq, status);
3297 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3299 struct cli_smb2_rename_state *state = tevent_req_data(
3300 req, struct cli_smb2_rename_state);
3301 NTSTATUS status = NT_STATUS_OK;
3303 if (!tevent_req_is_nterror(req, &status)) {
3304 status = state->rename_status;
3306 tevent_req_received(req);
3307 return status;
3310 /***************************************************************
3311 Wrapper that allows SMB2 to set an EA on a fnum.
3312 Synchronous only.
3313 ***************************************************************/
3315 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3316 uint16_t fnum,
3317 const char *ea_name,
3318 const char *ea_val,
3319 size_t ea_len)
3321 NTSTATUS status;
3322 DATA_BLOB inbuf = data_blob_null;
3323 size_t bloblen = 0;
3324 char *ea_name_ascii = NULL;
3325 size_t namelen = 0;
3326 TALLOC_CTX *frame = talloc_stackframe();
3328 if (smbXcli_conn_has_async_calls(cli->conn)) {
3330 * Can't use sync call while an async call is in flight
3332 status = NT_STATUS_INVALID_PARAMETER;
3333 goto fail;
3336 /* Marshall the SMB2 EA data. */
3337 if (ea_len > 0xFFFF) {
3338 status = NT_STATUS_INVALID_PARAMETER;
3339 goto fail;
3342 if (!push_ascii_talloc(frame,
3343 &ea_name_ascii,
3344 ea_name,
3345 &namelen)) {
3346 status = NT_STATUS_INVALID_PARAMETER;
3347 goto fail;
3350 if (namelen < 2 || namelen > 0xFF) {
3351 status = NT_STATUS_INVALID_PARAMETER;
3352 goto fail;
3355 bloblen = 8 + ea_len + namelen;
3356 /* Round up to a 4 byte boundary. */
3357 bloblen = ((bloblen + 3)&~3);
3359 inbuf = data_blob_talloc_zero(frame, bloblen);
3360 if (inbuf.data == NULL) {
3361 status = NT_STATUS_NO_MEMORY;
3362 goto fail;
3364 /* namelen doesn't include the NULL byte. */
3365 SCVAL(inbuf.data, 5, namelen - 1);
3366 SSVAL(inbuf.data, 6, ea_len);
3367 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3368 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3370 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3371 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3373 status = cli_smb2_set_info_fnum(
3374 cli,
3375 fnum,
3376 1, /* in_info_type */
3377 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3378 &inbuf, /* in_input_buffer */
3379 0); /* in_additional_info */
3381 fail:
3383 cli->raw_status = status;
3385 TALLOC_FREE(frame);
3386 return status;
3389 /***************************************************************
3390 Wrapper that allows SMB2 to set an EA on a pathname.
3391 Synchronous only.
3392 ***************************************************************/
3394 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3395 const char *name,
3396 const char *ea_name,
3397 const char *ea_val,
3398 size_t ea_len)
3400 NTSTATUS status;
3401 uint16_t fnum = 0xffff;
3403 if (smbXcli_conn_has_async_calls(cli->conn)) {
3405 * Can't use sync call while an async call is in flight
3407 status = NT_STATUS_INVALID_PARAMETER;
3408 goto fail;
3411 status = get_fnum_from_path(cli,
3412 name,
3413 FILE_WRITE_EA,
3414 &fnum);
3416 if (!NT_STATUS_IS_OK(status)) {
3417 goto fail;
3420 status = cli_set_ea_fnum(cli,
3421 fnum,
3422 ea_name,
3423 ea_val,
3424 ea_len);
3425 if (!NT_STATUS_IS_OK(status)) {
3426 goto fail;
3429 fail:
3431 if (fnum != 0xffff) {
3432 cli_smb2_close_fnum(cli, fnum);
3435 cli->raw_status = status;
3437 return status;
3440 /***************************************************************
3441 Wrapper that allows SMB2 to get an EA list on a pathname.
3442 Synchronous only.
3443 ***************************************************************/
3445 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3446 const char *name,
3447 TALLOC_CTX *ctx,
3448 size_t *pnum_eas,
3449 struct ea_struct **pea_array)
3451 NTSTATUS status;
3452 uint16_t fnum = 0xffff;
3453 DATA_BLOB outbuf = data_blob_null;
3454 struct ea_list *ea_list = NULL;
3455 struct ea_list *eal = NULL;
3456 size_t ea_count = 0;
3457 TALLOC_CTX *frame = talloc_stackframe();
3459 *pnum_eas = 0;
3460 *pea_array = NULL;
3462 if (smbXcli_conn_has_async_calls(cli->conn)) {
3464 * Can't use sync call while an async call is in flight
3466 status = NT_STATUS_INVALID_PARAMETER;
3467 goto fail;
3470 status = get_fnum_from_path(cli,
3471 name,
3472 FILE_READ_EA,
3473 &fnum);
3475 if (!NT_STATUS_IS_OK(status)) {
3476 goto fail;
3479 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3480 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3482 status = cli_smb2_query_info_fnum(
3483 cli,
3484 fnum,
3485 1, /* in_info_type */
3486 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3487 0xFFFF, /* in_max_output_length */
3488 NULL, /* in_input_buffer */
3489 0, /* in_additional_info */
3490 0, /* in_flags */
3491 frame,
3492 &outbuf);
3494 if (!NT_STATUS_IS_OK(status)) {
3495 goto fail;
3498 /* Parse the reply. */
3499 ea_list = read_nttrans_ea_list(ctx,
3500 (const char *)outbuf.data,
3501 outbuf.length);
3502 if (ea_list == NULL) {
3503 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3504 goto fail;
3507 /* Convert to an array. */
3508 for (eal = ea_list; eal; eal = eal->next) {
3509 ea_count++;
3512 if (ea_count) {
3513 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3514 if (*pea_array == NULL) {
3515 status = NT_STATUS_NO_MEMORY;
3516 goto fail;
3518 ea_count = 0;
3519 for (eal = ea_list; eal; eal = eal->next) {
3520 (*pea_array)[ea_count++] = eal->ea;
3522 *pnum_eas = ea_count;
3525 fail:
3527 if (fnum != 0xffff) {
3528 cli_smb2_close_fnum(cli, fnum);
3531 cli->raw_status = status;
3533 TALLOC_FREE(frame);
3534 return status;
3537 /***************************************************************
3538 Wrapper that allows SMB2 to get user quota.
3539 Synchronous only.
3540 ***************************************************************/
3542 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3543 int quota_fnum,
3544 SMB_NTQUOTA_STRUCT *pqt)
3546 NTSTATUS status;
3547 DATA_BLOB inbuf = data_blob_null;
3548 DATA_BLOB info_blob = data_blob_null;
3549 DATA_BLOB outbuf = data_blob_null;
3550 TALLOC_CTX *frame = talloc_stackframe();
3551 unsigned sid_len;
3552 unsigned int offset;
3553 struct smb2_query_quota_info query = {0};
3554 struct file_get_quota_info info = {0};
3555 enum ndr_err_code err;
3556 struct ndr_push *ndr_push = NULL;
3558 if (smbXcli_conn_has_async_calls(cli->conn)) {
3560 * Can't use sync call while an async call is in flight
3562 status = NT_STATUS_INVALID_PARAMETER;
3563 goto fail;
3566 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3568 query.return_single = 1;
3570 info.next_entry_offset = 0;
3571 info.sid_length = sid_len;
3572 info.sid = pqt->sid;
3574 err = ndr_push_struct_blob(
3575 &info_blob,
3576 frame,
3577 &info,
3578 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3580 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3581 status = NT_STATUS_INTERNAL_ERROR;
3582 goto fail;
3585 query.sid_list_length = info_blob.length;
3586 ndr_push = ndr_push_init_ctx(frame);
3587 if (!ndr_push) {
3588 status = NT_STATUS_NO_MEMORY;
3589 goto fail;
3592 err = ndr_push_smb2_query_quota_info(ndr_push,
3593 NDR_SCALARS | NDR_BUFFERS,
3594 &query);
3596 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3597 status = NT_STATUS_INTERNAL_ERROR;
3598 goto fail;
3601 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3602 info_blob.length);
3604 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3605 status = NT_STATUS_INTERNAL_ERROR;
3606 goto fail;
3608 inbuf.data = ndr_push->data;
3609 inbuf.length = ndr_push->offset;
3611 status = cli_smb2_query_info_fnum(
3612 cli,
3613 quota_fnum,
3614 4, /* in_info_type */
3615 0, /* in_file_info_class */
3616 0xFFFF, /* in_max_output_length */
3617 &inbuf, /* in_input_buffer */
3618 0, /* in_additional_info */
3619 0, /* in_flags */
3620 frame,
3621 &outbuf);
3623 if (!NT_STATUS_IS_OK(status)) {
3624 goto fail;
3627 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3628 pqt)) {
3629 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3630 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3633 fail:
3634 cli->raw_status = status;
3636 TALLOC_FREE(frame);
3637 return status;
3640 /***************************************************************
3641 Wrapper that allows SMB2 to list user quota.
3642 Synchronous only.
3643 ***************************************************************/
3645 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3646 TALLOC_CTX *mem_ctx,
3647 int quota_fnum,
3648 SMB_NTQUOTA_LIST **pqt_list,
3649 bool first)
3651 NTSTATUS status;
3652 DATA_BLOB inbuf = data_blob_null;
3653 DATA_BLOB outbuf = data_blob_null;
3654 TALLOC_CTX *frame = talloc_stackframe();
3655 struct smb2_query_quota_info info = {0};
3656 enum ndr_err_code err;
3658 if (smbXcli_conn_has_async_calls(cli->conn)) {
3660 * Can't use sync call while an async call is in flight
3662 status = NT_STATUS_INVALID_PARAMETER;
3663 goto cleanup;
3666 info.restart_scan = first ? 1 : 0;
3668 err = ndr_push_struct_blob(
3669 &inbuf,
3670 frame,
3671 &info,
3672 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3674 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3675 status = NT_STATUS_INTERNAL_ERROR;
3676 goto cleanup;
3679 status = cli_smb2_query_info_fnum(
3680 cli,
3681 quota_fnum,
3682 4, /* in_info_type */
3683 0, /* in_file_info_class */
3684 0xFFFF, /* in_max_output_length */
3685 &inbuf, /* in_input_buffer */
3686 0, /* in_additional_info */
3687 0, /* in_flags */
3688 frame,
3689 &outbuf);
3692 * safeguard against panic from calling parse_user_quota_list with
3693 * NULL buffer
3695 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3696 status = NT_STATUS_NO_MORE_ENTRIES;
3699 if (!NT_STATUS_IS_OK(status)) {
3700 goto cleanup;
3703 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3704 pqt_list);
3706 cleanup:
3707 cli->raw_status = status;
3709 TALLOC_FREE(frame);
3710 return status;
3713 /***************************************************************
3714 Wrapper that allows SMB2 to get file system quota.
3715 Synchronous only.
3716 ***************************************************************/
3718 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3719 int quota_fnum,
3720 SMB_NTQUOTA_STRUCT *pqt)
3722 NTSTATUS status;
3723 DATA_BLOB outbuf = data_blob_null;
3724 TALLOC_CTX *frame = talloc_stackframe();
3726 if (smbXcli_conn_has_async_calls(cli->conn)) {
3728 * Can't use sync call while an async call is in flight
3730 status = NT_STATUS_INVALID_PARAMETER;
3731 goto cleanup;
3734 status = cli_smb2_query_info_fnum(
3735 cli,
3736 quota_fnum,
3737 2, /* in_info_type */
3738 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3739 0xFFFF, /* in_max_output_length */
3740 NULL, /* in_input_buffer */
3741 0, /* in_additional_info */
3742 0, /* in_flags */
3743 frame,
3744 &outbuf);
3746 if (!NT_STATUS_IS_OK(status)) {
3747 goto cleanup;
3750 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3752 cleanup:
3753 cli->raw_status = status;
3755 TALLOC_FREE(frame);
3756 return status;
3759 /***************************************************************
3760 Wrapper that allows SMB2 to set user quota.
3761 Synchronous only.
3762 ***************************************************************/
3764 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3765 int quota_fnum,
3766 SMB_NTQUOTA_LIST *qtl)
3768 NTSTATUS status;
3769 DATA_BLOB inbuf = data_blob_null;
3770 TALLOC_CTX *frame = talloc_stackframe();
3772 if (smbXcli_conn_has_async_calls(cli->conn)) {
3774 * Can't use sync call while an async call is in flight
3776 status = NT_STATUS_INVALID_PARAMETER;
3777 goto cleanup;
3780 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3781 if (!NT_STATUS_IS_OK(status)) {
3782 goto cleanup;
3785 status = cli_smb2_set_info_fnum(
3786 cli,
3787 quota_fnum,
3788 4, /* in_info_type */
3789 0, /* in_file_info_class */
3790 &inbuf, /* in_input_buffer */
3791 0); /* in_additional_info */
3792 cleanup:
3794 cli->raw_status = status;
3796 TALLOC_FREE(frame);
3798 return status;
3801 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3802 int quota_fnum,
3803 SMB_NTQUOTA_STRUCT *pqt)
3805 NTSTATUS status;
3806 DATA_BLOB inbuf = data_blob_null;
3807 TALLOC_CTX *frame = talloc_stackframe();
3809 if (smbXcli_conn_has_async_calls(cli->conn)) {
3811 * Can't use sync call while an async call is in flight
3813 status = NT_STATUS_INVALID_PARAMETER;
3814 goto cleanup;
3817 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3818 if (!NT_STATUS_IS_OK(status)) {
3819 goto cleanup;
3822 status = cli_smb2_set_info_fnum(
3823 cli,
3824 quota_fnum,
3825 2, /* in_info_type */
3826 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3827 &inbuf, /* in_input_buffer */
3828 0); /* in_additional_info */
3829 cleanup:
3830 cli->raw_status = status;
3832 TALLOC_FREE(frame);
3833 return status;
3836 struct cli_smb2_read_state {
3837 struct tevent_context *ev;
3838 struct cli_state *cli;
3839 struct smb2_hnd *ph;
3840 uint64_t start_offset;
3841 uint32_t size;
3842 uint32_t received;
3843 uint8_t *buf;
3846 static void cli_smb2_read_done(struct tevent_req *subreq);
3848 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3849 struct tevent_context *ev,
3850 struct cli_state *cli,
3851 uint16_t fnum,
3852 off_t offset,
3853 size_t size)
3855 NTSTATUS status;
3856 struct tevent_req *req, *subreq;
3857 struct cli_smb2_read_state *state;
3859 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3860 if (req == NULL) {
3861 return NULL;
3863 state->ev = ev;
3864 state->cli = cli;
3865 state->start_offset = (uint64_t)offset;
3866 state->size = (uint32_t)size;
3867 state->received = 0;
3868 state->buf = NULL;
3870 status = map_fnum_to_smb2_handle(cli,
3871 fnum,
3872 &state->ph);
3873 if (tevent_req_nterror(req, status)) {
3874 return tevent_req_post(req, ev);
3877 subreq = smb2cli_read_send(state,
3878 state->ev,
3879 state->cli->conn,
3880 state->cli->timeout,
3881 state->cli->smb2.session,
3882 state->cli->smb2.tcon,
3883 state->size,
3884 state->start_offset,
3885 state->ph->fid_persistent,
3886 state->ph->fid_volatile,
3887 0, /* minimum_count */
3888 0); /* remaining_bytes */
3890 if (tevent_req_nomem(subreq, req)) {
3891 return tevent_req_post(req, ev);
3893 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3894 return req;
3897 static void cli_smb2_read_done(struct tevent_req *subreq)
3899 struct tevent_req *req = tevent_req_callback_data(
3900 subreq, struct tevent_req);
3901 struct cli_smb2_read_state *state = tevent_req_data(
3902 req, struct cli_smb2_read_state);
3903 NTSTATUS status;
3905 status = smb2cli_read_recv(subreq, state,
3906 &state->buf, &state->received);
3907 if (tevent_req_nterror(req, status)) {
3908 return;
3911 if (state->received > state->size) {
3912 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3913 return;
3916 tevent_req_done(req);
3919 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3920 ssize_t *received,
3921 uint8_t **rcvbuf)
3923 NTSTATUS status;
3924 struct cli_smb2_read_state *state = tevent_req_data(
3925 req, struct cli_smb2_read_state);
3927 if (tevent_req_is_nterror(req, &status)) {
3928 state->cli->raw_status = status;
3929 return status;
3932 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3933 * better make sure that you copy it away before you talloc_free(req).
3934 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3936 *received = (ssize_t)state->received;
3937 *rcvbuf = state->buf;
3938 state->cli->raw_status = NT_STATUS_OK;
3939 return NT_STATUS_OK;
3942 struct cli_smb2_write_state {
3943 struct tevent_context *ev;
3944 struct cli_state *cli;
3945 struct smb2_hnd *ph;
3946 uint32_t flags;
3947 const uint8_t *buf;
3948 uint64_t offset;
3949 uint32_t size;
3950 uint32_t written;
3953 static void cli_smb2_write_written(struct tevent_req *req);
3955 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3956 struct tevent_context *ev,
3957 struct cli_state *cli,
3958 uint16_t fnum,
3959 uint16_t mode,
3960 const uint8_t *buf,
3961 off_t offset,
3962 size_t size)
3964 NTSTATUS status;
3965 struct tevent_req *req, *subreq = NULL;
3966 struct cli_smb2_write_state *state = NULL;
3968 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3969 if (req == NULL) {
3970 return NULL;
3972 state->ev = ev;
3973 state->cli = cli;
3974 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
3975 state->flags = (uint32_t)mode;
3976 state->buf = buf;
3977 state->offset = (uint64_t)offset;
3978 state->size = (uint32_t)size;
3979 state->written = 0;
3981 status = map_fnum_to_smb2_handle(cli,
3982 fnum,
3983 &state->ph);
3984 if (tevent_req_nterror(req, status)) {
3985 return tevent_req_post(req, ev);
3988 subreq = smb2cli_write_send(state,
3989 state->ev,
3990 state->cli->conn,
3991 state->cli->timeout,
3992 state->cli->smb2.session,
3993 state->cli->smb2.tcon,
3994 state->size,
3995 state->offset,
3996 state->ph->fid_persistent,
3997 state->ph->fid_volatile,
3998 0, /* remaining_bytes */
3999 state->flags, /* flags */
4000 state->buf);
4002 if (tevent_req_nomem(subreq, req)) {
4003 return tevent_req_post(req, ev);
4005 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4006 return req;
4009 static void cli_smb2_write_written(struct tevent_req *subreq)
4011 struct tevent_req *req = tevent_req_callback_data(
4012 subreq, struct tevent_req);
4013 struct cli_smb2_write_state *state = tevent_req_data(
4014 req, struct cli_smb2_write_state);
4015 NTSTATUS status;
4016 uint32_t written;
4018 status = smb2cli_write_recv(subreq, &written);
4019 TALLOC_FREE(subreq);
4020 if (tevent_req_nterror(req, status)) {
4021 return;
4024 state->written = written;
4026 tevent_req_done(req);
4029 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4030 size_t *pwritten)
4032 struct cli_smb2_write_state *state = tevent_req_data(
4033 req, struct cli_smb2_write_state);
4034 NTSTATUS status;
4036 if (tevent_req_is_nterror(req, &status)) {
4037 state->cli->raw_status = status;
4038 tevent_req_received(req);
4039 return status;
4042 if (pwritten != NULL) {
4043 *pwritten = (size_t)state->written;
4045 state->cli->raw_status = NT_STATUS_OK;
4046 tevent_req_received(req);
4047 return NT_STATUS_OK;
4050 /***************************************************************
4051 Wrapper that allows SMB2 async write using an fnum.
4052 This is mostly cut-and-paste from Volker's code inside
4053 source3/libsmb/clireadwrite.c, adapted for SMB2.
4055 Done this way so I can reuse all the logic inside cli_push()
4056 for free :-).
4057 ***************************************************************/
4059 struct cli_smb2_writeall_state {
4060 struct tevent_context *ev;
4061 struct cli_state *cli;
4062 struct smb2_hnd *ph;
4063 uint32_t flags;
4064 const uint8_t *buf;
4065 uint64_t offset;
4066 uint32_t size;
4067 uint32_t written;
4070 static void cli_smb2_writeall_written(struct tevent_req *req);
4072 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4073 struct tevent_context *ev,
4074 struct cli_state *cli,
4075 uint16_t fnum,
4076 uint16_t mode,
4077 const uint8_t *buf,
4078 off_t offset,
4079 size_t size)
4081 NTSTATUS status;
4082 struct tevent_req *req, *subreq = NULL;
4083 struct cli_smb2_writeall_state *state = NULL;
4084 uint32_t to_write;
4085 uint32_t max_size;
4086 bool ok;
4088 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4089 if (req == NULL) {
4090 return NULL;
4092 state->ev = ev;
4093 state->cli = cli;
4094 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4095 state->flags = (uint32_t)mode;
4096 state->buf = buf;
4097 state->offset = (uint64_t)offset;
4098 state->size = (uint32_t)size;
4099 state->written = 0;
4101 status = map_fnum_to_smb2_handle(cli,
4102 fnum,
4103 &state->ph);
4104 if (tevent_req_nterror(req, status)) {
4105 return tevent_req_post(req, ev);
4108 to_write = state->size;
4109 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4110 to_write = MIN(max_size, to_write);
4111 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4112 if (ok) {
4113 to_write = MIN(max_size, to_write);
4116 subreq = smb2cli_write_send(state,
4117 state->ev,
4118 state->cli->conn,
4119 state->cli->timeout,
4120 state->cli->smb2.session,
4121 state->cli->smb2.tcon,
4122 to_write,
4123 state->offset,
4124 state->ph->fid_persistent,
4125 state->ph->fid_volatile,
4126 0, /* remaining_bytes */
4127 state->flags, /* flags */
4128 state->buf + state->written);
4130 if (tevent_req_nomem(subreq, req)) {
4131 return tevent_req_post(req, ev);
4133 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4134 return req;
4137 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4139 struct tevent_req *req = tevent_req_callback_data(
4140 subreq, struct tevent_req);
4141 struct cli_smb2_writeall_state *state = tevent_req_data(
4142 req, struct cli_smb2_writeall_state);
4143 NTSTATUS status;
4144 uint32_t written, to_write;
4145 uint32_t max_size;
4146 bool ok;
4148 status = smb2cli_write_recv(subreq, &written);
4149 TALLOC_FREE(subreq);
4150 if (tevent_req_nterror(req, status)) {
4151 return;
4154 state->written += written;
4156 if (state->written > state->size) {
4157 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4158 return;
4161 to_write = state->size - state->written;
4163 if (to_write == 0) {
4164 tevent_req_done(req);
4165 return;
4168 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4169 to_write = MIN(max_size, to_write);
4170 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4171 if (ok) {
4172 to_write = MIN(max_size, to_write);
4175 subreq = smb2cli_write_send(state,
4176 state->ev,
4177 state->cli->conn,
4178 state->cli->timeout,
4179 state->cli->smb2.session,
4180 state->cli->smb2.tcon,
4181 to_write,
4182 state->offset + state->written,
4183 state->ph->fid_persistent,
4184 state->ph->fid_volatile,
4185 0, /* remaining_bytes */
4186 state->flags, /* flags */
4187 state->buf + state->written);
4189 if (tevent_req_nomem(subreq, req)) {
4190 return;
4192 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4195 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4196 size_t *pwritten)
4198 struct cli_smb2_writeall_state *state = tevent_req_data(
4199 req, struct cli_smb2_writeall_state);
4200 NTSTATUS status;
4202 if (tevent_req_is_nterror(req, &status)) {
4203 state->cli->raw_status = status;
4204 return status;
4206 if (pwritten != NULL) {
4207 *pwritten = (size_t)state->written;
4209 state->cli->raw_status = NT_STATUS_OK;
4210 return NT_STATUS_OK;
4213 struct cli_smb2_splice_state {
4214 struct tevent_context *ev;
4215 struct cli_state *cli;
4216 struct smb2_hnd *src_ph;
4217 struct smb2_hnd *dst_ph;
4218 int (*splice_cb)(off_t n, void *priv);
4219 void *priv;
4220 off_t written;
4221 off_t size;
4222 off_t src_offset;
4223 off_t dst_offset;
4224 bool resized;
4225 struct req_resume_key_rsp resume_rsp;
4226 struct srv_copychunk_copy cc_copy;
4229 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4230 struct tevent_req *req);
4232 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4234 struct tevent_req *req = tevent_req_callback_data(
4235 subreq, struct tevent_req);
4236 struct cli_smb2_splice_state *state =
4237 tevent_req_data(req,
4238 struct cli_smb2_splice_state);
4239 struct smbXcli_conn *conn = state->cli->conn;
4240 DATA_BLOB out_input_buffer = data_blob_null;
4241 DATA_BLOB out_output_buffer = data_blob_null;
4242 struct srv_copychunk_rsp cc_copy_rsp;
4243 enum ndr_err_code ndr_ret;
4244 NTSTATUS status;
4246 status = smb2cli_ioctl_recv(subreq, state,
4247 &out_input_buffer,
4248 &out_output_buffer);
4249 TALLOC_FREE(subreq);
4250 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4251 state->resized) && tevent_req_nterror(req, status)) {
4252 return;
4255 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4256 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4257 if (ndr_ret != NDR_ERR_SUCCESS) {
4258 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4259 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4260 return;
4263 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4264 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4265 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4266 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4267 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4268 tevent_req_nterror(req, status)) {
4269 return;
4272 state->resized = true;
4273 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4274 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4275 } else {
4276 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4277 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4278 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4279 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4280 return;
4282 state->src_offset += cc_copy_rsp.total_bytes_written;
4283 state->dst_offset += cc_copy_rsp.total_bytes_written;
4284 state->written += cc_copy_rsp.total_bytes_written;
4285 if (!state->splice_cb(state->written, state->priv)) {
4286 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4287 return;
4291 cli_splice_copychunk_send(state, req);
4294 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4295 struct tevent_req *req)
4297 struct tevent_req *subreq;
4298 enum ndr_err_code ndr_ret;
4299 struct smbXcli_conn *conn = state->cli->conn;
4300 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4301 off_t src_offset = state->src_offset;
4302 off_t dst_offset = state->dst_offset;
4303 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4304 state->size - state->written);
4305 DATA_BLOB in_input_buffer = data_blob_null;
4306 DATA_BLOB in_output_buffer = data_blob_null;
4308 if (state->size - state->written == 0) {
4309 tevent_req_done(req);
4310 return;
4313 cc_copy->chunk_count = 0;
4314 while (req_len) {
4315 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4316 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4317 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4318 smb2cli_conn_cc_chunk_len(conn));
4319 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4320 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4321 return;
4323 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4324 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4325 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4326 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4327 return;
4329 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4330 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4331 cc_copy->chunk_count++;
4334 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4335 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4336 if (ndr_ret != NDR_ERR_SUCCESS) {
4337 DEBUG(0, ("failed to marshall copy chunk req\n"));
4338 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4339 return;
4342 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4343 state->cli->timeout,
4344 state->cli->smb2.session,
4345 state->cli->smb2.tcon,
4346 state->dst_ph->fid_persistent, /* in_fid_persistent */
4347 state->dst_ph->fid_volatile, /* in_fid_volatile */
4348 FSCTL_SRV_COPYCHUNK_WRITE,
4349 0, /* in_max_input_length */
4350 &in_input_buffer,
4351 12, /* in_max_output_length */
4352 &in_output_buffer,
4353 SMB2_IOCTL_FLAG_IS_FSCTL);
4354 if (tevent_req_nomem(subreq, req)) {
4355 return;
4357 tevent_req_set_callback(subreq,
4358 cli_splice_copychunk_done,
4359 req);
4362 static void cli_splice_key_done(struct tevent_req *subreq)
4364 struct tevent_req *req = tevent_req_callback_data(
4365 subreq, struct tevent_req);
4366 struct cli_smb2_splice_state *state =
4367 tevent_req_data(req,
4368 struct cli_smb2_splice_state);
4369 enum ndr_err_code ndr_ret;
4370 NTSTATUS status;
4372 DATA_BLOB out_input_buffer = data_blob_null;
4373 DATA_BLOB out_output_buffer = data_blob_null;
4375 status = smb2cli_ioctl_recv(subreq, state,
4376 &out_input_buffer,
4377 &out_output_buffer);
4378 TALLOC_FREE(subreq);
4379 if (tevent_req_nterror(req, status)) {
4380 return;
4383 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4384 state, &state->resume_rsp,
4385 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4386 if (ndr_ret != NDR_ERR_SUCCESS) {
4387 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4388 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4389 return;
4392 memcpy(&state->cc_copy.source_key,
4393 &state->resume_rsp.resume_key,
4394 sizeof state->resume_rsp.resume_key);
4396 cli_splice_copychunk_send(state, req);
4399 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4400 struct tevent_context *ev,
4401 struct cli_state *cli,
4402 uint16_t src_fnum, uint16_t dst_fnum,
4403 off_t size, off_t src_offset, off_t dst_offset,
4404 int (*splice_cb)(off_t n, void *priv),
4405 void *priv)
4407 struct tevent_req *req;
4408 struct tevent_req *subreq;
4409 struct cli_smb2_splice_state *state;
4410 NTSTATUS status;
4411 DATA_BLOB in_input_buffer = data_blob_null;
4412 DATA_BLOB in_output_buffer = data_blob_null;
4414 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4415 if (req == NULL) {
4416 return NULL;
4418 state->cli = cli;
4419 state->ev = ev;
4420 state->splice_cb = splice_cb;
4421 state->priv = priv;
4422 state->size = size;
4423 state->written = 0;
4424 state->src_offset = src_offset;
4425 state->dst_offset = dst_offset;
4426 state->cc_copy.chunks = talloc_array(state,
4427 struct srv_copychunk,
4428 smb2cli_conn_cc_max_chunks(cli->conn));
4429 if (state->cc_copy.chunks == NULL) {
4430 return NULL;
4433 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4434 if (tevent_req_nterror(req, status))
4435 return tevent_req_post(req, ev);
4437 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4438 if (tevent_req_nterror(req, status))
4439 return tevent_req_post(req, ev);
4441 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4442 cli->timeout,
4443 cli->smb2.session,
4444 cli->smb2.tcon,
4445 state->src_ph->fid_persistent, /* in_fid_persistent */
4446 state->src_ph->fid_volatile, /* in_fid_volatile */
4447 FSCTL_SRV_REQUEST_RESUME_KEY,
4448 0, /* in_max_input_length */
4449 &in_input_buffer,
4450 32, /* in_max_output_length */
4451 &in_output_buffer,
4452 SMB2_IOCTL_FLAG_IS_FSCTL);
4453 if (tevent_req_nomem(subreq, req)) {
4454 return NULL;
4456 tevent_req_set_callback(subreq,
4457 cli_splice_key_done,
4458 req);
4460 return req;
4463 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4465 struct cli_smb2_splice_state *state = tevent_req_data(
4466 req, struct cli_smb2_splice_state);
4467 NTSTATUS status;
4469 if (tevent_req_is_nterror(req, &status)) {
4470 state->cli->raw_status = status;
4471 tevent_req_received(req);
4472 return status;
4474 if (written != NULL) {
4475 *written = state->written;
4477 state->cli->raw_status = NT_STATUS_OK;
4478 tevent_req_received(req);
4479 return NT_STATUS_OK;
4482 /***************************************************************
4483 SMB2 enum shadow copy data.
4484 ***************************************************************/
4486 struct cli_smb2_shadow_copy_data_fnum_state {
4487 struct cli_state *cli;
4488 uint16_t fnum;
4489 struct smb2_hnd *ph;
4490 DATA_BLOB out_input_buffer;
4491 DATA_BLOB out_output_buffer;
4494 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4496 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4497 TALLOC_CTX *mem_ctx,
4498 struct tevent_context *ev,
4499 struct cli_state *cli,
4500 uint16_t fnum,
4501 bool get_names)
4503 struct tevent_req *req, *subreq;
4504 struct cli_smb2_shadow_copy_data_fnum_state *state;
4505 NTSTATUS status;
4507 req = tevent_req_create(mem_ctx, &state,
4508 struct cli_smb2_shadow_copy_data_fnum_state);
4509 if (req == NULL) {
4510 return NULL;
4513 state->cli = cli;
4514 state->fnum = fnum;
4516 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4517 if (tevent_req_nterror(req, status)) {
4518 return tevent_req_post(req, ev);
4522 * TODO. Under SMB2 we should send a zero max_output_length
4523 * ioctl to get the required size, then send another ioctl
4524 * to get the data, but the current SMB1 implementation just
4525 * does one roundtrip with a 64K buffer size. Do the same
4526 * for now. JRA.
4529 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4530 state->cli->timeout,
4531 state->cli->smb2.session,
4532 state->cli->smb2.tcon,
4533 state->ph->fid_persistent, /* in_fid_persistent */
4534 state->ph->fid_volatile, /* in_fid_volatile */
4535 FSCTL_GET_SHADOW_COPY_DATA,
4536 0, /* in_max_input_length */
4537 NULL, /* in_input_buffer */
4538 get_names ?
4539 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4540 NULL, /* in_output_buffer */
4541 SMB2_IOCTL_FLAG_IS_FSCTL);
4543 if (tevent_req_nomem(subreq, req)) {
4544 return tevent_req_post(req, ev);
4546 tevent_req_set_callback(subreq,
4547 cli_smb2_shadow_copy_data_fnum_done,
4548 req);
4550 return req;
4553 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4555 struct tevent_req *req = tevent_req_callback_data(
4556 subreq, struct tevent_req);
4557 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4558 req, struct cli_smb2_shadow_copy_data_fnum_state);
4559 NTSTATUS status;
4561 status = smb2cli_ioctl_recv(subreq, state,
4562 &state->out_input_buffer,
4563 &state->out_output_buffer);
4564 tevent_req_simple_finish_ntstatus(subreq, status);
4567 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4568 TALLOC_CTX *mem_ctx,
4569 bool get_names,
4570 char ***pnames,
4571 int *pnum_names)
4573 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4574 req, struct cli_smb2_shadow_copy_data_fnum_state);
4575 char **names = NULL;
4576 uint32_t num_names = 0;
4577 uint32_t num_names_returned = 0;
4578 uint32_t dlength = 0;
4579 uint32_t i;
4580 uint8_t *endp = NULL;
4581 NTSTATUS status;
4583 if (tevent_req_is_nterror(req, &status)) {
4584 return status;
4587 if (state->out_output_buffer.length < 16) {
4588 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4591 num_names = IVAL(state->out_output_buffer.data, 0);
4592 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4593 dlength = IVAL(state->out_output_buffer.data, 8);
4595 if (num_names > 0x7FFFFFFF) {
4596 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4599 if (get_names == false) {
4600 *pnum_names = (int)num_names;
4601 return NT_STATUS_OK;
4603 if (num_names != num_names_returned) {
4604 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4606 if (dlength + 12 < 12) {
4607 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4610 * NB. The below is an allowable return if there are
4611 * more snapshots than the buffer size we told the
4612 * server we can receive. We currently don't support
4613 * this.
4615 if (dlength + 12 > state->out_output_buffer.length) {
4616 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4618 if (state->out_output_buffer.length +
4619 (2 * sizeof(SHADOW_COPY_LABEL)) <
4620 state->out_output_buffer.length) {
4621 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4624 names = talloc_array(mem_ctx, char *, num_names_returned);
4625 if (names == NULL) {
4626 return NT_STATUS_NO_MEMORY;
4629 endp = state->out_output_buffer.data +
4630 state->out_output_buffer.length;
4632 for (i=0; i<num_names_returned; i++) {
4633 bool ret;
4634 uint8_t *src;
4635 size_t converted_size;
4637 src = state->out_output_buffer.data + 12 +
4638 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4640 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4641 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4643 ret = convert_string_talloc(
4644 names, CH_UTF16LE, CH_UNIX,
4645 src, 2 * sizeof(SHADOW_COPY_LABEL),
4646 &names[i], &converted_size);
4647 if (!ret) {
4648 TALLOC_FREE(names);
4649 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4652 *pnum_names = num_names;
4653 *pnames = names;
4654 return NT_STATUS_OK;
4657 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4658 struct cli_state *cli,
4659 uint16_t fnum,
4660 bool get_names,
4661 char ***pnames,
4662 int *pnum_names)
4664 TALLOC_CTX *frame = talloc_stackframe();
4665 struct tevent_context *ev;
4666 struct tevent_req *req;
4667 NTSTATUS status = NT_STATUS_NO_MEMORY;
4669 if (smbXcli_conn_has_async_calls(cli->conn)) {
4671 * Can't use sync call while an async call is in flight
4673 status = NT_STATUS_INVALID_PARAMETER;
4674 goto fail;
4676 ev = samba_tevent_context_init(frame);
4677 if (ev == NULL) {
4678 goto fail;
4680 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4682 cli,
4683 fnum,
4684 get_names);
4685 if (req == NULL) {
4686 goto fail;
4688 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4689 goto fail;
4691 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4692 mem_ctx,
4693 get_names,
4694 pnames,
4695 pnum_names);
4696 fail:
4697 cli->raw_status = status;
4699 TALLOC_FREE(frame);
4700 return status;
4703 /***************************************************************
4704 Wrapper that allows SMB2 to truncate a file.
4705 Synchronous only.
4706 ***************************************************************/
4708 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4709 uint16_t fnum,
4710 uint64_t newsize)
4712 NTSTATUS status;
4713 uint8_t buf[8] = {0};
4714 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4715 TALLOC_CTX *frame = talloc_stackframe();
4717 if (smbXcli_conn_has_async_calls(cli->conn)) {
4719 * Can't use sync call while an async call is in flight
4721 status = NT_STATUS_INVALID_PARAMETER;
4722 goto fail;
4725 SBVAL(buf, 0, newsize);
4727 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4728 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4730 status = cli_smb2_set_info_fnum(
4731 cli,
4732 fnum,
4733 1, /* in_info_type */
4734 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4735 &inbuf, /* in_input_buffer */
4738 fail:
4740 cli->raw_status = status;
4742 TALLOC_FREE(frame);
4743 return status;
4746 struct cli_smb2_notify_state {
4747 struct tevent_req *subreq;
4748 struct notify_change *changes;
4749 size_t num_changes;
4752 static void cli_smb2_notify_done(struct tevent_req *subreq);
4753 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4755 struct tevent_req *cli_smb2_notify_send(
4756 TALLOC_CTX *mem_ctx,
4757 struct tevent_context *ev,
4758 struct cli_state *cli,
4759 uint16_t fnum,
4760 uint32_t buffer_size,
4761 uint32_t completion_filter,
4762 bool recursive)
4764 struct tevent_req *req = NULL;
4765 struct cli_smb2_notify_state *state = NULL;
4766 struct smb2_hnd *ph = NULL;
4767 NTSTATUS status;
4769 req = tevent_req_create(mem_ctx, &state,
4770 struct cli_smb2_notify_state);
4771 if (req == NULL) {
4772 return NULL;
4775 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4776 if (tevent_req_nterror(req, status)) {
4777 return tevent_req_post(req, ev);
4780 state->subreq = smb2cli_notify_send(
4781 state,
4783 cli->conn,
4784 cli->timeout,
4785 cli->smb2.session,
4786 cli->smb2.tcon,
4787 buffer_size,
4788 ph->fid_persistent,
4789 ph->fid_volatile,
4790 completion_filter,
4791 recursive);
4792 if (tevent_req_nomem(state->subreq, req)) {
4793 return tevent_req_post(req, ev);
4795 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4796 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4797 return req;
4800 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4802 struct cli_smb2_notify_state *state = tevent_req_data(
4803 req, struct cli_smb2_notify_state);
4804 bool ok;
4806 ok = tevent_req_cancel(state->subreq);
4807 return ok;
4810 static void cli_smb2_notify_done(struct tevent_req *subreq)
4812 struct tevent_req *req = tevent_req_callback_data(
4813 subreq, struct tevent_req);
4814 struct cli_smb2_notify_state *state = tevent_req_data(
4815 req, struct cli_smb2_notify_state);
4816 uint8_t *base;
4817 uint32_t len;
4818 uint32_t ofs;
4819 NTSTATUS status;
4821 status = smb2cli_notify_recv(subreq, state, &base, &len);
4822 TALLOC_FREE(subreq);
4824 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4825 tevent_req_done(req);
4826 return;
4828 if (tevent_req_nterror(req, status)) {
4829 return;
4832 ofs = 0;
4834 while (len - ofs >= 12) {
4835 struct notify_change *tmp;
4836 struct notify_change *c;
4837 uint32_t next_ofs = IVAL(base, ofs);
4838 uint32_t file_name_length = IVAL(base, ofs+8);
4839 size_t namelen;
4840 bool ok;
4842 tmp = talloc_realloc(
4843 state,
4844 state->changes,
4845 struct notify_change,
4846 state->num_changes + 1);
4847 if (tevent_req_nomem(tmp, req)) {
4848 return;
4850 state->changes = tmp;
4851 c = &state->changes[state->num_changes];
4852 state->num_changes += 1;
4854 if (smb_buffer_oob(len, ofs, next_ofs) ||
4855 smb_buffer_oob(len, ofs+12, file_name_length)) {
4856 tevent_req_nterror(
4857 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4858 return;
4861 c->action = IVAL(base, ofs+4);
4863 ok = convert_string_talloc(
4864 state->changes,
4865 CH_UTF16LE,
4866 CH_UNIX,
4867 base + ofs + 12,
4868 file_name_length,
4869 &c->name,
4870 &namelen);
4871 if (!ok) {
4872 tevent_req_nterror(
4873 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4874 return;
4877 if (next_ofs == 0) {
4878 break;
4880 ofs += next_ofs;
4883 tevent_req_done(req);
4886 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4887 TALLOC_CTX *mem_ctx,
4888 struct notify_change **pchanges,
4889 uint32_t *pnum_changes)
4891 struct cli_smb2_notify_state *state = tevent_req_data(
4892 req, struct cli_smb2_notify_state);
4893 NTSTATUS status;
4895 if (tevent_req_is_nterror(req, &status)) {
4896 return status;
4898 *pchanges = talloc_move(mem_ctx, &state->changes);
4899 *pnum_changes = state->num_changes;
4900 return NT_STATUS_OK;
4903 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4904 uint32_t buffer_size, uint32_t completion_filter,
4905 bool recursive, TALLOC_CTX *mem_ctx,
4906 struct notify_change **pchanges,
4907 uint32_t *pnum_changes)
4909 TALLOC_CTX *frame = talloc_stackframe();
4910 struct tevent_context *ev;
4911 struct tevent_req *req;
4912 NTSTATUS status = NT_STATUS_NO_MEMORY;
4914 if (smbXcli_conn_has_async_calls(cli->conn)) {
4916 * Can't use sync call while an async call is in flight
4918 status = NT_STATUS_INVALID_PARAMETER;
4919 goto fail;
4921 ev = samba_tevent_context_init(frame);
4922 if (ev == NULL) {
4923 goto fail;
4925 req = cli_smb2_notify_send(
4926 frame,
4928 cli,
4929 fnum,
4930 buffer_size,
4931 completion_filter,
4932 recursive);
4933 if (req == NULL) {
4934 goto fail;
4936 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4937 goto fail;
4939 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4940 fail:
4941 TALLOC_FREE(frame);
4942 return status;
4945 struct cli_smb2_fsctl_state {
4946 DATA_BLOB out;
4949 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4951 struct tevent_req *cli_smb2_fsctl_send(
4952 TALLOC_CTX *mem_ctx,
4953 struct tevent_context *ev,
4954 struct cli_state *cli,
4955 uint16_t fnum,
4956 uint32_t ctl_code,
4957 const DATA_BLOB *in,
4958 uint32_t max_out)
4960 struct tevent_req *req = NULL, *subreq = NULL;
4961 struct cli_smb2_fsctl_state *state = NULL;
4962 struct smb2_hnd *ph = NULL;
4963 NTSTATUS status;
4965 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4966 if (req == NULL) {
4967 return NULL;
4970 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4971 if (tevent_req_nterror(req, status)) {
4972 return tevent_req_post(req, ev);
4975 subreq = smb2cli_ioctl_send(
4976 state,
4978 cli->conn,
4979 cli->timeout,
4980 cli->smb2.session,
4981 cli->smb2.tcon,
4982 ph->fid_persistent,
4983 ph->fid_volatile,
4984 ctl_code,
4985 0, /* in_max_input_length */
4987 max_out,
4988 NULL,
4989 SMB2_IOCTL_FLAG_IS_FSCTL);
4991 if (tevent_req_nomem(subreq, req)) {
4992 return tevent_req_post(req, ev);
4994 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
4995 return req;
4998 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5000 struct tevent_req *req = tevent_req_callback_data(
5001 subreq, struct tevent_req);
5002 struct cli_smb2_fsctl_state *state = tevent_req_data(
5003 req, struct cli_smb2_fsctl_state);
5004 NTSTATUS status;
5006 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5007 tevent_req_simple_finish_ntstatus(subreq, status);
5010 NTSTATUS cli_smb2_fsctl_recv(
5011 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5013 struct cli_smb2_fsctl_state *state = tevent_req_data(
5014 req, struct cli_smb2_fsctl_state);
5015 NTSTATUS status = NT_STATUS_OK;
5017 if (tevent_req_is_nterror(req, &status)) {
5018 tevent_req_received(req);
5019 return status;
5022 if (state->out.length == 0) {
5023 *out = (DATA_BLOB) { .data = NULL, };
5024 } else {
5026 * Can't use talloc_move() here, the outblobs from
5027 * smb2cli_ioctl_recv() are not standalone talloc
5028 * objects but just peek into the larger buffers
5029 * received, hanging off "state".
5031 *out = data_blob_talloc(
5032 mem_ctx, state->out.data, state->out.length);
5033 if (out->data == NULL) {
5034 status = NT_STATUS_NO_MEMORY;
5038 tevent_req_received(req);
5039 return NT_STATUS_OK;