libsmb: Use pidl generated parsing for posix file info
[Samba.git] / source3 / libsmb / cli_smb2_fnum.c
blob42f6d807fc5f3c795086d6fa75cac569ad037966
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 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
250 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
251 if (have_twrp) {
252 status = smb2_create_blob_add(
253 state,
254 &state->in_cblobs,
255 SMB2_CREATE_TAG_TWRP,
256 (DATA_BLOB) {
257 .data = (uint8_t *)&ntt,
258 .length = sizeof(ntt),
260 if (tevent_req_nterror(req, status)) {
261 return tevent_req_post(req, ev);
265 if (in_cblobs != NULL) {
266 uint32_t i;
267 for (i=0; i<in_cblobs->num_blobs; i++) {
268 struct smb2_create_blob *b = &in_cblobs->blobs[i];
269 status = smb2_create_blob_add(
270 state, &state->in_cblobs, b->tag, b->data);
271 if (!NT_STATUS_IS_OK(status)) {
272 tevent_req_nterror(req, status);
273 return tevent_req_post(req, ev);
278 fname = smb2_dfs_share_path(state, cli, fname);
279 if (tevent_req_nomem(fname, req)) {
280 return tevent_req_post(req, ev);
282 fname_len = strlen(fname);
284 /* SMB2 is pickier about pathnames. Ensure it doesn't
285 start in a '\' */
286 if (*fname == '\\') {
287 fname++;
288 fname_len--;
291 /* Or end in a '\' */
292 if (fname_len > 0 && fname[fname_len-1] == '\\') {
293 fname[fname_len-1] = '\0';
296 subreq = smb2cli_create_send(state, ev,
297 cli->conn,
298 cli->timeout,
299 cli->smb2.session,
300 cli->smb2.tcon,
301 fname,
302 flags_to_smb2_oplock(create_flags),
303 impersonation_level,
304 desired_access,
305 file_attributes,
306 share_access,
307 create_disposition,
308 create_options,
309 &state->in_cblobs);
310 if (tevent_req_nomem(subreq, req)) {
311 return tevent_req_post(req, ev);
313 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
315 state->subreq = subreq;
316 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
318 return req;
321 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
323 struct tevent_req *req = tevent_req_callback_data(
324 subreq, struct tevent_req);
325 struct cli_smb2_create_fnum_state *state = tevent_req_data(
326 req, struct cli_smb2_create_fnum_state);
327 struct smb2_hnd h;
328 NTSTATUS status;
330 status = smb2cli_create_recv(
331 subreq,
332 &h.fid_persistent,
333 &h.fid_volatile, &state->cr,
334 state,
335 &state->out_cblobs,
336 &state->symlink);
337 TALLOC_FREE(subreq);
338 if (tevent_req_nterror(req, status)) {
339 return;
342 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
343 if (tevent_req_nterror(req, status)) {
344 return;
346 tevent_req_done(req);
349 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
351 struct cli_smb2_create_fnum_state *state = tevent_req_data(
352 req, struct cli_smb2_create_fnum_state);
353 return tevent_req_cancel(state->subreq);
356 NTSTATUS cli_smb2_create_fnum_recv(
357 struct tevent_req *req,
358 uint16_t *pfnum,
359 struct smb_create_returns *cr,
360 TALLOC_CTX *mem_ctx,
361 struct smb2_create_blobs *out_cblobs,
362 struct symlink_reparse_struct **symlink)
364 struct cli_smb2_create_fnum_state *state = tevent_req_data(
365 req, struct cli_smb2_create_fnum_state);
366 NTSTATUS status;
368 if (tevent_req_is_nterror(req, &status)) {
369 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
370 (symlink != NULL)) {
371 *symlink = talloc_move(mem_ctx, &state->symlink);
373 state->cli->raw_status = status;
374 return status;
376 if (pfnum != NULL) {
377 *pfnum = state->fnum;
379 if (cr != NULL) {
380 *cr = state->cr;
382 if (out_cblobs != NULL) {
383 *out_cblobs = (struct smb2_create_blobs) {
384 .num_blobs = state->out_cblobs.num_blobs,
385 .blobs = talloc_move(
386 mem_ctx, &state->out_cblobs.blobs),
389 state->cli->raw_status = NT_STATUS_OK;
390 return NT_STATUS_OK;
393 NTSTATUS cli_smb2_create_fnum(
394 struct cli_state *cli,
395 const char *fname,
396 struct cli_smb2_create_flags create_flags,
397 uint32_t impersonation_level,
398 uint32_t desired_access,
399 uint32_t file_attributes,
400 uint32_t share_access,
401 uint32_t create_disposition,
402 uint32_t create_options,
403 const struct smb2_create_blobs *in_cblobs,
404 uint16_t *pfid,
405 struct smb_create_returns *cr,
406 TALLOC_CTX *mem_ctx,
407 struct smb2_create_blobs *out_cblobs)
409 TALLOC_CTX *frame = talloc_stackframe();
410 struct tevent_context *ev;
411 struct tevent_req *req;
412 NTSTATUS status = NT_STATUS_NO_MEMORY;
414 if (smbXcli_conn_has_async_calls(cli->conn)) {
416 * Can't use sync call while an async call is in flight
418 status = NT_STATUS_INVALID_PARAMETER;
419 goto fail;
421 ev = samba_tevent_context_init(frame);
422 if (ev == NULL) {
423 goto fail;
425 req = cli_smb2_create_fnum_send(
426 frame,
428 cli,
429 fname,
430 create_flags,
431 impersonation_level,
432 desired_access,
433 file_attributes,
434 share_access,
435 create_disposition,
436 create_options,
437 in_cblobs);
438 if (req == NULL) {
439 goto fail;
441 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
442 goto fail;
444 status = cli_smb2_create_fnum_recv(
445 req, pfid, cr, mem_ctx, out_cblobs, NULL);
446 fail:
447 TALLOC_FREE(frame);
448 return status;
451 /***************************************************************
452 Small wrapper that allows SMB2 close to use a uint16_t fnum.
453 ***************************************************************/
455 struct cli_smb2_close_fnum_state {
456 struct cli_state *cli;
457 uint16_t fnum;
458 struct smb2_hnd *ph;
461 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
463 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
464 struct tevent_context *ev,
465 struct cli_state *cli,
466 uint16_t fnum)
468 struct tevent_req *req, *subreq;
469 struct cli_smb2_close_fnum_state *state;
470 NTSTATUS status;
472 req = tevent_req_create(mem_ctx, &state,
473 struct cli_smb2_close_fnum_state);
474 if (req == NULL) {
475 return NULL;
477 state->cli = cli;
478 state->fnum = fnum;
480 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
481 if (tevent_req_nterror(req, status)) {
482 return tevent_req_post(req, ev);
485 subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
486 cli->smb2.session, cli->smb2.tcon,
487 0, state->ph->fid_persistent,
488 state->ph->fid_volatile);
489 if (tevent_req_nomem(subreq, req)) {
490 return tevent_req_post(req, ev);
492 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
493 return req;
496 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
498 struct tevent_req *req = tevent_req_callback_data(
499 subreq, struct tevent_req);
500 struct cli_smb2_close_fnum_state *state = tevent_req_data(
501 req, struct cli_smb2_close_fnum_state);
502 NTSTATUS status;
504 status = smb2cli_close_recv(subreq);
505 if (tevent_req_nterror(req, status)) {
506 return;
509 /* Delete the fnum -> handle mapping. */
510 status = delete_smb2_handle_mapping(state->cli, &state->ph,
511 state->fnum);
512 if (tevent_req_nterror(req, status)) {
513 return;
515 tevent_req_done(req);
518 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
520 struct cli_smb2_close_fnum_state *state = tevent_req_data(
521 req, struct cli_smb2_close_fnum_state);
522 NTSTATUS status = NT_STATUS_OK;
524 if (tevent_req_is_nterror(req, &status)) {
525 state->cli->raw_status = status;
527 tevent_req_received(req);
528 return status;
531 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
533 TALLOC_CTX *frame = talloc_stackframe();
534 struct tevent_context *ev;
535 struct tevent_req *req;
536 NTSTATUS status = NT_STATUS_NO_MEMORY;
538 if (smbXcli_conn_has_async_calls(cli->conn)) {
540 * Can't use sync call while an async call is in flight
542 status = NT_STATUS_INVALID_PARAMETER;
543 goto fail;
545 ev = samba_tevent_context_init(frame);
546 if (ev == NULL) {
547 goto fail;
549 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
550 if (req == NULL) {
551 goto fail;
553 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
554 goto fail;
556 status = cli_smb2_close_fnum_recv(req);
557 fail:
558 TALLOC_FREE(frame);
559 return status;
562 struct cli_smb2_set_info_fnum_state {
563 uint8_t dummy;
566 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
568 struct tevent_req *cli_smb2_set_info_fnum_send(
569 TALLOC_CTX *mem_ctx,
570 struct tevent_context *ev,
571 struct cli_state *cli,
572 uint16_t fnum,
573 uint8_t in_info_type,
574 uint8_t in_info_class,
575 const DATA_BLOB *in_input_buffer,
576 uint32_t in_additional_info)
578 struct tevent_req *req = NULL, *subreq = NULL;
579 struct cli_smb2_set_info_fnum_state *state = NULL;
580 struct smb2_hnd *ph = NULL;
581 NTSTATUS status;
583 req = tevent_req_create(
584 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
585 if (req == NULL) {
586 return NULL;
589 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
590 if (tevent_req_nterror(req, status)) {
591 return tevent_req_post(req, ev);
594 subreq = smb2cli_set_info_send(
595 state,
597 cli->conn,
598 cli->timeout,
599 cli->smb2.session,
600 cli->smb2.tcon,
601 in_info_type,
602 in_info_class,
603 in_input_buffer,
604 in_additional_info,
605 ph->fid_persistent,
606 ph->fid_volatile);
607 if (tevent_req_nomem(subreq, req)) {
608 return tevent_req_post(req, ev);
610 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
611 return req;
614 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
616 NTSTATUS status = smb2cli_set_info_recv(subreq);
617 tevent_req_simple_finish_ntstatus(subreq, status);
620 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
622 return tevent_req_simple_recv_ntstatus(req);
625 NTSTATUS cli_smb2_set_info_fnum(
626 struct cli_state *cli,
627 uint16_t fnum,
628 uint8_t in_info_type,
629 uint8_t in_info_class,
630 const DATA_BLOB *in_input_buffer,
631 uint32_t in_additional_info)
633 TALLOC_CTX *frame = talloc_stackframe();
634 struct tevent_context *ev = NULL;
635 struct tevent_req *req = NULL;
636 NTSTATUS status = NT_STATUS_NO_MEMORY;
637 bool ok;
639 if (smbXcli_conn_has_async_calls(cli->conn)) {
641 * Can't use sync call while an async call is in flight
643 status = NT_STATUS_INVALID_PARAMETER;
644 goto fail;
646 ev = samba_tevent_context_init(frame);
647 if (ev == NULL) {
648 goto fail;
650 req = cli_smb2_set_info_fnum_send(
651 frame,
653 cli,
654 fnum,
655 in_info_type,
656 in_info_class,
657 in_input_buffer,
658 in_additional_info);
659 if (req == NULL) {
660 goto fail;
662 ok = tevent_req_poll_ntstatus(req, ev, &status);
663 if (!ok) {
664 goto fail;
666 status = cli_smb2_set_info_fnum_recv(req);
667 fail:
668 TALLOC_FREE(frame);
669 return status;
672 struct cli_smb2_delete_on_close_state {
673 struct cli_state *cli;
674 uint8_t data[1];
675 DATA_BLOB inbuf;
678 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
680 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
681 struct tevent_context *ev,
682 struct cli_state *cli,
683 uint16_t fnum,
684 bool flag)
686 struct tevent_req *req = NULL;
687 struct cli_smb2_delete_on_close_state *state = NULL;
688 struct tevent_req *subreq = NULL;
689 uint8_t in_info_type;
690 uint8_t in_file_info_class;
692 req = tevent_req_create(mem_ctx, &state,
693 struct cli_smb2_delete_on_close_state);
694 if (req == NULL) {
695 return NULL;
697 state->cli = cli;
700 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
701 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
703 in_info_type = 1;
704 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
705 /* Setup data array. */
706 SCVAL(&state->data[0], 0, flag ? 1 : 0);
707 state->inbuf.data = &state->data[0];
708 state->inbuf.length = 1;
710 subreq = cli_smb2_set_info_fnum_send(
711 state,
713 cli,
714 fnum,
715 in_info_type,
716 in_file_info_class,
717 &state->inbuf,
719 if (tevent_req_nomem(subreq, req)) {
720 return tevent_req_post(req, ev);
722 tevent_req_set_callback(subreq,
723 cli_smb2_delete_on_close_done,
724 req);
725 return req;
728 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
730 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
731 tevent_req_simple_finish_ntstatus(subreq, status);
734 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
736 struct cli_smb2_delete_on_close_state *state =
737 tevent_req_data(req,
738 struct cli_smb2_delete_on_close_state);
739 NTSTATUS status;
741 if (tevent_req_is_nterror(req, &status)) {
742 state->cli->raw_status = status;
743 tevent_req_received(req);
744 return status;
747 state->cli->raw_status = NT_STATUS_OK;
748 tevent_req_received(req);
749 return NT_STATUS_OK;
752 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
754 TALLOC_CTX *frame = talloc_stackframe();
755 struct tevent_context *ev;
756 struct tevent_req *req;
757 NTSTATUS status = NT_STATUS_NO_MEMORY;
759 if (smbXcli_conn_has_async_calls(cli->conn)) {
761 * Can't use sync call while an async call is in flight
763 status = NT_STATUS_INVALID_PARAMETER;
764 goto fail;
766 ev = samba_tevent_context_init(frame);
767 if (ev == NULL) {
768 goto fail;
770 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
771 if (req == NULL) {
772 goto fail;
774 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
775 goto fail;
777 status = cli_smb2_delete_on_close_recv(req);
778 fail:
779 TALLOC_FREE(frame);
780 return status;
783 struct cli_smb2_mkdir_state {
784 struct tevent_context *ev;
785 struct cli_state *cli;
788 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
789 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
791 struct tevent_req *cli_smb2_mkdir_send(
792 TALLOC_CTX *mem_ctx,
793 struct tevent_context *ev,
794 struct cli_state *cli,
795 const char *dname)
797 struct tevent_req *req = NULL, *subreq = NULL;
798 struct cli_smb2_mkdir_state *state = NULL;
800 req = tevent_req_create(
801 mem_ctx, &state, struct cli_smb2_mkdir_state);
802 if (req == NULL) {
803 return NULL;
805 state->ev = ev;
806 state->cli = cli;
808 /* Ensure this is a directory. */
809 subreq = cli_smb2_create_fnum_send(
810 state, /* mem_ctx */
811 ev, /* ev */
812 cli, /* cli */
813 dname, /* fname */
814 (struct cli_smb2_create_flags){0}, /* create_flags */
815 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
816 FILE_READ_ATTRIBUTES, /* desired_access */
817 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
818 FILE_SHARE_READ|
819 FILE_SHARE_WRITE, /* share_access */
820 FILE_CREATE, /* create_disposition */
821 FILE_DIRECTORY_FILE, /* create_options */
822 NULL); /* in_cblobs */
823 if (tevent_req_nomem(subreq, req)) {
824 return tevent_req_post(req, ev);
826 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
827 return req;
830 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
832 struct tevent_req *req = tevent_req_callback_data(
833 subreq, struct tevent_req);
834 struct cli_smb2_mkdir_state *state = tevent_req_data(
835 req, struct cli_smb2_mkdir_state);
836 NTSTATUS status;
837 uint16_t fnum = 0xffff;
839 status = cli_smb2_create_fnum_recv(
840 subreq, &fnum, NULL, NULL, NULL, NULL);
841 TALLOC_FREE(subreq);
842 if (tevent_req_nterror(req, status)) {
843 return;
846 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
847 if (tevent_req_nomem(subreq, req)) {
848 return;
850 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
853 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
855 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
856 tevent_req_simple_finish_ntstatus(subreq, status);
859 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
861 return tevent_req_simple_recv_ntstatus(req);
864 struct cli_smb2_rmdir_state {
865 struct tevent_context *ev;
866 struct cli_state *cli;
867 const char *dname;
868 const struct smb2_create_blobs *in_cblobs;
869 uint16_t fnum;
870 NTSTATUS status;
873 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
874 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
875 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
876 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
878 struct tevent_req *cli_smb2_rmdir_send(
879 TALLOC_CTX *mem_ctx,
880 struct tevent_context *ev,
881 struct cli_state *cli,
882 const char *dname,
883 const struct smb2_create_blobs *in_cblobs)
885 struct tevent_req *req = NULL, *subreq = NULL;
886 struct cli_smb2_rmdir_state *state = NULL;
888 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
889 if (req == NULL) {
890 return NULL;
892 state->ev = ev;
893 state->cli = cli;
894 state->dname = dname;
895 state->in_cblobs = in_cblobs;
897 subreq = cli_smb2_create_fnum_send(
898 state,
899 state->ev,
900 state->cli,
901 state->dname,
902 (struct cli_smb2_create_flags){0},
903 SMB2_IMPERSONATION_IMPERSONATION,
904 DELETE_ACCESS, /* desired_access */
905 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
906 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
907 FILE_OPEN, /* create_disposition */
908 FILE_DIRECTORY_FILE, /* create_options */
909 state->in_cblobs); /* in_cblobs */
910 if (tevent_req_nomem(subreq, req)) {
911 return tevent_req_post(req, ev);
913 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
914 return req;
917 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
919 struct tevent_req *req = tevent_req_callback_data(
920 subreq, struct tevent_req);
921 struct cli_smb2_rmdir_state *state = tevent_req_data(
922 req, struct cli_smb2_rmdir_state);
923 NTSTATUS status;
925 status = cli_smb2_create_fnum_recv(
926 subreq, &state->fnum, NULL, NULL, NULL, NULL);
927 TALLOC_FREE(subreq);
929 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
931 * Naive option to match our SMB1 code. Assume the
932 * symlink path that tripped us up was the last
933 * component and try again. Eventually we will have to
934 * deal with the returned path unprocessed component. JRA.
936 subreq = cli_smb2_create_fnum_send(
937 state,
938 state->ev,
939 state->cli,
940 state->dname,
941 (struct cli_smb2_create_flags){0},
942 SMB2_IMPERSONATION_IMPERSONATION,
943 DELETE_ACCESS, /* desired_access */
944 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
945 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
946 FILE_OPEN, /* create_disposition */
947 FILE_DIRECTORY_FILE|
948 FILE_DELETE_ON_CLOSE|
949 FILE_OPEN_REPARSE_POINT, /* create_options */
950 state->in_cblobs); /* in_cblobs */
951 if (tevent_req_nomem(subreq, req)) {
952 return;
954 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
955 return;
958 if (tevent_req_nterror(req, status)) {
959 return;
962 subreq = cli_smb2_delete_on_close_send(
963 state, state->ev, state->cli, state->fnum, true);
964 if (tevent_req_nomem(subreq, req)) {
965 return;
967 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
970 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
972 struct tevent_req *req = tevent_req_callback_data(
973 subreq, struct tevent_req);
974 struct cli_smb2_rmdir_state *state = tevent_req_data(
975 req, struct cli_smb2_rmdir_state);
976 NTSTATUS status;
978 status = cli_smb2_create_fnum_recv(
979 subreq, &state->fnum, NULL, NULL, NULL, NULL);
980 TALLOC_FREE(subreq);
981 if (tevent_req_nterror(req, status)) {
982 return;
985 subreq = cli_smb2_delete_on_close_send(
986 state, state->ev, state->cli, state->fnum, true);
987 if (tevent_req_nomem(subreq, req)) {
988 return;
990 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
993 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
995 struct tevent_req *req = tevent_req_callback_data(
996 subreq, struct tevent_req);
997 struct cli_smb2_rmdir_state *state = tevent_req_data(
998 req, struct cli_smb2_rmdir_state);
1000 state->status = cli_smb2_delete_on_close_recv(subreq);
1001 TALLOC_FREE(subreq);
1004 * Close the fd even if the set_disp failed
1007 subreq = cli_smb2_close_fnum_send(
1008 state, state->ev, state->cli, state->fnum);
1009 if (tevent_req_nomem(subreq, req)) {
1010 return;
1012 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1015 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1017 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1018 tevent_req_simple_finish_ntstatus(subreq, status);
1021 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1023 struct cli_smb2_rmdir_state *state = tevent_req_data(
1024 req, struct cli_smb2_rmdir_state);
1025 NTSTATUS status;
1027 if (tevent_req_is_nterror(req, &status)) {
1028 return status;
1030 return state->status;
1033 /***************************************************************
1034 Small wrapper that allows SMB2 to unlink a pathname.
1035 ***************************************************************/
1037 struct cli_smb2_unlink_state {
1038 struct tevent_context *ev;
1039 struct cli_state *cli;
1040 const char *fname;
1041 const struct smb2_create_blobs *in_cblobs;
1044 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1045 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1046 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1048 struct tevent_req *cli_smb2_unlink_send(
1049 TALLOC_CTX *mem_ctx,
1050 struct tevent_context *ev,
1051 struct cli_state *cli,
1052 const char *fname,
1053 const struct smb2_create_blobs *in_cblobs)
1055 struct tevent_req *req = NULL, *subreq = NULL;
1056 struct cli_smb2_unlink_state *state = NULL;
1058 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1059 if (req == NULL) {
1060 return NULL;
1062 state->ev = ev;
1063 state->cli = cli;
1064 state->fname = fname;
1065 state->in_cblobs = in_cblobs;
1067 subreq = cli_smb2_create_fnum_send(
1068 state, /* mem_ctx */
1069 state->ev, /* tevent_context */
1070 state->cli, /* cli_struct */
1071 state->fname, /* filename */
1072 (struct cli_smb2_create_flags){0},
1073 SMB2_IMPERSONATION_IMPERSONATION,
1074 DELETE_ACCESS, /* desired_access */
1075 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1076 FILE_SHARE_READ|
1077 FILE_SHARE_WRITE|
1078 FILE_SHARE_DELETE, /* share_access */
1079 FILE_OPEN, /* create_disposition */
1080 FILE_DELETE_ON_CLOSE, /* create_options */
1081 state->in_cblobs); /* in_cblobs */
1082 if (tevent_req_nomem(subreq, req)) {
1083 return tevent_req_post(req, ev);
1085 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1086 return req;
1089 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1091 struct tevent_req *req = tevent_req_callback_data(
1092 subreq, struct tevent_req);
1093 struct cli_smb2_unlink_state *state = tevent_req_data(
1094 req, struct cli_smb2_unlink_state);
1095 uint16_t fnum = 0xffff;
1096 NTSTATUS status;
1098 status = cli_smb2_create_fnum_recv(
1099 subreq, &fnum, NULL, NULL, NULL, NULL);
1100 TALLOC_FREE(subreq);
1102 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1103 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1105 * Naive option to match our SMB1 code. Assume the
1106 * symlink path that tripped us up was the last
1107 * component and try again. Eventually we will have to
1108 * deal with the returned path unprocessed component. JRA.
1110 subreq = cli_smb2_create_fnum_send(
1111 state, /* mem_ctx */
1112 state->ev, /* tevent_context */
1113 state->cli, /* cli_struct */
1114 state->fname, /* filename */
1115 (struct cli_smb2_create_flags){0},
1116 SMB2_IMPERSONATION_IMPERSONATION,
1117 DELETE_ACCESS, /* desired_access */
1118 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1119 FILE_SHARE_READ|
1120 FILE_SHARE_WRITE|
1121 FILE_SHARE_DELETE, /* share_access */
1122 FILE_OPEN, /* create_disposition */
1123 FILE_DELETE_ON_CLOSE|
1124 FILE_OPEN_REPARSE_POINT, /* create_options */
1125 state->in_cblobs); /* in_cblobs */
1126 if (tevent_req_nomem(subreq, req)) {
1127 return;
1129 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1130 return;
1133 if (tevent_req_nterror(req, status)) {
1134 return;
1137 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1138 if (tevent_req_nomem(subreq, req)) {
1139 return;
1141 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1144 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1146 struct tevent_req *req = tevent_req_callback_data(
1147 subreq, struct tevent_req);
1148 struct cli_smb2_unlink_state *state = tevent_req_data(
1149 req, struct cli_smb2_unlink_state);
1150 uint16_t fnum = 0xffff;
1151 NTSTATUS status;
1153 status = cli_smb2_create_fnum_recv(
1154 subreq, &fnum, NULL, NULL, NULL, NULL);
1155 TALLOC_FREE(subreq);
1156 if (tevent_req_nterror(req, status)) {
1157 return;
1160 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1161 if (tevent_req_nomem(subreq, req)) {
1162 return;
1164 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1167 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1169 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1170 tevent_req_simple_finish_ntstatus(subreq, status);
1173 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1175 return tevent_req_simple_recv_ntstatus(req);
1178 /***************************************************************
1179 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1180 ***************************************************************/
1182 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1183 uint32_t dir_data_length,
1184 struct file_info *finfo,
1185 uint32_t *next_offset)
1187 struct smb3_file_posix_information info = {};
1188 size_t consumed;
1189 enum ndr_err_code ndr_err;
1190 size_t namelen = 0;
1191 size_t ret = 0;
1192 uint32_t _next_offset = 0;
1194 if (dir_data_length < 4) {
1195 return NT_STATUS_INFO_LENGTH_MISMATCH;
1198 _next_offset = IVAL(dir_data, 0);
1200 if (_next_offset > dir_data_length) {
1201 return NT_STATUS_INFO_LENGTH_MISMATCH;
1204 if (_next_offset != 0) {
1205 /* Ensure we only read what in this record. */
1206 dir_data_length = _next_offset;
1210 * Skip NextEntryOffset and FileIndex
1212 if (dir_data_length < 8) {
1213 return NT_STATUS_INFO_LENGTH_MISMATCH;
1215 dir_data += 8;
1216 dir_data_length -= 8;
1218 ndr_err = ndr_pull_struct_blob_noalloc(
1219 dir_data,
1220 dir_data_length,
1221 &info,
1222 (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1223 &consumed);
1224 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1225 return ndr_map_error2ntstatus(ndr_err);
1227 if (consumed > dir_data_length) {
1228 return NT_STATUS_INFO_LENGTH_MISMATCH;
1230 dir_data += consumed;
1231 dir_data_length -= consumed;
1233 finfo->btime_ts = interpret_long_date(info.creation_time);
1234 finfo->atime_ts = interpret_long_date(info.last_access_time);
1235 finfo->mtime_ts = interpret_long_date(info.last_write_time);
1236 finfo->ctime_ts = interpret_long_date(info.change_time);
1237 finfo->allocated_size = info.allocation_size;
1238 finfo->size = info.end_of_file;
1239 finfo->mode = info.file_attributes;
1240 finfo->ino = info.inode;
1241 finfo->st_ex_dev = info.device;
1242 finfo->st_ex_nlink = info.cc.nlinks;
1243 finfo->reparse_tag = info.cc.reparse_tag;
1244 finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
1245 sid_copy(&finfo->owner_sid, &info.cc.owner);
1246 sid_copy(&finfo->group_sid, &info.cc.group);
1248 if (dir_data_length < 4) {
1249 return NT_STATUS_INFO_LENGTH_MISMATCH;
1251 namelen = PULL_LE_U32(dir_data, 0);
1253 dir_data += 4;
1254 dir_data_length -= 4;
1256 if (namelen > dir_data_length) {
1257 return NT_STATUS_INFO_LENGTH_MISMATCH;
1260 ret = pull_string_talloc(finfo,
1261 dir_data,
1262 FLAGS2_UNICODE_STRINGS,
1263 &finfo->name,
1264 dir_data,
1265 namelen,
1266 STR_UNICODE);
1267 if (ret == (size_t)-1) {
1268 /* Bad conversion. */
1269 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1272 if (finfo->name == NULL) {
1273 /* Bad conversion. */
1274 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1277 *next_offset = _next_offset;
1278 return NT_STATUS_OK;
1281 /***************************************************************
1282 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1283 ***************************************************************/
1285 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1286 uint32_t dir_data_length,
1287 struct file_info *finfo,
1288 uint32_t *next_offset)
1290 size_t namelen = 0;
1291 size_t slen = 0;
1292 size_t ret = 0;
1294 if (dir_data_length < 4) {
1295 return NT_STATUS_INFO_LENGTH_MISMATCH;
1298 *next_offset = IVAL(dir_data, 0);
1300 if (*next_offset > dir_data_length) {
1301 return NT_STATUS_INFO_LENGTH_MISMATCH;
1304 if (*next_offset != 0) {
1305 /* Ensure we only read what in this record. */
1306 dir_data_length = *next_offset;
1309 if (dir_data_length < 105) {
1310 return NT_STATUS_INFO_LENGTH_MISMATCH;
1313 finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1314 finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1315 finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1316 finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1317 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1318 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1319 finfo->attr = IVAL(dir_data + 56, 0);
1320 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1321 namelen = IVAL(dir_data + 60,0);
1322 if (namelen > (dir_data_length - 104)) {
1323 return NT_STATUS_INFO_LENGTH_MISMATCH;
1325 slen = CVAL(dir_data + 68, 0);
1326 if (slen > 24) {
1327 return NT_STATUS_INFO_LENGTH_MISMATCH;
1329 ret = pull_string_talloc(finfo,
1330 dir_data,
1331 FLAGS2_UNICODE_STRINGS,
1332 &finfo->short_name,
1333 dir_data + 70,
1334 slen,
1335 STR_UNICODE);
1336 if (ret == (size_t)-1) {
1337 /* Bad conversion. */
1338 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1341 ret = pull_string_talloc(finfo,
1342 dir_data,
1343 FLAGS2_UNICODE_STRINGS,
1344 &finfo->name,
1345 dir_data + 104,
1346 namelen,
1347 STR_UNICODE);
1348 if (ret == (size_t)-1) {
1349 /* Bad conversion. */
1350 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1353 if (finfo->name == NULL) {
1354 /* Bad conversion. */
1355 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1358 return NT_STATUS_OK;
1361 /*******************************************************************
1362 Given a filename - get its directory name
1363 ********************************************************************/
1365 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1366 const char *dir,
1367 char **parent,
1368 const char **name)
1370 char *p;
1371 ptrdiff_t len;
1373 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1375 if (p == NULL) {
1376 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1377 return false;
1379 if (name) {
1380 *name = dir;
1382 return true;
1385 len = p-dir;
1387 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1388 return false;
1390 (*parent)[len] = '\0';
1392 if (name) {
1393 *name = p+1;
1395 return true;
1398 struct cli_smb2_list_dir_data {
1399 uint8_t *data;
1400 uint32_t length;
1403 struct cli_smb2_list_state {
1404 struct tevent_context *ev;
1405 struct cli_state *cli;
1406 const char *mask;
1408 uint16_t fnum;
1410 NTSTATUS status;
1411 struct cli_smb2_list_dir_data *response;
1412 uint32_t offset;
1413 unsigned int info_level;
1416 static void cli_smb2_list_opened(struct tevent_req *subreq);
1417 static void cli_smb2_list_done(struct tevent_req *subreq);
1418 static void cli_smb2_list_closed(struct tevent_req *subreq);
1420 struct tevent_req *cli_smb2_list_send(
1421 TALLOC_CTX *mem_ctx,
1422 struct tevent_context *ev,
1423 struct cli_state *cli,
1424 const char *pathname,
1425 unsigned int info_level,
1426 bool posix)
1428 struct tevent_req *req = NULL, *subreq = NULL;
1429 struct cli_smb2_list_state *state = NULL;
1430 char *parent = NULL;
1431 bool ok;
1432 struct smb2_create_blobs *in_cblobs = NULL;
1434 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1435 if (req == NULL) {
1436 return NULL;
1438 state->ev = ev;
1439 state->cli = cli;
1440 state->status = NT_STATUS_OK;
1441 state->info_level = info_level;
1443 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1444 if (!ok) {
1445 tevent_req_oom(req);
1446 return tevent_req_post(req, ev);
1449 if (smbXcli_conn_have_posix(cli->conn) && posix) {
1450 NTSTATUS status;
1452 /* The mode MUST be 0 when opening an existing file/dir, and
1453 * will be ignored by the server.
1455 uint8_t linear_mode[4] = { 0 };
1456 DATA_BLOB blob = { .data=linear_mode,
1457 .length=sizeof(linear_mode) };
1459 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1460 if (in_cblobs == NULL) {
1461 return NULL;
1464 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1465 SMB2_CREATE_TAG_POSIX, blob);
1466 if (tevent_req_nterror(req, status)) {
1467 tevent_req_nterror(req, status);
1468 return tevent_req_post(req, ev);
1472 subreq = cli_smb2_create_fnum_send(
1473 state, /* mem_ctx */
1474 ev, /* ev */
1475 cli, /* cli */
1476 parent, /* fname */
1477 (struct cli_smb2_create_flags){0}, /* create_flags */
1478 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1479 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1480 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1481 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1482 FILE_OPEN, /* create_disposition */
1483 FILE_DIRECTORY_FILE, /* create_options */
1484 in_cblobs); /* in_cblobs */
1485 TALLOC_FREE(in_cblobs);
1486 if (tevent_req_nomem(subreq, req)) {
1487 return tevent_req_post(req, ev);
1489 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1490 return req;
1493 static void cli_smb2_list_opened(struct tevent_req *subreq)
1495 struct tevent_req *req = tevent_req_callback_data(
1496 subreq, struct tevent_req);
1497 struct cli_smb2_list_state *state = tevent_req_data(
1498 req, struct cli_smb2_list_state);
1499 NTSTATUS status;
1501 status = cli_smb2_create_fnum_recv(
1502 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1503 TALLOC_FREE(subreq);
1504 if (tevent_req_nterror(req, status)) {
1505 return;
1509 * Make our caller get back to us via cli_smb2_list_recv(),
1510 * triggering the smb2_query_directory_send()
1512 tevent_req_defer_callback(req, state->ev);
1513 tevent_req_notify_callback(req);
1516 static void cli_smb2_list_done(struct tevent_req *subreq)
1518 struct tevent_req *req = tevent_req_callback_data(
1519 subreq, struct tevent_req);
1520 struct cli_smb2_list_state *state = tevent_req_data(
1521 req, struct cli_smb2_list_state);
1522 struct cli_smb2_list_dir_data *response = NULL;
1524 response = talloc(state, struct cli_smb2_list_dir_data);
1525 if (tevent_req_nomem(response, req)) {
1526 return;
1529 state->status = smb2cli_query_directory_recv(
1530 subreq, response, &response->data, &response->length);
1531 TALLOC_FREE(subreq);
1533 if (NT_STATUS_IS_OK(state->status)) {
1534 state->response = response;
1535 state->offset = 0;
1537 tevent_req_defer_callback(req, state->ev);
1538 tevent_req_notify_callback(req);
1539 return;
1542 TALLOC_FREE(response);
1544 subreq = cli_smb2_close_fnum_send(
1545 state, state->ev, state->cli, state->fnum);
1546 if (tevent_req_nomem(subreq, req)) {
1547 return;
1549 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1552 static void cli_smb2_list_closed(struct tevent_req *subreq)
1554 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1555 tevent_req_simple_finish_ntstatus(subreq, status);
1559 * Return the next finfo directory.
1561 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1562 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1563 * NT_STATUS_RETRY, which will then trigger the caller again when the
1564 * QUERY_DIRECTORY has returned with another buffer. This way we
1565 * guarantee that no asynchronous request is open after this call
1566 * returns an entry, so that other synchronous requests can be issued
1567 * on the same connection while the directory listing proceeds.
1569 NTSTATUS cli_smb2_list_recv(
1570 struct tevent_req *req,
1571 TALLOC_CTX *mem_ctx,
1572 struct file_info **pfinfo)
1574 struct cli_smb2_list_state *state = tevent_req_data(
1575 req, struct cli_smb2_list_state);
1576 struct cli_smb2_list_dir_data *response = NULL;
1577 struct file_info *finfo = NULL;
1578 NTSTATUS status;
1579 uint32_t next_offset = 0;
1580 bool in_progress;
1582 in_progress = tevent_req_is_in_progress(req);
1584 if (!in_progress) {
1585 if (!tevent_req_is_nterror(req, &status)) {
1586 status = NT_STATUS_NO_MORE_FILES;
1588 goto fail;
1591 response = state->response;
1592 if (response == NULL) {
1593 struct tevent_req *subreq = NULL;
1594 struct cli_state *cli = state->cli;
1595 struct smb2_hnd *ph = NULL;
1596 uint32_t max_trans, max_avail_len;
1597 bool ok;
1599 if (!NT_STATUS_IS_OK(state->status)) {
1600 status = state->status;
1601 goto fail;
1604 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1605 if (!NT_STATUS_IS_OK(status)) {
1606 goto fail;
1609 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1610 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1611 if (ok) {
1612 max_trans = MIN(max_trans, max_avail_len);
1615 subreq = smb2cli_query_directory_send(
1616 state, /* mem_ctx */
1617 state->ev, /* ev */
1618 cli->conn, /* conn */
1619 cli->timeout, /* timeout_msec */
1620 cli->smb2.session, /* session */
1621 cli->smb2.tcon, /* tcon */
1622 state->info_level, /* level */
1623 0, /* flags */
1624 0, /* file_index */
1625 ph->fid_persistent, /* fid_persistent */
1626 ph->fid_volatile, /* fid_volatile */
1627 state->mask, /* mask */
1628 max_trans); /* outbuf_len */
1629 if (subreq == NULL) {
1630 status = NT_STATUS_NO_MEMORY;
1631 goto fail;
1633 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1634 return NT_STATUS_RETRY;
1637 SMB_ASSERT(response->length > state->offset);
1639 finfo = talloc_zero(mem_ctx, struct file_info);
1640 if (finfo == NULL) {
1641 status = NT_STATUS_NO_MEMORY;
1642 goto fail;
1645 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1646 status = parse_finfo_posix_info(
1647 response->data + state->offset,
1648 response->length - state->offset,
1649 finfo,
1650 &next_offset);
1651 } else {
1652 status = parse_finfo_id_both_directory_info(
1653 response->data + state->offset,
1654 response->length - state->offset,
1655 finfo,
1656 &next_offset);
1658 if (!NT_STATUS_IS_OK(status)) {
1659 goto fail;
1662 status = is_bad_finfo_name(state->cli, finfo);
1663 if (!NT_STATUS_IS_OK(status)) {
1664 goto fail;
1668 * parse_finfo_id_both_directory_info() checks for overflow,
1669 * no need to check again here.
1671 state->offset += next_offset;
1673 if (next_offset == 0) {
1674 TALLOC_FREE(state->response);
1677 tevent_req_defer_callback(req, state->ev);
1678 tevent_req_notify_callback(req);
1680 *pfinfo = finfo;
1681 return NT_STATUS_OK;
1683 fail:
1684 TALLOC_FREE(finfo);
1685 tevent_req_received(req);
1686 return status;
1689 /***************************************************************
1690 Wrapper that allows SMB2 to query a path info (basic level).
1691 Synchronous only.
1692 ***************************************************************/
1694 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1695 const char *name,
1696 SMB_STRUCT_STAT *sbuf,
1697 uint32_t *attributes)
1699 NTSTATUS status;
1700 struct smb_create_returns cr;
1701 uint16_t fnum = 0xffff;
1702 size_t namelen = strlen(name);
1704 if (smbXcli_conn_has_async_calls(cli->conn)) {
1706 * Can't use sync call while an async call is in flight
1708 return NT_STATUS_INVALID_PARAMETER;
1711 /* SMB2 is pickier about pathnames. Ensure it doesn't
1712 end in a '\' */
1713 if (namelen > 0 && name[namelen-1] == '\\') {
1714 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1715 if (modname == NULL) {
1716 return NT_STATUS_NO_MEMORY;
1718 name = modname;
1721 /* This is commonly used as a 'cd'. Try qpathinfo on
1722 a directory handle first. */
1724 status = cli_smb2_create_fnum(cli,
1725 name,
1726 (struct cli_smb2_create_flags){0},
1727 SMB2_IMPERSONATION_IMPERSONATION,
1728 FILE_READ_ATTRIBUTES, /* desired_access */
1729 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1730 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1731 FILE_OPEN, /* create_disposition */
1732 FILE_DIRECTORY_FILE, /* create_options */
1733 NULL,
1734 &fnum,
1735 &cr,
1736 NULL,
1737 NULL);
1739 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1740 /* Maybe a file ? */
1741 status = cli_smb2_create_fnum(cli,
1742 name,
1743 (struct cli_smb2_create_flags){0},
1744 SMB2_IMPERSONATION_IMPERSONATION,
1745 FILE_READ_ATTRIBUTES, /* desired_access */
1746 0, /* file attributes */
1747 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1748 FILE_OPEN, /* create_disposition */
1749 0, /* create_options */
1750 NULL,
1751 &fnum,
1752 &cr,
1753 NULL,
1754 NULL);
1757 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1758 /* Maybe a reparse point ? */
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 0, /* file attributes */
1765 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1766 FILE_OPEN, /* create_disposition */
1767 FILE_OPEN_REPARSE_POINT, /* create_options */
1768 NULL,
1769 &fnum,
1770 &cr,
1771 NULL,
1772 NULL);
1775 if (!NT_STATUS_IS_OK(status)) {
1776 return status;
1779 status = cli_smb2_close_fnum(cli, fnum);
1781 ZERO_STRUCTP(sbuf);
1783 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1784 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1785 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1786 sbuf->st_ex_size = cr.end_of_file;
1787 *attributes = cr.file_attributes;
1789 return status;
1792 struct cli_smb2_query_info_fnum_state {
1793 DATA_BLOB outbuf;
1796 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1798 struct tevent_req *cli_smb2_query_info_fnum_send(
1799 TALLOC_CTX *mem_ctx,
1800 struct tevent_context *ev,
1801 struct cli_state *cli,
1802 uint16_t fnum,
1803 uint8_t in_info_type,
1804 uint8_t in_info_class,
1805 uint32_t in_max_output_length,
1806 const DATA_BLOB *in_input_buffer,
1807 uint32_t in_additional_info,
1808 uint32_t in_flags)
1810 struct tevent_req *req = NULL, *subreq = NULL;
1811 struct cli_smb2_query_info_fnum_state *state = NULL;
1812 struct smb2_hnd *ph = NULL;
1813 NTSTATUS status;
1815 req = tevent_req_create(
1816 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1817 if (req == NULL) {
1818 return req;
1821 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1822 if (tevent_req_nterror(req, status)) {
1823 return tevent_req_post(req, ev);
1826 subreq = smb2cli_query_info_send(
1827 state,
1829 cli->conn,
1830 cli->timeout,
1831 cli->smb2.session,
1832 cli->smb2.tcon,
1833 in_info_type,
1834 in_info_class,
1835 in_max_output_length,
1836 in_input_buffer,
1837 in_additional_info,
1838 in_flags,
1839 ph->fid_persistent,
1840 ph->fid_volatile);
1841 if (tevent_req_nomem(subreq, req)) {
1842 return tevent_req_post(req, ev);
1844 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1845 return req;
1848 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1850 struct tevent_req *req = tevent_req_callback_data(
1851 subreq, struct tevent_req);
1852 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1853 req, struct cli_smb2_query_info_fnum_state);
1854 DATA_BLOB outbuf;
1855 NTSTATUS status;
1857 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1858 TALLOC_FREE(subreq);
1859 if (tevent_req_nterror(req, status)) {
1860 return;
1864 * We have to dup the memory here because outbuf.data is not
1865 * returned as a talloc object by smb2cli_query_info_recv.
1866 * It's a pointer into the received buffer.
1868 state->outbuf = data_blob_dup_talloc(state, outbuf);
1870 if ((outbuf.length != 0) &&
1871 tevent_req_nomem(state->outbuf.data, req)) {
1872 return;
1874 tevent_req_done(req);
1877 NTSTATUS cli_smb2_query_info_fnum_recv(
1878 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1880 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1881 req, struct cli_smb2_query_info_fnum_state);
1882 NTSTATUS status;
1884 if (tevent_req_is_nterror(req, &status)) {
1885 return status;
1887 *outbuf = (DATA_BLOB) {
1888 .data = talloc_move(mem_ctx, &state->outbuf.data),
1889 .length = state->outbuf.length,
1891 return NT_STATUS_OK;
1894 NTSTATUS cli_smb2_query_info_fnum(
1895 struct cli_state *cli,
1896 uint16_t fnum,
1897 uint8_t in_info_type,
1898 uint8_t in_info_class,
1899 uint32_t in_max_output_length,
1900 const DATA_BLOB *in_input_buffer,
1901 uint32_t in_additional_info,
1902 uint32_t in_flags,
1903 TALLOC_CTX *mem_ctx,
1904 DATA_BLOB *outbuf)
1906 TALLOC_CTX *frame = talloc_stackframe();
1907 struct tevent_context *ev = NULL;
1908 struct tevent_req *req = NULL;
1909 NTSTATUS status = NT_STATUS_NO_MEMORY;
1910 bool ok;
1912 if (smbXcli_conn_has_async_calls(cli->conn)) {
1914 * Can't use sync call while an async call is in flight
1916 status = NT_STATUS_INVALID_PARAMETER;
1917 goto fail;
1919 ev = samba_tevent_context_init(frame);
1920 if (ev == NULL) {
1921 goto fail;
1923 req = cli_smb2_query_info_fnum_send(
1924 frame,
1926 cli,
1927 fnum,
1928 in_info_type,
1929 in_info_class,
1930 in_max_output_length,
1931 in_input_buffer,
1932 in_additional_info,
1933 in_flags);
1934 if (req == NULL) {
1935 goto fail;
1937 ok = tevent_req_poll_ntstatus(req, ev, &status);
1938 if (!ok) {
1939 goto fail;
1941 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1942 fail:
1943 TALLOC_FREE(frame);
1944 return status;
1947 /***************************************************************
1948 Helper function for pathname operations.
1949 ***************************************************************/
1951 struct get_fnum_from_path_state {
1952 struct tevent_context *ev;
1953 struct cli_state *cli;
1954 const char *name;
1955 uint32_t desired_access;
1956 uint16_t fnum;
1959 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1960 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1961 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1963 static struct tevent_req *get_fnum_from_path_send(
1964 TALLOC_CTX *mem_ctx,
1965 struct tevent_context *ev,
1966 struct cli_state *cli,
1967 const char *name,
1968 uint32_t desired_access)
1970 struct tevent_req *req = NULL, *subreq = NULL;
1971 struct get_fnum_from_path_state *state = NULL;
1972 size_t namelen = strlen(name);
1974 req = tevent_req_create(
1975 mem_ctx, &state, struct get_fnum_from_path_state);
1976 if (req == NULL) {
1977 return NULL;
1979 state->ev = ev;
1980 state->cli = cli;
1981 state->name = name;
1982 state->desired_access = desired_access;
1985 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
1986 * '\'
1988 if (namelen > 0 && name[namelen-1] == '\\') {
1989 state->name = talloc_strndup(state, name, namelen-1);
1990 if (tevent_req_nomem(state->name, req)) {
1991 return tevent_req_post(req, ev);
1995 subreq = cli_smb2_create_fnum_send(
1996 state, /* mem_ctx, */
1997 ev, /* ev */
1998 cli, /* cli */
1999 state->name, /* fname */
2000 (struct cli_smb2_create_flags){0}, /* create_flags */
2001 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2002 desired_access, /* desired_access */
2003 0, /* file_attributes */
2004 FILE_SHARE_READ|
2005 FILE_SHARE_WRITE|
2006 FILE_SHARE_DELETE, /* share_access */
2007 FILE_OPEN, /* create_disposition */
2008 0, /* create_options */
2009 NULL); /* in_cblobs */
2010 if (tevent_req_nomem(subreq, req)) {
2011 return tevent_req_post(req, ev);
2013 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2014 return req;
2017 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2019 struct tevent_req *req = tevent_req_callback_data(
2020 subreq, struct tevent_req);
2021 struct get_fnum_from_path_state *state = tevent_req_data(
2022 req, struct get_fnum_from_path_state);
2023 NTSTATUS status;
2025 status = cli_smb2_create_fnum_recv(
2026 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2027 TALLOC_FREE(subreq);
2029 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2030 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2032 * Naive option to match our SMB1 code. Assume the
2033 * symlink path that tripped us up was the last
2034 * component and try again. Eventually we will have to
2035 * deal with the returned path unprocessed component. JRA.
2037 subreq = cli_smb2_create_fnum_send(
2038 state, /* mem_ctx, */
2039 state->ev, /* ev */
2040 state->cli, /* cli */
2041 state->name, /* fname */
2042 (struct cli_smb2_create_flags){0}, /* create_flags */
2043 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2044 state->desired_access, /* desired_access */
2045 0, /* file_attributes */
2046 FILE_SHARE_READ|
2047 FILE_SHARE_WRITE|
2048 FILE_SHARE_DELETE, /* share_access */
2049 FILE_OPEN, /* create_disposition */
2050 FILE_OPEN_REPARSE_POINT, /* create_options */
2051 NULL); /* in_cblobs */
2052 if (tevent_req_nomem(subreq, req)) {
2053 return;
2055 tevent_req_set_callback(
2056 subreq, get_fnum_from_path_opened_reparse, req);
2057 return;
2060 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2061 subreq = cli_smb2_create_fnum_send(
2062 state, /* mem_ctx, */
2063 state->ev, /* ev */
2064 state->cli, /* cli */
2065 state->name, /* fname */
2066 (struct cli_smb2_create_flags){0}, /* create_flags */
2067 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2068 state->desired_access, /* desired_access */
2069 0, /* file_attributes */
2070 FILE_SHARE_READ|
2071 FILE_SHARE_WRITE|
2072 FILE_SHARE_DELETE, /* share_access */
2073 FILE_OPEN, /* create_disposition */
2074 FILE_DIRECTORY_FILE, /* create_options */
2075 NULL); /* in_cblobs */
2076 if (tevent_req_nomem(subreq, req)) {
2077 return;
2079 tevent_req_set_callback(
2080 subreq, get_fnum_from_path_opened_dir, req);
2081 return;
2084 if (tevent_req_nterror(req, status)) {
2085 return;
2087 tevent_req_done(req);
2090 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2092 struct tevent_req *req = tevent_req_callback_data(
2093 subreq, struct tevent_req);
2094 struct get_fnum_from_path_state *state = tevent_req_data(
2095 req, struct get_fnum_from_path_state);
2096 NTSTATUS status = cli_smb2_create_fnum_recv(
2097 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2098 tevent_req_simple_finish_ntstatus(subreq, status);
2101 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2103 /* Abstraction violation, but these two are just the same... */
2104 get_fnum_from_path_opened_reparse(subreq);
2107 static NTSTATUS get_fnum_from_path_recv(
2108 struct tevent_req *req, uint16_t *pfnum)
2110 struct get_fnum_from_path_state *state = tevent_req_data(
2111 req, struct get_fnum_from_path_state);
2112 NTSTATUS status = NT_STATUS_OK;
2114 if (!tevent_req_is_nterror(req, &status)) {
2115 *pfnum = state->fnum;
2117 tevent_req_received(req);
2118 return status;
2121 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2122 const char *name,
2123 uint32_t desired_access,
2124 uint16_t *pfnum)
2126 TALLOC_CTX *frame = talloc_stackframe();
2127 struct tevent_context *ev = NULL;
2128 struct tevent_req *req = NULL;
2129 NTSTATUS status = NT_STATUS_NO_MEMORY;
2131 if (smbXcli_conn_has_async_calls(cli->conn)) {
2132 status = NT_STATUS_INVALID_PARAMETER;
2133 goto fail;
2135 ev = samba_tevent_context_init(frame);
2136 if (ev == NULL) {
2137 goto fail;
2139 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2140 if (req == NULL) {
2141 goto fail;
2143 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2144 goto fail;
2146 status = get_fnum_from_path_recv(req, pfnum);
2147 fail:
2148 TALLOC_FREE(frame);
2149 return status;
2152 /***************************************************************
2153 Wrapper that allows SMB2 to query a path info (ALTNAME level).
2154 Synchronous only.
2155 ***************************************************************/
2157 NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
2158 const char *name,
2159 fstring alt_name)
2161 NTSTATUS status;
2162 DATA_BLOB outbuf = data_blob_null;
2163 uint16_t fnum = 0xffff;
2164 uint32_t altnamelen = 0;
2165 TALLOC_CTX *frame = talloc_stackframe();
2167 if (smbXcli_conn_has_async_calls(cli->conn)) {
2169 * Can't use sync call while an async call is in flight
2171 status = NT_STATUS_INVALID_PARAMETER;
2172 goto fail;
2175 status = get_fnum_from_path(cli,
2176 name,
2177 FILE_READ_ATTRIBUTES,
2178 &fnum);
2180 if (!NT_STATUS_IS_OK(status)) {
2181 goto fail;
2184 status = cli_smb2_query_info_fnum(
2185 cli,
2186 fnum,
2187 1, /* in_info_type */
2188 (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
2189 0xFFFF, /* in_max_output_length */
2190 NULL, /* in_input_buffer */
2191 0, /* in_additional_info */
2192 0, /* in_flags */
2193 frame,
2194 &outbuf);
2196 if (!NT_STATUS_IS_OK(status)) {
2197 goto fail;
2200 /* Parse the reply. */
2201 if (outbuf.length < 4) {
2202 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2203 goto fail;
2206 altnamelen = IVAL(outbuf.data, 0);
2207 if (altnamelen > outbuf.length - 4) {
2208 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2209 goto fail;
2212 if (altnamelen > 0) {
2213 size_t ret = 0;
2214 char *short_name = NULL;
2215 ret = pull_string_talloc(frame,
2216 outbuf.data,
2217 FLAGS2_UNICODE_STRINGS,
2218 &short_name,
2219 outbuf.data + 4,
2220 altnamelen,
2221 STR_UNICODE);
2222 if (ret == (size_t)-1) {
2223 /* Bad conversion. */
2224 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2225 goto fail;
2228 fstrcpy(alt_name, short_name);
2229 } else {
2230 alt_name[0] = '\0';
2233 status = NT_STATUS_OK;
2235 fail:
2237 if (fnum != 0xffff) {
2238 cli_smb2_close_fnum(cli, fnum);
2241 cli->raw_status = status;
2243 TALLOC_FREE(frame);
2244 return status;
2247 struct cli_smb2_qpathinfo_state {
2248 struct tevent_context *ev;
2249 struct cli_state *cli;
2250 const char *fname;
2251 uint16_t fnum;
2252 uint16_t level;
2253 uint32_t min_rdata;
2254 uint32_t max_rdata;
2256 NTSTATUS status;
2257 DATA_BLOB out;
2260 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2261 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2262 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2264 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2265 struct tevent_context *ev,
2266 struct cli_state *cli,
2267 const char *fname,
2268 uint16_t level,
2269 uint32_t min_rdata,
2270 uint32_t max_rdata)
2272 struct tevent_req *req = NULL, *subreq = NULL;
2273 struct cli_smb2_qpathinfo_state *state = NULL;
2275 req = tevent_req_create(mem_ctx,
2276 &state,
2277 struct cli_smb2_qpathinfo_state);
2278 if (req == NULL) {
2279 return NULL;
2281 state->ev = ev;
2282 state->cli = cli;
2283 state->level = level;
2284 state->min_rdata = min_rdata;
2285 state->max_rdata = max_rdata;
2287 subreq = get_fnum_from_path_send(state,
2289 cli,
2290 fname,
2291 FILE_READ_ATTRIBUTES);
2292 if (tevent_req_nomem(subreq, req)) {
2293 return tevent_req_post(req, ev);
2295 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2296 return req;
2299 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2301 struct tevent_req *req =
2302 tevent_req_callback_data(subreq, struct tevent_req);
2303 struct cli_smb2_qpathinfo_state *state =
2304 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2305 NTSTATUS status;
2307 status = get_fnum_from_path_recv(subreq, &state->fnum);
2308 TALLOC_FREE(subreq);
2309 if (tevent_req_nterror(req, status)) {
2310 return;
2313 subreq = cli_smb2_query_info_fnum_send(state,
2314 state->ev,
2315 state->cli,
2316 state->fnum,
2317 1, /* in_info_type */
2318 state->level,
2319 state->max_rdata,
2320 NULL, /* in_input_buffer */
2321 0, /* in_additional_info */
2322 0); /* in_flags */
2323 if (tevent_req_nomem(subreq, req)) {
2324 return;
2326 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2329 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2331 struct tevent_req *req =
2332 tevent_req_callback_data(subreq, struct tevent_req);
2333 struct cli_smb2_qpathinfo_state *state =
2334 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2336 state->status =
2337 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2338 TALLOC_FREE(subreq);
2340 if (NT_STATUS_IS_OK(state->status) &&
2341 (state->out.length < state->min_rdata)) {
2342 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2345 subreq = cli_smb2_close_fnum_send(state,
2346 state->ev,
2347 state->cli,
2348 state->fnum);
2349 if (tevent_req_nomem(subreq, req)) {
2350 return;
2352 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2355 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2357 struct tevent_req *req =
2358 tevent_req_callback_data(subreq, struct tevent_req);
2359 struct cli_smb2_qpathinfo_state *state =
2360 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2361 NTSTATUS status;
2363 status = cli_smb2_close_fnum_recv(subreq);
2364 TALLOC_FREE(subreq);
2365 if (tevent_req_nterror(req, status)) {
2366 return;
2368 if (tevent_req_nterror(req, state->status)) {
2369 return;
2371 tevent_req_done(req);
2374 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2375 TALLOC_CTX *mem_ctx,
2376 uint8_t **rdata,
2377 uint32_t *num_rdata)
2379 struct cli_smb2_qpathinfo_state *state =
2380 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2381 NTSTATUS status;
2383 if (tevent_req_is_nterror(req, &status)) {
2384 return status;
2387 *rdata = talloc_move(mem_ctx, &state->out.data);
2388 *num_rdata = state->out.length;
2389 tevent_req_received(req);
2390 return NT_STATUS_OK;
2393 /***************************************************************
2394 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2395 a pathname.
2396 Synchronous only.
2397 ***************************************************************/
2399 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2400 const char *name,
2401 uint8_t in_info_type,
2402 uint8_t in_file_info_class,
2403 const DATA_BLOB *p_in_data)
2405 NTSTATUS status;
2406 uint16_t fnum = 0xffff;
2407 TALLOC_CTX *frame = talloc_stackframe();
2409 if (smbXcli_conn_has_async_calls(cli->conn)) {
2411 * Can't use sync call while an async call is in flight
2413 status = NT_STATUS_INVALID_PARAMETER;
2414 goto fail;
2417 status = get_fnum_from_path(cli,
2418 name,
2419 FILE_WRITE_ATTRIBUTES,
2420 &fnum);
2422 if (!NT_STATUS_IS_OK(status)) {
2423 goto fail;
2426 status = cli_smb2_set_info_fnum(
2427 cli,
2428 fnum,
2429 in_info_type,
2430 in_file_info_class,
2431 p_in_data, /* in_input_buffer */
2432 0); /* in_additional_info */
2433 fail:
2435 if (fnum != 0xffff) {
2436 cli_smb2_close_fnum(cli, fnum);
2439 cli->raw_status = status;
2441 TALLOC_FREE(frame);
2442 return status;
2446 /***************************************************************
2447 Wrapper that allows SMB2 to set pathname attributes.
2448 Synchronous only.
2449 ***************************************************************/
2451 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2452 const char *name,
2453 uint32_t attr,
2454 time_t mtime)
2456 uint8_t inbuf_store[40];
2457 DATA_BLOB inbuf = data_blob_null;
2459 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2460 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2462 inbuf.data = inbuf_store;
2463 inbuf.length = sizeof(inbuf_store);
2464 data_blob_clear(&inbuf);
2467 * SMB1 uses attr == 0 to clear all attributes
2468 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2469 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2470 * request attribute change.
2472 * SMB2 uses exactly the reverse. Unfortunately as the
2473 * cli_setatr() ABI is exposed inside libsmbclient,
2474 * we must make the SMB2 cli_smb2_setatr() call
2475 * export the same ABI as the SMB1 cli_setatr()
2476 * which calls it. This means reversing the sense
2477 * of the requested attr argument if it's zero
2478 * or FILE_ATTRIBUTE_NORMAL.
2480 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2483 if (attr == 0) {
2484 attr = FILE_ATTRIBUTE_NORMAL;
2485 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2486 attr = 0;
2489 SIVAL(inbuf.data, 32, attr);
2490 if (mtime != 0) {
2491 put_long_date((char *)inbuf.data + 16,mtime);
2493 /* Set all the other times to -1. */
2494 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2495 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2496 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2498 return cli_smb2_setpathinfo(cli,
2499 name,
2500 1, /* in_info_type */
2501 /* in_file_info_class */
2502 SMB_FILE_BASIC_INFORMATION - 1000,
2503 &inbuf);
2507 /***************************************************************
2508 Wrapper that allows SMB2 to set file handle times.
2509 Synchronous only.
2510 ***************************************************************/
2512 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2513 uint16_t fnum,
2514 time_t change_time,
2515 time_t access_time,
2516 time_t write_time)
2518 uint8_t inbuf_store[40];
2519 DATA_BLOB inbuf = data_blob_null;
2520 NTSTATUS status;
2522 if (smbXcli_conn_has_async_calls(cli->conn)) {
2524 * Can't use sync call while an async call is in flight
2526 return NT_STATUS_INVALID_PARAMETER;
2529 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2530 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2532 inbuf.data = inbuf_store;
2533 inbuf.length = sizeof(inbuf_store);
2534 data_blob_clear(&inbuf);
2536 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2537 if (change_time != 0) {
2538 put_long_date((char *)inbuf.data + 24, change_time);
2540 if (access_time != 0) {
2541 put_long_date((char *)inbuf.data + 8, access_time);
2543 if (write_time != 0) {
2544 put_long_date((char *)inbuf.data + 16, write_time);
2547 status = cli_smb2_set_info_fnum(cli,
2548 fnum,
2549 1, /* in_info_type */
2550 SMB_FILE_BASIC_INFORMATION -
2551 1000, /* in_file_info_class */
2552 &inbuf, /* in_input_buffer */
2553 0); /* in_additional_info */
2554 cli->raw_status = status;
2555 return status;
2558 /***************************************************************
2559 Wrapper that allows SMB2 to query disk attributes (size).
2560 Synchronous only.
2561 ***************************************************************/
2563 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2564 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2566 NTSTATUS status;
2567 uint16_t fnum = 0xffff;
2568 DATA_BLOB outbuf = data_blob_null;
2569 uint32_t sectors_per_unit = 0;
2570 uint32_t bytes_per_sector = 0;
2571 uint64_t total_size = 0;
2572 uint64_t size_free = 0;
2573 TALLOC_CTX *frame = talloc_stackframe();
2575 if (smbXcli_conn_has_async_calls(cli->conn)) {
2577 * Can't use sync call while an async call is in flight
2579 status = NT_STATUS_INVALID_PARAMETER;
2580 goto fail;
2583 /* First open the top level directory. */
2584 status = cli_smb2_create_fnum(cli,
2585 path,
2586 (struct cli_smb2_create_flags){0},
2587 SMB2_IMPERSONATION_IMPERSONATION,
2588 FILE_READ_ATTRIBUTES, /* desired_access */
2589 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2590 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2591 FILE_OPEN, /* create_disposition */
2592 FILE_DIRECTORY_FILE, /* create_options */
2593 NULL,
2594 &fnum,
2595 NULL,
2596 NULL,
2597 NULL);
2599 if (!NT_STATUS_IS_OK(status)) {
2600 goto fail;
2603 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2604 level 3 (SMB_FS_SIZE_INFORMATION). */
2606 status = cli_smb2_query_info_fnum(
2607 cli,
2608 fnum,
2609 2, /* in_info_type */
2610 3, /* in_file_info_class */
2611 0xFFFF, /* in_max_output_length */
2612 NULL, /* in_input_buffer */
2613 0, /* in_additional_info */
2614 0, /* in_flags */
2615 frame,
2616 &outbuf);
2617 if (!NT_STATUS_IS_OK(status)) {
2618 goto fail;
2621 /* Parse the reply. */
2622 if (outbuf.length != 24) {
2623 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2624 goto fail;
2627 total_size = BVAL(outbuf.data, 0);
2628 size_free = BVAL(outbuf.data, 8);
2629 sectors_per_unit = IVAL(outbuf.data, 16);
2630 bytes_per_sector = IVAL(outbuf.data, 20);
2632 if (bsize) {
2633 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2635 if (total) {
2636 *total = total_size;
2638 if (avail) {
2639 *avail = size_free;
2642 status = NT_STATUS_OK;
2644 fail:
2646 if (fnum != 0xffff) {
2647 cli_smb2_close_fnum(cli, fnum);
2650 cli->raw_status = status;
2652 TALLOC_FREE(frame);
2653 return status;
2656 /***************************************************************
2657 Wrapper that allows SMB2 to query file system sizes.
2658 Synchronous only.
2659 ***************************************************************/
2661 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2662 uint64_t *total_allocation_units,
2663 uint64_t *caller_allocation_units,
2664 uint64_t *actual_allocation_units,
2665 uint64_t *sectors_per_allocation_unit,
2666 uint64_t *bytes_per_sector)
2668 NTSTATUS status;
2669 uint16_t fnum = 0xffff;
2670 DATA_BLOB outbuf = data_blob_null;
2671 TALLOC_CTX *frame = talloc_stackframe();
2673 if (smbXcli_conn_has_async_calls(cli->conn)) {
2675 * Can't use sync call while an async call is in flight
2677 status = NT_STATUS_INVALID_PARAMETER;
2678 goto fail;
2681 /* First open the top level directory. */
2682 status =
2683 cli_smb2_create_fnum(cli, "",
2684 (struct cli_smb2_create_flags){0},
2685 SMB2_IMPERSONATION_IMPERSONATION,
2686 FILE_READ_ATTRIBUTES, /* desired_access */
2687 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2688 FILE_SHARE_READ | FILE_SHARE_WRITE |
2689 FILE_SHARE_DELETE, /* share_access */
2690 FILE_OPEN, /* create_disposition */
2691 FILE_DIRECTORY_FILE, /* create_options */
2692 NULL,
2693 &fnum,
2694 NULL,
2695 NULL,
2696 NULL);
2698 if (!NT_STATUS_IS_OK(status)) {
2699 goto fail;
2702 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2703 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2705 status = cli_smb2_query_info_fnum(
2706 cli,
2707 fnum,
2708 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2709 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2710 0xFFFF, /* in_max_output_length */
2711 NULL, /* in_input_buffer */
2712 0, /* in_additional_info */
2713 0, /* in_flags */
2714 frame,
2715 &outbuf);
2716 if (!NT_STATUS_IS_OK(status)) {
2717 goto fail;
2720 if (outbuf.length < 32) {
2721 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2722 goto fail;
2725 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2726 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2727 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2728 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2729 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2731 fail:
2733 if (fnum != 0xffff) {
2734 cli_smb2_close_fnum(cli, fnum);
2737 cli->raw_status = status;
2739 TALLOC_FREE(frame);
2740 return status;
2743 /***************************************************************
2744 Wrapper that allows SMB2 to query file system attributes.
2745 Synchronous only.
2746 ***************************************************************/
2748 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2750 NTSTATUS status;
2751 uint16_t fnum = 0xffff;
2752 DATA_BLOB outbuf = data_blob_null;
2753 TALLOC_CTX *frame = talloc_stackframe();
2755 if (smbXcli_conn_has_async_calls(cli->conn)) {
2757 * Can't use sync call while an async call is in flight
2759 status = NT_STATUS_INVALID_PARAMETER;
2760 goto fail;
2763 /* First open the top level directory. */
2764 status =
2765 cli_smb2_create_fnum(cli, "",
2766 (struct cli_smb2_create_flags){0},
2767 SMB2_IMPERSONATION_IMPERSONATION,
2768 FILE_READ_ATTRIBUTES, /* desired_access */
2769 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2770 FILE_SHARE_READ | FILE_SHARE_WRITE |
2771 FILE_SHARE_DELETE, /* share_access */
2772 FILE_OPEN, /* create_disposition */
2773 FILE_DIRECTORY_FILE, /* create_options */
2774 NULL,
2775 &fnum,
2776 NULL,
2777 NULL,
2778 NULL);
2780 if (!NT_STATUS_IS_OK(status)) {
2781 goto fail;
2784 status = cli_smb2_query_info_fnum(
2785 cli,
2786 fnum,
2787 2, /* in_info_type */
2788 5, /* in_file_info_class */
2789 0xFFFF, /* in_max_output_length */
2790 NULL, /* in_input_buffer */
2791 0, /* in_additional_info */
2792 0, /* in_flags */
2793 frame,
2794 &outbuf);
2795 if (!NT_STATUS_IS_OK(status)) {
2796 goto fail;
2799 if (outbuf.length < 12) {
2800 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2801 goto fail;
2804 *fs_attr = IVAL(outbuf.data, 0);
2806 fail:
2808 if (fnum != 0xffff) {
2809 cli_smb2_close_fnum(cli, fnum);
2812 cli->raw_status = status;
2814 TALLOC_FREE(frame);
2815 return status;
2818 /***************************************************************
2819 Wrapper that allows SMB2 to query file system volume info.
2820 Synchronous only.
2821 ***************************************************************/
2823 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2824 TALLOC_CTX *mem_ctx,
2825 char **_volume_name,
2826 uint32_t *pserial_number,
2827 time_t *pdate)
2829 NTSTATUS status;
2830 uint16_t fnum = 0xffff;
2831 DATA_BLOB outbuf = data_blob_null;
2832 uint32_t nlen;
2833 char *volume_name = NULL;
2834 TALLOC_CTX *frame = talloc_stackframe();
2836 if (smbXcli_conn_has_async_calls(cli->conn)) {
2838 * Can't use sync call while an async call is in flight
2840 status = NT_STATUS_INVALID_PARAMETER;
2841 goto fail;
2844 /* First open the top level directory. */
2845 status =
2846 cli_smb2_create_fnum(cli, "",
2847 (struct cli_smb2_create_flags){0},
2848 SMB2_IMPERSONATION_IMPERSONATION,
2849 FILE_READ_ATTRIBUTES, /* desired_access */
2850 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2851 FILE_SHARE_READ | FILE_SHARE_WRITE |
2852 FILE_SHARE_DELETE, /* share_access */
2853 FILE_OPEN, /* create_disposition */
2854 FILE_DIRECTORY_FILE, /* create_options */
2855 NULL,
2856 &fnum,
2857 NULL,
2858 NULL,
2859 NULL);
2861 if (!NT_STATUS_IS_OK(status)) {
2862 goto fail;
2865 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2866 level 1 (SMB_FS_VOLUME_INFORMATION). */
2868 status = cli_smb2_query_info_fnum(
2869 cli,
2870 fnum,
2871 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2872 /* in_file_info_class */
2873 SMB_FS_VOLUME_INFORMATION - 1000,
2874 0xFFFF, /* in_max_output_length */
2875 NULL, /* in_input_buffer */
2876 0, /* in_additional_info */
2877 0, /* in_flags */
2878 frame,
2879 &outbuf);
2880 if (!NT_STATUS_IS_OK(status)) {
2881 goto fail;
2884 if (outbuf.length < 24) {
2885 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2886 goto fail;
2889 if (pdate) {
2890 struct timespec ts;
2891 ts = interpret_long_date(BVAL(outbuf.data, 0));
2892 *pdate = ts.tv_sec;
2894 if (pserial_number) {
2895 *pserial_number = IVAL(outbuf.data,8);
2897 nlen = IVAL(outbuf.data,12);
2898 if (nlen + 18 < 18) {
2899 /* Integer wrap. */
2900 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2901 goto fail;
2904 * The next check is safe as we know outbuf.length >= 24
2905 * from above.
2907 if (nlen > (outbuf.length - 18)) {
2908 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2909 goto fail;
2912 pull_string_talloc(mem_ctx,
2913 (const char *)outbuf.data,
2915 &volume_name,
2916 outbuf.data + 18,
2917 nlen,
2918 STR_UNICODE);
2919 if (volume_name == NULL) {
2920 status = map_nt_error_from_unix(errno);
2921 goto fail;
2924 *_volume_name = volume_name;
2926 fail:
2928 if (fnum != 0xffff) {
2929 cli_smb2_close_fnum(cli, fnum);
2932 cli->raw_status = status;
2934 TALLOC_FREE(frame);
2935 return status;
2938 struct cli_smb2_mxac_state {
2939 struct tevent_context *ev;
2940 struct cli_state *cli;
2941 const char *fname;
2942 struct smb2_create_blobs in_cblobs;
2943 uint16_t fnum;
2944 NTSTATUS status;
2945 uint32_t mxac;
2948 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2949 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2951 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2952 struct tevent_context *ev,
2953 struct cli_state *cli,
2954 const char *fname)
2956 struct tevent_req *req = NULL, *subreq = NULL;
2957 struct cli_smb2_mxac_state *state = NULL;
2958 NTSTATUS status;
2960 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2961 if (req == NULL) {
2962 return NULL;
2964 *state = (struct cli_smb2_mxac_state) {
2965 .ev = ev,
2966 .cli = cli,
2967 .fname = fname,
2970 status = smb2_create_blob_add(state,
2971 &state->in_cblobs,
2972 SMB2_CREATE_TAG_MXAC,
2973 data_blob(NULL, 0));
2974 if (tevent_req_nterror(req, status)) {
2975 return tevent_req_post(req, ev);
2978 subreq = cli_smb2_create_fnum_send(
2979 state,
2980 state->ev,
2981 state->cli,
2982 state->fname,
2983 (struct cli_smb2_create_flags){0},
2984 SMB2_IMPERSONATION_IMPERSONATION,
2985 FILE_READ_ATTRIBUTES,
2986 0, /* file attributes */
2987 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2988 FILE_OPEN,
2989 0, /* create_options */
2990 &state->in_cblobs);
2991 if (tevent_req_nomem(subreq, req)) {
2992 return tevent_req_post(req, ev);
2994 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2995 return req;
2998 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
3000 struct tevent_req *req = tevent_req_callback_data(
3001 subreq, struct tevent_req);
3002 struct cli_smb2_mxac_state *state = tevent_req_data(
3003 req, struct cli_smb2_mxac_state);
3004 struct smb2_create_blobs out_cblobs = {0};
3005 struct smb2_create_blob *mxac_blob = NULL;
3006 NTSTATUS status;
3008 status = cli_smb2_create_fnum_recv(
3009 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
3010 TALLOC_FREE(subreq);
3012 if (tevent_req_nterror(req, status)) {
3013 return;
3016 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
3017 if (mxac_blob == NULL) {
3018 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3019 goto close;
3021 if (mxac_blob->data.length != 8) {
3022 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3023 goto close;
3026 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
3027 state->mxac = IVAL(mxac_blob->data.data, 4);
3029 close:
3030 subreq = cli_smb2_close_fnum_send(
3031 state, state->ev, state->cli, state->fnum);
3032 if (tevent_req_nomem(subreq, req)) {
3033 return;
3035 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
3037 return;
3040 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
3042 struct tevent_req *req = tevent_req_callback_data(
3043 subreq, struct tevent_req);
3044 NTSTATUS status;
3046 status = cli_smb2_close_fnum_recv(subreq);
3047 if (tevent_req_nterror(req, status)) {
3048 return;
3051 tevent_req_done(req);
3054 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3056 struct cli_smb2_mxac_state *state = tevent_req_data(
3057 req, struct cli_smb2_mxac_state);
3058 NTSTATUS status;
3060 if (tevent_req_is_nterror(req, &status)) {
3061 return status;
3064 if (!NT_STATUS_IS_OK(state->status)) {
3065 return state->status;
3068 *mxac = state->mxac;
3069 return NT_STATUS_OK;
3072 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3073 const char *fname,
3074 uint32_t *_mxac)
3076 TALLOC_CTX *frame = talloc_stackframe();
3077 struct tevent_context *ev = NULL;
3078 struct tevent_req *req = NULL;
3079 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3080 bool ok;
3082 if (smbXcli_conn_has_async_calls(cli->conn)) {
3084 * Can't use sync call while an async call is in flight
3086 status = NT_STATUS_INVALID_PARAMETER;
3087 goto fail;
3090 ev = samba_tevent_context_init(frame);
3091 if (ev == NULL) {
3092 goto fail;
3094 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3095 if (req == NULL) {
3096 goto fail;
3098 ok = tevent_req_poll_ntstatus(req, ev, &status);
3099 if (!ok) {
3100 goto fail;
3102 status = cli_smb2_query_mxac_recv(req, _mxac);
3104 fail:
3105 cli->raw_status = status;
3106 TALLOC_FREE(frame);
3107 return status;
3110 struct cli_smb2_rename_fnum_state {
3111 DATA_BLOB inbuf;
3114 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3116 static struct tevent_req *cli_smb2_rename_fnum_send(
3117 TALLOC_CTX *mem_ctx,
3118 struct tevent_context *ev,
3119 struct cli_state *cli,
3120 uint16_t fnum,
3121 const char *fname_dst,
3122 bool replace)
3124 struct tevent_req *req = NULL, *subreq = NULL;
3125 struct cli_smb2_rename_fnum_state *state = NULL;
3126 size_t namelen = strlen(fname_dst);
3127 smb_ucs2_t *converted_str = NULL;
3128 size_t converted_size_bytes = 0;
3129 size_t inbuf_size;
3130 bool ok;
3132 req = tevent_req_create(
3133 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3134 if (req == NULL) {
3135 return NULL;
3139 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3140 * a '\'
3142 if (*fname_dst == '\\') {
3143 fname_dst++;
3147 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3148 * '\'
3150 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3151 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3152 if (tevent_req_nomem(fname_dst, req)) {
3153 return tevent_req_post(req, ev);
3157 ok = push_ucs2_talloc(
3158 state, &converted_str, fname_dst, &converted_size_bytes);
3159 if (!ok) {
3160 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3161 return tevent_req_post(req, ev);
3165 * W2K8 insists the dest name is not null terminated. Remove
3166 * the last 2 zero bytes and reduce the name length.
3168 if (converted_size_bytes < 2) {
3169 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3170 return tevent_req_post(req, ev);
3172 converted_size_bytes -= 2;
3174 inbuf_size = 20 + converted_size_bytes;
3175 if (inbuf_size < 20) {
3176 /* Integer wrap check. */
3177 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3178 return tevent_req_post(req, ev);
3182 * The Windows 10 SMB2 server has a minimum length
3183 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3184 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3185 * if the length is less. This isn't an alignment
3186 * issue as Windows client accepts happily 2-byte align
3187 * for larger target name sizes. Also the Windows 10
3188 * SMB1 server doesn't have this restriction.
3190 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3192 inbuf_size = MAX(inbuf_size, 24);
3194 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3195 if (tevent_req_nomem(state->inbuf.data, req)) {
3196 return tevent_req_post(req, ev);
3199 if (replace) {
3200 SCVAL(state->inbuf.data, 0, 1);
3203 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3204 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3206 TALLOC_FREE(converted_str);
3208 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3209 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3211 subreq = cli_smb2_set_info_fnum_send(
3212 state, /* mem_ctx */
3213 ev, /* ev */
3214 cli, /* cli */
3215 fnum, /* fnum */
3216 1, /* in_info_type */
3217 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3218 &state->inbuf, /* in_input_buffer */
3219 0); /* in_additional_info */
3220 if (tevent_req_nomem(subreq, req)) {
3221 return tevent_req_post(req, ev);
3223 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3224 return req;
3227 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3229 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3230 tevent_req_simple_finish_ntstatus(subreq, status);
3233 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3235 return tevent_req_simple_recv_ntstatus(req);
3238 /***************************************************************
3239 Wrapper that allows SMB2 to rename a file.
3240 ***************************************************************/
3242 struct cli_smb2_rename_state {
3243 struct tevent_context *ev;
3244 struct cli_state *cli;
3245 const char *fname_dst;
3246 bool replace;
3247 uint16_t fnum;
3249 NTSTATUS rename_status;
3252 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3253 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3254 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3256 struct tevent_req *cli_smb2_rename_send(
3257 TALLOC_CTX *mem_ctx,
3258 struct tevent_context *ev,
3259 struct cli_state *cli,
3260 const char *fname_src,
3261 const char *fname_dst,
3262 bool replace)
3264 struct tevent_req *req = NULL, *subreq = NULL;
3265 struct cli_smb2_rename_state *state = NULL;
3266 NTSTATUS status;
3268 req = tevent_req_create(
3269 mem_ctx, &state, struct cli_smb2_rename_state);
3270 if (req == NULL) {
3271 return NULL;
3275 * Strip a MSDFS path from fname_dst if we were given one.
3277 status = cli_dfs_target_check(state,
3278 cli,
3279 fname_dst,
3280 &fname_dst);
3281 if (tevent_req_nterror(req, status)) {
3282 return tevent_req_post(req, ev);
3285 state->ev = ev;
3286 state->cli = cli;
3287 state->fname_dst = fname_dst;
3288 state->replace = replace;
3290 subreq = get_fnum_from_path_send(
3291 state, ev, cli, fname_src, DELETE_ACCESS);
3292 if (tevent_req_nomem(subreq, req)) {
3293 return tevent_req_post(req, ev);
3295 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3296 return req;
3299 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3301 struct tevent_req *req = tevent_req_callback_data(
3302 subreq, struct tevent_req);
3303 struct cli_smb2_rename_state *state = tevent_req_data(
3304 req, struct cli_smb2_rename_state);
3305 NTSTATUS status;
3307 status = get_fnum_from_path_recv(subreq, &state->fnum);
3308 TALLOC_FREE(subreq);
3309 if (tevent_req_nterror(req, status)) {
3310 return;
3313 subreq = cli_smb2_rename_fnum_send(
3314 state,
3315 state->ev,
3316 state->cli,
3317 state->fnum,
3318 state->fname_dst,
3319 state->replace);
3320 if (tevent_req_nomem(subreq, req)) {
3321 return;
3323 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3326 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3328 struct tevent_req *req = tevent_req_callback_data(
3329 subreq, struct tevent_req);
3330 struct cli_smb2_rename_state *state = tevent_req_data(
3331 req, struct cli_smb2_rename_state);
3333 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3334 TALLOC_FREE(subreq);
3336 subreq = cli_smb2_close_fnum_send(
3337 state, state->ev, state->cli, state->fnum);
3338 if (tevent_req_nomem(subreq, req)) {
3339 return;
3341 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3344 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3346 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3347 tevent_req_simple_finish_ntstatus(subreq, status);
3350 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3352 struct cli_smb2_rename_state *state = tevent_req_data(
3353 req, struct cli_smb2_rename_state);
3354 NTSTATUS status = NT_STATUS_OK;
3356 if (!tevent_req_is_nterror(req, &status)) {
3357 status = state->rename_status;
3359 tevent_req_received(req);
3360 return status;
3363 /***************************************************************
3364 Wrapper that allows SMB2 to set an EA on a fnum.
3365 Synchronous only.
3366 ***************************************************************/
3368 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3369 uint16_t fnum,
3370 const char *ea_name,
3371 const char *ea_val,
3372 size_t ea_len)
3374 NTSTATUS status;
3375 DATA_BLOB inbuf = data_blob_null;
3376 size_t bloblen = 0;
3377 char *ea_name_ascii = NULL;
3378 size_t namelen = 0;
3379 TALLOC_CTX *frame = talloc_stackframe();
3381 if (smbXcli_conn_has_async_calls(cli->conn)) {
3383 * Can't use sync call while an async call is in flight
3385 status = NT_STATUS_INVALID_PARAMETER;
3386 goto fail;
3389 /* Marshall the SMB2 EA data. */
3390 if (ea_len > 0xFFFF) {
3391 status = NT_STATUS_INVALID_PARAMETER;
3392 goto fail;
3395 if (!push_ascii_talloc(frame,
3396 &ea_name_ascii,
3397 ea_name,
3398 &namelen)) {
3399 status = NT_STATUS_INVALID_PARAMETER;
3400 goto fail;
3403 if (namelen < 2 || namelen > 0xFF) {
3404 status = NT_STATUS_INVALID_PARAMETER;
3405 goto fail;
3408 bloblen = 8 + ea_len + namelen;
3409 /* Round up to a 4 byte boundary. */
3410 bloblen = ((bloblen + 3)&~3);
3412 inbuf = data_blob_talloc_zero(frame, bloblen);
3413 if (inbuf.data == NULL) {
3414 status = NT_STATUS_NO_MEMORY;
3415 goto fail;
3417 /* namelen doesn't include the NULL byte. */
3418 SCVAL(inbuf.data, 5, namelen - 1);
3419 SSVAL(inbuf.data, 6, ea_len);
3420 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3421 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3423 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3424 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3426 status = cli_smb2_set_info_fnum(
3427 cli,
3428 fnum,
3429 1, /* in_info_type */
3430 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3431 &inbuf, /* in_input_buffer */
3432 0); /* in_additional_info */
3434 fail:
3436 cli->raw_status = status;
3438 TALLOC_FREE(frame);
3439 return status;
3442 /***************************************************************
3443 Wrapper that allows SMB2 to set an EA on a pathname.
3444 Synchronous only.
3445 ***************************************************************/
3447 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3448 const char *name,
3449 const char *ea_name,
3450 const char *ea_val,
3451 size_t ea_len)
3453 NTSTATUS status;
3454 uint16_t fnum = 0xffff;
3456 if (smbXcli_conn_has_async_calls(cli->conn)) {
3458 * Can't use sync call while an async call is in flight
3460 status = NT_STATUS_INVALID_PARAMETER;
3461 goto fail;
3464 status = get_fnum_from_path(cli,
3465 name,
3466 FILE_WRITE_EA,
3467 &fnum);
3469 if (!NT_STATUS_IS_OK(status)) {
3470 goto fail;
3473 status = cli_set_ea_fnum(cli,
3474 fnum,
3475 ea_name,
3476 ea_val,
3477 ea_len);
3478 if (!NT_STATUS_IS_OK(status)) {
3479 goto fail;
3482 fail:
3484 if (fnum != 0xffff) {
3485 cli_smb2_close_fnum(cli, fnum);
3488 cli->raw_status = status;
3490 return status;
3493 /***************************************************************
3494 Wrapper that allows SMB2 to get an EA list on a pathname.
3495 Synchronous only.
3496 ***************************************************************/
3498 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3499 const char *name,
3500 TALLOC_CTX *ctx,
3501 size_t *pnum_eas,
3502 struct ea_struct **pea_array)
3504 NTSTATUS status;
3505 uint16_t fnum = 0xffff;
3506 DATA_BLOB outbuf = data_blob_null;
3507 struct ea_list *ea_list = NULL;
3508 struct ea_list *eal = NULL;
3509 size_t ea_count = 0;
3510 TALLOC_CTX *frame = talloc_stackframe();
3512 *pnum_eas = 0;
3513 *pea_array = NULL;
3515 if (smbXcli_conn_has_async_calls(cli->conn)) {
3517 * Can't use sync call while an async call is in flight
3519 status = NT_STATUS_INVALID_PARAMETER;
3520 goto fail;
3523 status = get_fnum_from_path(cli,
3524 name,
3525 FILE_READ_EA,
3526 &fnum);
3528 if (!NT_STATUS_IS_OK(status)) {
3529 goto fail;
3532 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3533 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3535 status = cli_smb2_query_info_fnum(
3536 cli,
3537 fnum,
3538 1, /* in_info_type */
3539 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3540 0xFFFF, /* in_max_output_length */
3541 NULL, /* in_input_buffer */
3542 0, /* in_additional_info */
3543 0, /* in_flags */
3544 frame,
3545 &outbuf);
3547 if (!NT_STATUS_IS_OK(status)) {
3548 goto fail;
3551 /* Parse the reply. */
3552 ea_list = read_nttrans_ea_list(ctx,
3553 (const char *)outbuf.data,
3554 outbuf.length);
3555 if (ea_list == NULL) {
3556 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3557 goto fail;
3560 /* Convert to an array. */
3561 for (eal = ea_list; eal; eal = eal->next) {
3562 ea_count++;
3565 if (ea_count) {
3566 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3567 if (*pea_array == NULL) {
3568 status = NT_STATUS_NO_MEMORY;
3569 goto fail;
3571 ea_count = 0;
3572 for (eal = ea_list; eal; eal = eal->next) {
3573 (*pea_array)[ea_count++] = eal->ea;
3575 *pnum_eas = ea_count;
3578 fail:
3580 if (fnum != 0xffff) {
3581 cli_smb2_close_fnum(cli, fnum);
3584 cli->raw_status = status;
3586 TALLOC_FREE(frame);
3587 return status;
3590 /***************************************************************
3591 Wrapper that allows SMB2 to get user quota.
3592 Synchronous only.
3593 ***************************************************************/
3595 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3596 int quota_fnum,
3597 SMB_NTQUOTA_STRUCT *pqt)
3599 NTSTATUS status;
3600 DATA_BLOB inbuf = data_blob_null;
3601 DATA_BLOB info_blob = data_blob_null;
3602 DATA_BLOB outbuf = data_blob_null;
3603 TALLOC_CTX *frame = talloc_stackframe();
3604 unsigned sid_len;
3605 unsigned int offset;
3606 struct smb2_query_quota_info query = {0};
3607 struct file_get_quota_info info = {0};
3608 enum ndr_err_code err;
3609 struct ndr_push *ndr_push = NULL;
3611 if (smbXcli_conn_has_async_calls(cli->conn)) {
3613 * Can't use sync call while an async call is in flight
3615 status = NT_STATUS_INVALID_PARAMETER;
3616 goto fail;
3619 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3621 query.return_single = 1;
3623 info.next_entry_offset = 0;
3624 info.sid_length = sid_len;
3625 info.sid = pqt->sid;
3627 err = ndr_push_struct_blob(
3628 &info_blob,
3629 frame,
3630 &info,
3631 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3633 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3634 status = NT_STATUS_INTERNAL_ERROR;
3635 goto fail;
3638 query.sid_list_length = info_blob.length;
3639 ndr_push = ndr_push_init_ctx(frame);
3640 if (!ndr_push) {
3641 status = NT_STATUS_NO_MEMORY;
3642 goto fail;
3645 err = ndr_push_smb2_query_quota_info(ndr_push,
3646 NDR_SCALARS | NDR_BUFFERS,
3647 &query);
3649 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3650 status = NT_STATUS_INTERNAL_ERROR;
3651 goto fail;
3654 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3655 info_blob.length);
3657 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3658 status = NT_STATUS_INTERNAL_ERROR;
3659 goto fail;
3661 inbuf.data = ndr_push->data;
3662 inbuf.length = ndr_push->offset;
3664 status = cli_smb2_query_info_fnum(
3665 cli,
3666 quota_fnum,
3667 4, /* in_info_type */
3668 0, /* in_file_info_class */
3669 0xFFFF, /* in_max_output_length */
3670 &inbuf, /* in_input_buffer */
3671 0, /* in_additional_info */
3672 0, /* in_flags */
3673 frame,
3674 &outbuf);
3676 if (!NT_STATUS_IS_OK(status)) {
3677 goto fail;
3680 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3681 pqt)) {
3682 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3683 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3686 fail:
3687 cli->raw_status = status;
3689 TALLOC_FREE(frame);
3690 return status;
3693 /***************************************************************
3694 Wrapper that allows SMB2 to list user quota.
3695 Synchronous only.
3696 ***************************************************************/
3698 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3699 TALLOC_CTX *mem_ctx,
3700 int quota_fnum,
3701 SMB_NTQUOTA_LIST **pqt_list,
3702 bool first)
3704 NTSTATUS status;
3705 DATA_BLOB inbuf = data_blob_null;
3706 DATA_BLOB outbuf = data_blob_null;
3707 TALLOC_CTX *frame = talloc_stackframe();
3708 struct smb2_query_quota_info info = {0};
3709 enum ndr_err_code err;
3711 if (smbXcli_conn_has_async_calls(cli->conn)) {
3713 * Can't use sync call while an async call is in flight
3715 status = NT_STATUS_INVALID_PARAMETER;
3716 goto cleanup;
3719 info.restart_scan = first ? 1 : 0;
3721 err = ndr_push_struct_blob(
3722 &inbuf,
3723 frame,
3724 &info,
3725 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3727 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3728 status = NT_STATUS_INTERNAL_ERROR;
3729 goto cleanup;
3732 status = cli_smb2_query_info_fnum(
3733 cli,
3734 quota_fnum,
3735 4, /* in_info_type */
3736 0, /* in_file_info_class */
3737 0xFFFF, /* in_max_output_length */
3738 &inbuf, /* in_input_buffer */
3739 0, /* in_additional_info */
3740 0, /* in_flags */
3741 frame,
3742 &outbuf);
3745 * safeguard against panic from calling parse_user_quota_list with
3746 * NULL buffer
3748 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3749 status = NT_STATUS_NO_MORE_ENTRIES;
3752 if (!NT_STATUS_IS_OK(status)) {
3753 goto cleanup;
3756 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3757 pqt_list);
3759 cleanup:
3760 cli->raw_status = status;
3762 TALLOC_FREE(frame);
3763 return status;
3766 /***************************************************************
3767 Wrapper that allows SMB2 to get file system quota.
3768 Synchronous only.
3769 ***************************************************************/
3771 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3772 int quota_fnum,
3773 SMB_NTQUOTA_STRUCT *pqt)
3775 NTSTATUS status;
3776 DATA_BLOB outbuf = data_blob_null;
3777 TALLOC_CTX *frame = talloc_stackframe();
3779 if (smbXcli_conn_has_async_calls(cli->conn)) {
3781 * Can't use sync call while an async call is in flight
3783 status = NT_STATUS_INVALID_PARAMETER;
3784 goto cleanup;
3787 status = cli_smb2_query_info_fnum(
3788 cli,
3789 quota_fnum,
3790 2, /* in_info_type */
3791 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3792 0xFFFF, /* in_max_output_length */
3793 NULL, /* in_input_buffer */
3794 0, /* in_additional_info */
3795 0, /* in_flags */
3796 frame,
3797 &outbuf);
3799 if (!NT_STATUS_IS_OK(status)) {
3800 goto cleanup;
3803 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3805 cleanup:
3806 cli->raw_status = status;
3808 TALLOC_FREE(frame);
3809 return status;
3812 /***************************************************************
3813 Wrapper that allows SMB2 to set user quota.
3814 Synchronous only.
3815 ***************************************************************/
3817 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3818 int quota_fnum,
3819 SMB_NTQUOTA_LIST *qtl)
3821 NTSTATUS status;
3822 DATA_BLOB inbuf = data_blob_null;
3823 TALLOC_CTX *frame = talloc_stackframe();
3825 if (smbXcli_conn_has_async_calls(cli->conn)) {
3827 * Can't use sync call while an async call is in flight
3829 status = NT_STATUS_INVALID_PARAMETER;
3830 goto cleanup;
3833 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3834 if (!NT_STATUS_IS_OK(status)) {
3835 goto cleanup;
3838 status = cli_smb2_set_info_fnum(
3839 cli,
3840 quota_fnum,
3841 4, /* in_info_type */
3842 0, /* in_file_info_class */
3843 &inbuf, /* in_input_buffer */
3844 0); /* in_additional_info */
3845 cleanup:
3847 cli->raw_status = status;
3849 TALLOC_FREE(frame);
3851 return status;
3854 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3855 int quota_fnum,
3856 SMB_NTQUOTA_STRUCT *pqt)
3858 NTSTATUS status;
3859 DATA_BLOB inbuf = data_blob_null;
3860 TALLOC_CTX *frame = talloc_stackframe();
3862 if (smbXcli_conn_has_async_calls(cli->conn)) {
3864 * Can't use sync call while an async call is in flight
3866 status = NT_STATUS_INVALID_PARAMETER;
3867 goto cleanup;
3870 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3871 if (!NT_STATUS_IS_OK(status)) {
3872 goto cleanup;
3875 status = cli_smb2_set_info_fnum(
3876 cli,
3877 quota_fnum,
3878 2, /* in_info_type */
3879 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3880 &inbuf, /* in_input_buffer */
3881 0); /* in_additional_info */
3882 cleanup:
3883 cli->raw_status = status;
3885 TALLOC_FREE(frame);
3886 return status;
3889 struct cli_smb2_read_state {
3890 struct tevent_context *ev;
3891 struct cli_state *cli;
3892 struct smb2_hnd *ph;
3893 uint64_t start_offset;
3894 uint32_t size;
3895 uint32_t received;
3896 uint8_t *buf;
3899 static void cli_smb2_read_done(struct tevent_req *subreq);
3901 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3902 struct tevent_context *ev,
3903 struct cli_state *cli,
3904 uint16_t fnum,
3905 off_t offset,
3906 size_t size)
3908 NTSTATUS status;
3909 struct tevent_req *req, *subreq;
3910 struct cli_smb2_read_state *state;
3912 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3913 if (req == NULL) {
3914 return NULL;
3916 state->ev = ev;
3917 state->cli = cli;
3918 state->start_offset = (uint64_t)offset;
3919 state->size = (uint32_t)size;
3920 state->received = 0;
3921 state->buf = NULL;
3923 status = map_fnum_to_smb2_handle(cli,
3924 fnum,
3925 &state->ph);
3926 if (tevent_req_nterror(req, status)) {
3927 return tevent_req_post(req, ev);
3930 subreq = smb2cli_read_send(state,
3931 state->ev,
3932 state->cli->conn,
3933 state->cli->timeout,
3934 state->cli->smb2.session,
3935 state->cli->smb2.tcon,
3936 state->size,
3937 state->start_offset,
3938 state->ph->fid_persistent,
3939 state->ph->fid_volatile,
3940 0, /* minimum_count */
3941 0); /* remaining_bytes */
3943 if (tevent_req_nomem(subreq, req)) {
3944 return tevent_req_post(req, ev);
3946 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3947 return req;
3950 static void cli_smb2_read_done(struct tevent_req *subreq)
3952 struct tevent_req *req = tevent_req_callback_data(
3953 subreq, struct tevent_req);
3954 struct cli_smb2_read_state *state = tevent_req_data(
3955 req, struct cli_smb2_read_state);
3956 NTSTATUS status;
3958 status = smb2cli_read_recv(subreq, state,
3959 &state->buf, &state->received);
3960 if (tevent_req_nterror(req, status)) {
3961 return;
3964 if (state->received > state->size) {
3965 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3966 return;
3969 tevent_req_done(req);
3972 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3973 ssize_t *received,
3974 uint8_t **rcvbuf)
3976 NTSTATUS status;
3977 struct cli_smb2_read_state *state = tevent_req_data(
3978 req, struct cli_smb2_read_state);
3980 if (tevent_req_is_nterror(req, &status)) {
3981 state->cli->raw_status = status;
3982 return status;
3985 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3986 * better make sure that you copy it away before you talloc_free(req).
3987 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3989 *received = (ssize_t)state->received;
3990 *rcvbuf = state->buf;
3991 state->cli->raw_status = NT_STATUS_OK;
3992 return NT_STATUS_OK;
3995 struct cli_smb2_write_state {
3996 struct tevent_context *ev;
3997 struct cli_state *cli;
3998 struct smb2_hnd *ph;
3999 uint32_t flags;
4000 const uint8_t *buf;
4001 uint64_t offset;
4002 uint32_t size;
4003 uint32_t written;
4006 static void cli_smb2_write_written(struct tevent_req *req);
4008 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
4009 struct tevent_context *ev,
4010 struct cli_state *cli,
4011 uint16_t fnum,
4012 uint16_t mode,
4013 const uint8_t *buf,
4014 off_t offset,
4015 size_t size)
4017 NTSTATUS status;
4018 struct tevent_req *req, *subreq = NULL;
4019 struct cli_smb2_write_state *state = NULL;
4021 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
4022 if (req == NULL) {
4023 return NULL;
4025 state->ev = ev;
4026 state->cli = cli;
4027 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4028 state->flags = (uint32_t)mode;
4029 state->buf = buf;
4030 state->offset = (uint64_t)offset;
4031 state->size = (uint32_t)size;
4032 state->written = 0;
4034 status = map_fnum_to_smb2_handle(cli,
4035 fnum,
4036 &state->ph);
4037 if (tevent_req_nterror(req, status)) {
4038 return tevent_req_post(req, ev);
4041 subreq = smb2cli_write_send(state,
4042 state->ev,
4043 state->cli->conn,
4044 state->cli->timeout,
4045 state->cli->smb2.session,
4046 state->cli->smb2.tcon,
4047 state->size,
4048 state->offset,
4049 state->ph->fid_persistent,
4050 state->ph->fid_volatile,
4051 0, /* remaining_bytes */
4052 state->flags, /* flags */
4053 state->buf);
4055 if (tevent_req_nomem(subreq, req)) {
4056 return tevent_req_post(req, ev);
4058 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4059 return req;
4062 static void cli_smb2_write_written(struct tevent_req *subreq)
4064 struct tevent_req *req = tevent_req_callback_data(
4065 subreq, struct tevent_req);
4066 struct cli_smb2_write_state *state = tevent_req_data(
4067 req, struct cli_smb2_write_state);
4068 NTSTATUS status;
4069 uint32_t written;
4071 status = smb2cli_write_recv(subreq, &written);
4072 TALLOC_FREE(subreq);
4073 if (tevent_req_nterror(req, status)) {
4074 return;
4077 state->written = written;
4079 tevent_req_done(req);
4082 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4083 size_t *pwritten)
4085 struct cli_smb2_write_state *state = tevent_req_data(
4086 req, struct cli_smb2_write_state);
4087 NTSTATUS status;
4089 if (tevent_req_is_nterror(req, &status)) {
4090 state->cli->raw_status = status;
4091 tevent_req_received(req);
4092 return status;
4095 if (pwritten != NULL) {
4096 *pwritten = (size_t)state->written;
4098 state->cli->raw_status = NT_STATUS_OK;
4099 tevent_req_received(req);
4100 return NT_STATUS_OK;
4103 /***************************************************************
4104 Wrapper that allows SMB2 async write using an fnum.
4105 This is mostly cut-and-paste from Volker's code inside
4106 source3/libsmb/clireadwrite.c, adapted for SMB2.
4108 Done this way so I can reuse all the logic inside cli_push()
4109 for free :-).
4110 ***************************************************************/
4112 struct cli_smb2_writeall_state {
4113 struct tevent_context *ev;
4114 struct cli_state *cli;
4115 struct smb2_hnd *ph;
4116 uint32_t flags;
4117 const uint8_t *buf;
4118 uint64_t offset;
4119 uint32_t size;
4120 uint32_t written;
4123 static void cli_smb2_writeall_written(struct tevent_req *req);
4125 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4126 struct tevent_context *ev,
4127 struct cli_state *cli,
4128 uint16_t fnum,
4129 uint16_t mode,
4130 const uint8_t *buf,
4131 off_t offset,
4132 size_t size)
4134 NTSTATUS status;
4135 struct tevent_req *req, *subreq = NULL;
4136 struct cli_smb2_writeall_state *state = NULL;
4137 uint32_t to_write;
4138 uint32_t max_size;
4139 bool ok;
4141 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4142 if (req == NULL) {
4143 return NULL;
4145 state->ev = ev;
4146 state->cli = cli;
4147 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4148 state->flags = (uint32_t)mode;
4149 state->buf = buf;
4150 state->offset = (uint64_t)offset;
4151 state->size = (uint32_t)size;
4152 state->written = 0;
4154 status = map_fnum_to_smb2_handle(cli,
4155 fnum,
4156 &state->ph);
4157 if (tevent_req_nterror(req, status)) {
4158 return tevent_req_post(req, ev);
4161 to_write = state->size;
4162 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4163 to_write = MIN(max_size, to_write);
4164 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4165 if (ok) {
4166 to_write = MIN(max_size, to_write);
4169 subreq = smb2cli_write_send(state,
4170 state->ev,
4171 state->cli->conn,
4172 state->cli->timeout,
4173 state->cli->smb2.session,
4174 state->cli->smb2.tcon,
4175 to_write,
4176 state->offset,
4177 state->ph->fid_persistent,
4178 state->ph->fid_volatile,
4179 0, /* remaining_bytes */
4180 state->flags, /* flags */
4181 state->buf + state->written);
4183 if (tevent_req_nomem(subreq, req)) {
4184 return tevent_req_post(req, ev);
4186 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4187 return req;
4190 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4192 struct tevent_req *req = tevent_req_callback_data(
4193 subreq, struct tevent_req);
4194 struct cli_smb2_writeall_state *state = tevent_req_data(
4195 req, struct cli_smb2_writeall_state);
4196 NTSTATUS status;
4197 uint32_t written, to_write;
4198 uint32_t max_size;
4199 bool ok;
4201 status = smb2cli_write_recv(subreq, &written);
4202 TALLOC_FREE(subreq);
4203 if (tevent_req_nterror(req, status)) {
4204 return;
4207 state->written += written;
4209 if (state->written > state->size) {
4210 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4211 return;
4214 to_write = state->size - state->written;
4216 if (to_write == 0) {
4217 tevent_req_done(req);
4218 return;
4221 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4222 to_write = MIN(max_size, to_write);
4223 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4224 if (ok) {
4225 to_write = MIN(max_size, to_write);
4228 subreq = smb2cli_write_send(state,
4229 state->ev,
4230 state->cli->conn,
4231 state->cli->timeout,
4232 state->cli->smb2.session,
4233 state->cli->smb2.tcon,
4234 to_write,
4235 state->offset + state->written,
4236 state->ph->fid_persistent,
4237 state->ph->fid_volatile,
4238 0, /* remaining_bytes */
4239 state->flags, /* flags */
4240 state->buf + state->written);
4242 if (tevent_req_nomem(subreq, req)) {
4243 return;
4245 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4248 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4249 size_t *pwritten)
4251 struct cli_smb2_writeall_state *state = tevent_req_data(
4252 req, struct cli_smb2_writeall_state);
4253 NTSTATUS status;
4255 if (tevent_req_is_nterror(req, &status)) {
4256 state->cli->raw_status = status;
4257 return status;
4259 if (pwritten != NULL) {
4260 *pwritten = (size_t)state->written;
4262 state->cli->raw_status = NT_STATUS_OK;
4263 return NT_STATUS_OK;
4266 struct cli_smb2_splice_state {
4267 struct tevent_context *ev;
4268 struct cli_state *cli;
4269 struct smb2_hnd *src_ph;
4270 struct smb2_hnd *dst_ph;
4271 int (*splice_cb)(off_t n, void *priv);
4272 void *priv;
4273 off_t written;
4274 off_t size;
4275 off_t src_offset;
4276 off_t dst_offset;
4277 bool resized;
4278 struct req_resume_key_rsp resume_rsp;
4279 struct srv_copychunk_copy cc_copy;
4282 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4283 struct tevent_req *req);
4285 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4287 struct tevent_req *req = tevent_req_callback_data(
4288 subreq, struct tevent_req);
4289 struct cli_smb2_splice_state *state =
4290 tevent_req_data(req,
4291 struct cli_smb2_splice_state);
4292 struct smbXcli_conn *conn = state->cli->conn;
4293 DATA_BLOB out_input_buffer = data_blob_null;
4294 DATA_BLOB out_output_buffer = data_blob_null;
4295 struct srv_copychunk_rsp cc_copy_rsp;
4296 enum ndr_err_code ndr_ret;
4297 NTSTATUS status;
4299 status = smb2cli_ioctl_recv(subreq, state,
4300 &out_input_buffer,
4301 &out_output_buffer);
4302 TALLOC_FREE(subreq);
4303 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4304 state->resized) && tevent_req_nterror(req, status)) {
4305 return;
4308 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4309 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4310 if (ndr_ret != NDR_ERR_SUCCESS) {
4311 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4312 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4313 return;
4316 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4317 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4318 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4319 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4320 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4321 tevent_req_nterror(req, status)) {
4322 return;
4325 state->resized = true;
4326 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4327 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4328 } else {
4329 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4330 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4331 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4332 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4333 return;
4335 state->src_offset += cc_copy_rsp.total_bytes_written;
4336 state->dst_offset += cc_copy_rsp.total_bytes_written;
4337 state->written += cc_copy_rsp.total_bytes_written;
4338 if (!state->splice_cb(state->written, state->priv)) {
4339 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4340 return;
4344 cli_splice_copychunk_send(state, req);
4347 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4348 struct tevent_req *req)
4350 struct tevent_req *subreq;
4351 enum ndr_err_code ndr_ret;
4352 struct smbXcli_conn *conn = state->cli->conn;
4353 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4354 off_t src_offset = state->src_offset;
4355 off_t dst_offset = state->dst_offset;
4356 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4357 state->size - state->written);
4358 DATA_BLOB in_input_buffer = data_blob_null;
4359 DATA_BLOB in_output_buffer = data_blob_null;
4361 if (state->size - state->written == 0) {
4362 tevent_req_done(req);
4363 return;
4366 cc_copy->chunk_count = 0;
4367 while (req_len) {
4368 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4369 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4370 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4371 smb2cli_conn_cc_chunk_len(conn));
4372 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4373 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4374 return;
4376 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4377 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4378 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4379 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4380 return;
4382 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4383 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4384 cc_copy->chunk_count++;
4387 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4388 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4389 if (ndr_ret != NDR_ERR_SUCCESS) {
4390 DEBUG(0, ("failed to marshall copy chunk req\n"));
4391 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4392 return;
4395 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4396 state->cli->timeout,
4397 state->cli->smb2.session,
4398 state->cli->smb2.tcon,
4399 state->dst_ph->fid_persistent, /* in_fid_persistent */
4400 state->dst_ph->fid_volatile, /* in_fid_volatile */
4401 FSCTL_SRV_COPYCHUNK_WRITE,
4402 0, /* in_max_input_length */
4403 &in_input_buffer,
4404 12, /* in_max_output_length */
4405 &in_output_buffer,
4406 SMB2_IOCTL_FLAG_IS_FSCTL);
4407 if (tevent_req_nomem(subreq, req)) {
4408 return;
4410 tevent_req_set_callback(subreq,
4411 cli_splice_copychunk_done,
4412 req);
4415 static void cli_splice_key_done(struct tevent_req *subreq)
4417 struct tevent_req *req = tevent_req_callback_data(
4418 subreq, struct tevent_req);
4419 struct cli_smb2_splice_state *state =
4420 tevent_req_data(req,
4421 struct cli_smb2_splice_state);
4422 enum ndr_err_code ndr_ret;
4423 NTSTATUS status;
4425 DATA_BLOB out_input_buffer = data_blob_null;
4426 DATA_BLOB out_output_buffer = data_blob_null;
4428 status = smb2cli_ioctl_recv(subreq, state,
4429 &out_input_buffer,
4430 &out_output_buffer);
4431 TALLOC_FREE(subreq);
4432 if (tevent_req_nterror(req, status)) {
4433 return;
4436 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4437 state, &state->resume_rsp,
4438 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4439 if (ndr_ret != NDR_ERR_SUCCESS) {
4440 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4441 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4442 return;
4445 memcpy(&state->cc_copy.source_key,
4446 &state->resume_rsp.resume_key,
4447 sizeof state->resume_rsp.resume_key);
4449 cli_splice_copychunk_send(state, req);
4452 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4453 struct tevent_context *ev,
4454 struct cli_state *cli,
4455 uint16_t src_fnum, uint16_t dst_fnum,
4456 off_t size, off_t src_offset, off_t dst_offset,
4457 int (*splice_cb)(off_t n, void *priv),
4458 void *priv)
4460 struct tevent_req *req;
4461 struct tevent_req *subreq;
4462 struct cli_smb2_splice_state *state;
4463 NTSTATUS status;
4464 DATA_BLOB in_input_buffer = data_blob_null;
4465 DATA_BLOB in_output_buffer = data_blob_null;
4467 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4468 if (req == NULL) {
4469 return NULL;
4471 state->cli = cli;
4472 state->ev = ev;
4473 state->splice_cb = splice_cb;
4474 state->priv = priv;
4475 state->size = size;
4476 state->written = 0;
4477 state->src_offset = src_offset;
4478 state->dst_offset = dst_offset;
4479 state->cc_copy.chunks = talloc_array(state,
4480 struct srv_copychunk,
4481 smb2cli_conn_cc_max_chunks(cli->conn));
4482 if (state->cc_copy.chunks == NULL) {
4483 return NULL;
4486 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4487 if (tevent_req_nterror(req, status))
4488 return tevent_req_post(req, ev);
4490 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4491 if (tevent_req_nterror(req, status))
4492 return tevent_req_post(req, ev);
4494 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4495 cli->timeout,
4496 cli->smb2.session,
4497 cli->smb2.tcon,
4498 state->src_ph->fid_persistent, /* in_fid_persistent */
4499 state->src_ph->fid_volatile, /* in_fid_volatile */
4500 FSCTL_SRV_REQUEST_RESUME_KEY,
4501 0, /* in_max_input_length */
4502 &in_input_buffer,
4503 32, /* in_max_output_length */
4504 &in_output_buffer,
4505 SMB2_IOCTL_FLAG_IS_FSCTL);
4506 if (tevent_req_nomem(subreq, req)) {
4507 return NULL;
4509 tevent_req_set_callback(subreq,
4510 cli_splice_key_done,
4511 req);
4513 return req;
4516 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4518 struct cli_smb2_splice_state *state = tevent_req_data(
4519 req, struct cli_smb2_splice_state);
4520 NTSTATUS status;
4522 if (tevent_req_is_nterror(req, &status)) {
4523 state->cli->raw_status = status;
4524 tevent_req_received(req);
4525 return status;
4527 if (written != NULL) {
4528 *written = state->written;
4530 state->cli->raw_status = NT_STATUS_OK;
4531 tevent_req_received(req);
4532 return NT_STATUS_OK;
4535 /***************************************************************
4536 SMB2 enum shadow copy data.
4537 ***************************************************************/
4539 struct cli_smb2_shadow_copy_data_fnum_state {
4540 struct cli_state *cli;
4541 uint16_t fnum;
4542 struct smb2_hnd *ph;
4543 DATA_BLOB out_input_buffer;
4544 DATA_BLOB out_output_buffer;
4547 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4549 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4550 TALLOC_CTX *mem_ctx,
4551 struct tevent_context *ev,
4552 struct cli_state *cli,
4553 uint16_t fnum,
4554 bool get_names)
4556 struct tevent_req *req, *subreq;
4557 struct cli_smb2_shadow_copy_data_fnum_state *state;
4558 NTSTATUS status;
4560 req = tevent_req_create(mem_ctx, &state,
4561 struct cli_smb2_shadow_copy_data_fnum_state);
4562 if (req == NULL) {
4563 return NULL;
4566 state->cli = cli;
4567 state->fnum = fnum;
4569 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4570 if (tevent_req_nterror(req, status)) {
4571 return tevent_req_post(req, ev);
4575 * TODO. Under SMB2 we should send a zero max_output_length
4576 * ioctl to get the required size, then send another ioctl
4577 * to get the data, but the current SMB1 implementation just
4578 * does one roundtrip with a 64K buffer size. Do the same
4579 * for now. JRA.
4582 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4583 state->cli->timeout,
4584 state->cli->smb2.session,
4585 state->cli->smb2.tcon,
4586 state->ph->fid_persistent, /* in_fid_persistent */
4587 state->ph->fid_volatile, /* in_fid_volatile */
4588 FSCTL_GET_SHADOW_COPY_DATA,
4589 0, /* in_max_input_length */
4590 NULL, /* in_input_buffer */
4591 get_names ?
4592 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4593 NULL, /* in_output_buffer */
4594 SMB2_IOCTL_FLAG_IS_FSCTL);
4596 if (tevent_req_nomem(subreq, req)) {
4597 return tevent_req_post(req, ev);
4599 tevent_req_set_callback(subreq,
4600 cli_smb2_shadow_copy_data_fnum_done,
4601 req);
4603 return req;
4606 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4608 struct tevent_req *req = tevent_req_callback_data(
4609 subreq, struct tevent_req);
4610 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4611 req, struct cli_smb2_shadow_copy_data_fnum_state);
4612 NTSTATUS status;
4614 status = smb2cli_ioctl_recv(subreq, state,
4615 &state->out_input_buffer,
4616 &state->out_output_buffer);
4617 tevent_req_simple_finish_ntstatus(subreq, status);
4620 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4621 TALLOC_CTX *mem_ctx,
4622 bool get_names,
4623 char ***pnames,
4624 int *pnum_names)
4626 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4627 req, struct cli_smb2_shadow_copy_data_fnum_state);
4628 char **names = NULL;
4629 uint32_t num_names = 0;
4630 uint32_t num_names_returned = 0;
4631 uint32_t dlength = 0;
4632 uint32_t i;
4633 uint8_t *endp = NULL;
4634 NTSTATUS status;
4636 if (tevent_req_is_nterror(req, &status)) {
4637 return status;
4640 if (state->out_output_buffer.length < 16) {
4641 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4644 num_names = IVAL(state->out_output_buffer.data, 0);
4645 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4646 dlength = IVAL(state->out_output_buffer.data, 8);
4648 if (num_names > 0x7FFFFFFF) {
4649 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4652 if (get_names == false) {
4653 *pnum_names = (int)num_names;
4654 return NT_STATUS_OK;
4656 if (num_names != num_names_returned) {
4657 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4659 if (dlength + 12 < 12) {
4660 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4663 * NB. The below is an allowable return if there are
4664 * more snapshots than the buffer size we told the
4665 * server we can receive. We currently don't support
4666 * this.
4668 if (dlength + 12 > state->out_output_buffer.length) {
4669 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4671 if (state->out_output_buffer.length +
4672 (2 * sizeof(SHADOW_COPY_LABEL)) <
4673 state->out_output_buffer.length) {
4674 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4677 names = talloc_array(mem_ctx, char *, num_names_returned);
4678 if (names == NULL) {
4679 return NT_STATUS_NO_MEMORY;
4682 endp = state->out_output_buffer.data +
4683 state->out_output_buffer.length;
4685 for (i=0; i<num_names_returned; i++) {
4686 bool ret;
4687 uint8_t *src;
4688 size_t converted_size;
4690 src = state->out_output_buffer.data + 12 +
4691 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4693 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4694 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4696 ret = convert_string_talloc(
4697 names, CH_UTF16LE, CH_UNIX,
4698 src, 2 * sizeof(SHADOW_COPY_LABEL),
4699 &names[i], &converted_size);
4700 if (!ret) {
4701 TALLOC_FREE(names);
4702 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4705 *pnum_names = num_names;
4706 *pnames = names;
4707 return NT_STATUS_OK;
4710 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4711 struct cli_state *cli,
4712 uint16_t fnum,
4713 bool get_names,
4714 char ***pnames,
4715 int *pnum_names)
4717 TALLOC_CTX *frame = talloc_stackframe();
4718 struct tevent_context *ev;
4719 struct tevent_req *req;
4720 NTSTATUS status = NT_STATUS_NO_MEMORY;
4722 if (smbXcli_conn_has_async_calls(cli->conn)) {
4724 * Can't use sync call while an async call is in flight
4726 status = NT_STATUS_INVALID_PARAMETER;
4727 goto fail;
4729 ev = samba_tevent_context_init(frame);
4730 if (ev == NULL) {
4731 goto fail;
4733 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4735 cli,
4736 fnum,
4737 get_names);
4738 if (req == NULL) {
4739 goto fail;
4741 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4742 goto fail;
4744 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4745 mem_ctx,
4746 get_names,
4747 pnames,
4748 pnum_names);
4749 fail:
4750 cli->raw_status = status;
4752 TALLOC_FREE(frame);
4753 return status;
4756 /***************************************************************
4757 Wrapper that allows SMB2 to truncate a file.
4758 Synchronous only.
4759 ***************************************************************/
4761 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4762 uint16_t fnum,
4763 uint64_t newsize)
4765 NTSTATUS status;
4766 uint8_t buf[8] = {0};
4767 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4768 TALLOC_CTX *frame = talloc_stackframe();
4770 if (smbXcli_conn_has_async_calls(cli->conn)) {
4772 * Can't use sync call while an async call is in flight
4774 status = NT_STATUS_INVALID_PARAMETER;
4775 goto fail;
4778 SBVAL(buf, 0, newsize);
4780 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4781 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4783 status = cli_smb2_set_info_fnum(
4784 cli,
4785 fnum,
4786 1, /* in_info_type */
4787 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4788 &inbuf, /* in_input_buffer */
4791 fail:
4793 cli->raw_status = status;
4795 TALLOC_FREE(frame);
4796 return status;
4799 struct cli_smb2_notify_state {
4800 struct tevent_req *subreq;
4801 struct notify_change *changes;
4802 size_t num_changes;
4805 static void cli_smb2_notify_done(struct tevent_req *subreq);
4806 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4808 struct tevent_req *cli_smb2_notify_send(
4809 TALLOC_CTX *mem_ctx,
4810 struct tevent_context *ev,
4811 struct cli_state *cli,
4812 uint16_t fnum,
4813 uint32_t buffer_size,
4814 uint32_t completion_filter,
4815 bool recursive)
4817 struct tevent_req *req = NULL;
4818 struct cli_smb2_notify_state *state = NULL;
4819 struct smb2_hnd *ph = NULL;
4820 NTSTATUS status;
4822 req = tevent_req_create(mem_ctx, &state,
4823 struct cli_smb2_notify_state);
4824 if (req == NULL) {
4825 return NULL;
4828 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4829 if (tevent_req_nterror(req, status)) {
4830 return tevent_req_post(req, ev);
4833 state->subreq = smb2cli_notify_send(
4834 state,
4836 cli->conn,
4837 cli->timeout,
4838 cli->smb2.session,
4839 cli->smb2.tcon,
4840 buffer_size,
4841 ph->fid_persistent,
4842 ph->fid_volatile,
4843 completion_filter,
4844 recursive);
4845 if (tevent_req_nomem(state->subreq, req)) {
4846 return tevent_req_post(req, ev);
4848 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4849 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4850 return req;
4853 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4855 struct cli_smb2_notify_state *state = tevent_req_data(
4856 req, struct cli_smb2_notify_state);
4857 bool ok;
4859 ok = tevent_req_cancel(state->subreq);
4860 return ok;
4863 static void cli_smb2_notify_done(struct tevent_req *subreq)
4865 struct tevent_req *req = tevent_req_callback_data(
4866 subreq, struct tevent_req);
4867 struct cli_smb2_notify_state *state = tevent_req_data(
4868 req, struct cli_smb2_notify_state);
4869 uint8_t *base;
4870 uint32_t len;
4871 uint32_t ofs;
4872 NTSTATUS status;
4874 status = smb2cli_notify_recv(subreq, state, &base, &len);
4875 TALLOC_FREE(subreq);
4877 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4878 tevent_req_done(req);
4879 return;
4881 if (tevent_req_nterror(req, status)) {
4882 return;
4885 ofs = 0;
4887 while (len - ofs >= 12) {
4888 struct notify_change *tmp;
4889 struct notify_change *c;
4890 uint32_t next_ofs = IVAL(base, ofs);
4891 uint32_t file_name_length = IVAL(base, ofs+8);
4892 size_t namelen;
4893 bool ok;
4895 tmp = talloc_realloc(
4896 state,
4897 state->changes,
4898 struct notify_change,
4899 state->num_changes + 1);
4900 if (tevent_req_nomem(tmp, req)) {
4901 return;
4903 state->changes = tmp;
4904 c = &state->changes[state->num_changes];
4905 state->num_changes += 1;
4907 if (smb_buffer_oob(len, ofs, next_ofs) ||
4908 smb_buffer_oob(len, ofs+12, file_name_length)) {
4909 tevent_req_nterror(
4910 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4911 return;
4914 c->action = IVAL(base, ofs+4);
4916 ok = convert_string_talloc(
4917 state->changes,
4918 CH_UTF16LE,
4919 CH_UNIX,
4920 base + ofs + 12,
4921 file_name_length,
4922 &c->name,
4923 &namelen);
4924 if (!ok) {
4925 tevent_req_nterror(
4926 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4927 return;
4930 if (next_ofs == 0) {
4931 break;
4933 ofs += next_ofs;
4936 tevent_req_done(req);
4939 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4940 TALLOC_CTX *mem_ctx,
4941 struct notify_change **pchanges,
4942 uint32_t *pnum_changes)
4944 struct cli_smb2_notify_state *state = tevent_req_data(
4945 req, struct cli_smb2_notify_state);
4946 NTSTATUS status;
4948 if (tevent_req_is_nterror(req, &status)) {
4949 return status;
4951 *pchanges = talloc_move(mem_ctx, &state->changes);
4952 *pnum_changes = state->num_changes;
4953 return NT_STATUS_OK;
4956 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4957 uint32_t buffer_size, uint32_t completion_filter,
4958 bool recursive, TALLOC_CTX *mem_ctx,
4959 struct notify_change **pchanges,
4960 uint32_t *pnum_changes)
4962 TALLOC_CTX *frame = talloc_stackframe();
4963 struct tevent_context *ev;
4964 struct tevent_req *req;
4965 NTSTATUS status = NT_STATUS_NO_MEMORY;
4967 if (smbXcli_conn_has_async_calls(cli->conn)) {
4969 * Can't use sync call while an async call is in flight
4971 status = NT_STATUS_INVALID_PARAMETER;
4972 goto fail;
4974 ev = samba_tevent_context_init(frame);
4975 if (ev == NULL) {
4976 goto fail;
4978 req = cli_smb2_notify_send(
4979 frame,
4981 cli,
4982 fnum,
4983 buffer_size,
4984 completion_filter,
4985 recursive);
4986 if (req == NULL) {
4987 goto fail;
4989 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4990 goto fail;
4992 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4993 fail:
4994 TALLOC_FREE(frame);
4995 return status;
4998 struct cli_smb2_fsctl_state {
4999 DATA_BLOB out;
5002 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
5004 struct tevent_req *cli_smb2_fsctl_send(
5005 TALLOC_CTX *mem_ctx,
5006 struct tevent_context *ev,
5007 struct cli_state *cli,
5008 uint16_t fnum,
5009 uint32_t ctl_code,
5010 const DATA_BLOB *in,
5011 uint32_t max_out)
5013 struct tevent_req *req = NULL, *subreq = NULL;
5014 struct cli_smb2_fsctl_state *state = NULL;
5015 struct smb2_hnd *ph = NULL;
5016 NTSTATUS status;
5018 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
5019 if (req == NULL) {
5020 return NULL;
5023 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
5024 if (tevent_req_nterror(req, status)) {
5025 return tevent_req_post(req, ev);
5028 subreq = smb2cli_ioctl_send(
5029 state,
5031 cli->conn,
5032 cli->timeout,
5033 cli->smb2.session,
5034 cli->smb2.tcon,
5035 ph->fid_persistent,
5036 ph->fid_volatile,
5037 ctl_code,
5038 0, /* in_max_input_length */
5040 max_out,
5041 NULL,
5042 SMB2_IOCTL_FLAG_IS_FSCTL);
5044 if (tevent_req_nomem(subreq, req)) {
5045 return tevent_req_post(req, ev);
5047 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
5048 return req;
5051 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5053 struct tevent_req *req = tevent_req_callback_data(
5054 subreq, struct tevent_req);
5055 struct cli_smb2_fsctl_state *state = tevent_req_data(
5056 req, struct cli_smb2_fsctl_state);
5057 NTSTATUS status;
5059 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5060 tevent_req_simple_finish_ntstatus(subreq, status);
5063 NTSTATUS cli_smb2_fsctl_recv(
5064 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5066 struct cli_smb2_fsctl_state *state = tevent_req_data(
5067 req, struct cli_smb2_fsctl_state);
5068 NTSTATUS status = NT_STATUS_OK;
5070 if (tevent_req_is_nterror(req, &status)) {
5071 tevent_req_received(req);
5072 return status;
5075 if (state->out.length == 0) {
5076 *out = (DATA_BLOB) { .data = NULL, };
5077 } else {
5079 * Can't use talloc_move() here, the outblobs from
5080 * smb2cli_ioctl_recv() are not standalone talloc
5081 * objects but just peek into the larger buffers
5082 * received, hanging off "state".
5084 *out = data_blob_talloc(
5085 mem_ctx, state->out.data, state->out.length);
5086 if (out->data == NULL) {
5087 status = NT_STATUS_NO_MEMORY;
5091 tevent_req_received(req);
5092 return NT_STATUS_OK;