2 Unix SMB/CIFS implementation.
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.
31 #include "async_smb.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "cli_smb2_fnum.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"
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"
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 */
68 struct idr_context *idp = cli->smb2.open_handles;
69 struct smb2_hnd *owned_h = talloc_memdup(cli,
71 sizeof(struct smb2_hnd));
73 if (owned_h == NULL) {
74 return NT_STATUS_NO_MEMORY;
79 cli->smb2.open_handles = idr_init(cli);
80 if (cli->smb2.open_handles == NULL) {
82 return NT_STATUS_NO_MEMORY;
84 idp = cli->smb2.open_handles;
87 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
90 return NT_STATUS_NO_MEMORY;
93 *pfnum = (uint16_t)ret;
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;
108 return NT_STATUS_INVALID_PARAMETER;
110 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
112 return NT_STATUS_INVALID_HANDLE;
117 /***************************************************************
118 Delete the fnum to smb2_hnd mapping. Zeros out handle on
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;
130 return NT_STATUS_INVALID_PARAMETER;
133 ph = (struct smb2_hnd *)idr_find(idp, fnum);
135 return NT_STATUS_INVALID_PARAMETER;
137 idr_remove(idp, fnum);
142 /***************************************************************
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,
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;
174 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
175 if (is_already_dfs_path) {
178 if (path[0] == '\0') {
179 return talloc_asprintf(ctx,
181 smbXcli_conn_remote_name(cli->conn),
184 while (*path == '\\') {
187 return talloc_asprintf(ctx,
189 smbXcli_conn_remote_name(cli->conn),
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;
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(
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;
228 size_t fname_len = 0;
233 req = tevent_req_create(mem_ctx, &state,
234 struct cli_smb2_create_fnum_state);
240 fname = talloc_strdup(state, fname_in);
241 if (tevent_req_nomem(fname, req)) {
242 return tevent_req_post(req, ev);
245 if (cli->backup_intent) {
246 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
249 if (cli->smb2.client_smb311_posix) {
250 uint8_t modebuf[4] = {
255 smb2_create_blob_add(state,
257 SMB2_CREATE_TAG_POSIX,
260 .length = sizeof(modebuf),
262 if (tevent_req_nterror(req, status)) {
263 return tevent_req_post(req, ev);
267 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
268 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
270 status = smb2_create_blob_add(
273 SMB2_CREATE_TAG_TWRP,
275 .data = (uint8_t *)&ntt,
276 .length = sizeof(ntt),
278 if (tevent_req_nterror(req, status)) {
279 return tevent_req_post(req, ev);
283 if (in_cblobs != NULL) {
285 for (i=0; i<in_cblobs->num_blobs; i++) {
286 struct smb2_create_blob *b = &in_cblobs->blobs[i];
287 status = smb2_create_blob_add(
288 state, &state->in_cblobs, b->tag, b->data);
289 if (!NT_STATUS_IS_OK(status)) {
290 tevent_req_nterror(req, status);
291 return tevent_req_post(req, ev);
296 fname = smb2_dfs_share_path(state, cli, fname);
297 if (tevent_req_nomem(fname, req)) {
298 return tevent_req_post(req, ev);
300 fname_len = strlen(fname);
302 /* SMB2 is pickier about pathnames. Ensure it doesn't
304 if (*fname == '\\') {
309 /* Or end in a '\' */
310 if (fname_len > 0 && fname[fname_len-1] == '\\') {
311 fname[fname_len-1] = '\0';
314 subreq = smb2cli_create_send(state, ev,
320 flags_to_smb2_oplock(create_flags),
328 if (tevent_req_nomem(subreq, req)) {
329 return tevent_req_post(req, ev);
331 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
333 state->subreq = subreq;
334 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
339 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
341 struct tevent_req *req = tevent_req_callback_data(
342 subreq, struct tevent_req);
343 struct cli_smb2_create_fnum_state *state = tevent_req_data(
344 req, struct cli_smb2_create_fnum_state);
348 status = smb2cli_create_recv(
351 &h.fid_volatile, &state->cr,
356 if (tevent_req_nterror(req, status)) {
360 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
361 if (tevent_req_nterror(req, status)) {
364 tevent_req_done(req);
367 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
369 struct cli_smb2_create_fnum_state *state = tevent_req_data(
370 req, struct cli_smb2_create_fnum_state);
371 return tevent_req_cancel(state->subreq);
374 NTSTATUS cli_smb2_create_fnum_recv(
375 struct tevent_req *req,
377 struct smb_create_returns *cr,
379 struct smb2_create_blobs *out_cblobs,
380 struct symlink_reparse_struct **symlink)
382 struct cli_smb2_create_fnum_state *state = tevent_req_data(
383 req, struct cli_smb2_create_fnum_state);
386 if (tevent_req_is_nterror(req, &status)) {
387 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
389 *symlink = talloc_move(mem_ctx, &state->symlink);
391 state->cli->raw_status = status;
395 *pfnum = state->fnum;
400 if (out_cblobs != NULL) {
401 *out_cblobs = (struct smb2_create_blobs) {
402 .num_blobs = state->out_cblobs.num_blobs,
403 .blobs = talloc_move(
404 mem_ctx, &state->out_cblobs.blobs),
407 state->cli->raw_status = NT_STATUS_OK;
411 NTSTATUS cli_smb2_create_fnum(
412 struct cli_state *cli,
414 struct cli_smb2_create_flags create_flags,
415 uint32_t impersonation_level,
416 uint32_t desired_access,
417 uint32_t file_attributes,
418 uint32_t share_access,
419 uint32_t create_disposition,
420 uint32_t create_options,
421 const struct smb2_create_blobs *in_cblobs,
423 struct smb_create_returns *cr,
425 struct smb2_create_blobs *out_cblobs)
427 TALLOC_CTX *frame = talloc_stackframe();
428 struct tevent_context *ev;
429 struct tevent_req *req;
430 NTSTATUS status = NT_STATUS_NO_MEMORY;
432 if (smbXcli_conn_has_async_calls(cli->conn)) {
434 * Can't use sync call while an async call is in flight
436 status = NT_STATUS_INVALID_PARAMETER;
439 ev = samba_tevent_context_init(frame);
443 req = cli_smb2_create_fnum_send(
459 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
462 status = cli_smb2_create_fnum_recv(
463 req, pfid, cr, mem_ctx, out_cblobs, NULL);
469 /***************************************************************
470 Small wrapper that allows SMB2 close to use a uint16_t fnum.
471 ***************************************************************/
473 struct cli_smb2_close_fnum_state {
474 struct cli_state *cli;
479 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
481 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
482 struct tevent_context *ev,
483 struct cli_state *cli,
487 struct tevent_req *req, *subreq;
488 struct cli_smb2_close_fnum_state *state;
491 req = tevent_req_create(mem_ctx, &state,
492 struct cli_smb2_close_fnum_state);
499 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
500 if (tevent_req_nterror(req, status)) {
501 return tevent_req_post(req, ev);
504 subreq = smb2cli_close_send(state,
511 state->ph->fid_persistent,
512 state->ph->fid_volatile);
513 if (tevent_req_nomem(subreq, req)) {
514 return tevent_req_post(req, ev);
516 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
520 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
522 struct tevent_req *req = tevent_req_callback_data(
523 subreq, struct tevent_req);
524 struct cli_smb2_close_fnum_state *state = tevent_req_data(
525 req, struct cli_smb2_close_fnum_state);
528 status = smb2cli_close_recv(subreq);
529 if (tevent_req_nterror(req, status)) {
533 /* Delete the fnum -> handle mapping. */
534 status = delete_smb2_handle_mapping(state->cli, &state->ph,
536 if (tevent_req_nterror(req, status)) {
539 tevent_req_done(req);
542 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
544 struct cli_smb2_close_fnum_state *state = tevent_req_data(
545 req, struct cli_smb2_close_fnum_state);
546 NTSTATUS status = NT_STATUS_OK;
548 if (tevent_req_is_nterror(req, &status)) {
549 state->cli->raw_status = status;
551 tevent_req_received(req);
555 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
557 TALLOC_CTX *frame = talloc_stackframe();
558 struct tevent_context *ev;
559 struct tevent_req *req;
560 NTSTATUS status = NT_STATUS_NO_MEMORY;
562 if (smbXcli_conn_has_async_calls(cli->conn)) {
564 * Can't use sync call while an async call is in flight
566 status = NT_STATUS_INVALID_PARAMETER;
569 ev = samba_tevent_context_init(frame);
573 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
577 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
580 status = cli_smb2_close_fnum_recv(req);
586 struct cli_smb2_set_info_fnum_state {
590 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
592 struct tevent_req *cli_smb2_set_info_fnum_send(
594 struct tevent_context *ev,
595 struct cli_state *cli,
597 uint8_t in_info_type,
598 uint8_t in_info_class,
599 const DATA_BLOB *in_input_buffer,
600 uint32_t in_additional_info)
602 struct tevent_req *req = NULL, *subreq = NULL;
603 struct cli_smb2_set_info_fnum_state *state = NULL;
604 struct smb2_hnd *ph = NULL;
607 req = tevent_req_create(
608 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
613 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
614 if (tevent_req_nterror(req, status)) {
615 return tevent_req_post(req, ev);
618 subreq = smb2cli_set_info_send(
631 if (tevent_req_nomem(subreq, req)) {
632 return tevent_req_post(req, ev);
634 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
638 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
640 NTSTATUS status = smb2cli_set_info_recv(subreq);
641 tevent_req_simple_finish_ntstatus(subreq, status);
644 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
646 return tevent_req_simple_recv_ntstatus(req);
649 NTSTATUS cli_smb2_set_info_fnum(
650 struct cli_state *cli,
652 uint8_t in_info_type,
653 uint8_t in_info_class,
654 const DATA_BLOB *in_input_buffer,
655 uint32_t in_additional_info)
657 TALLOC_CTX *frame = talloc_stackframe();
658 struct tevent_context *ev = NULL;
659 struct tevent_req *req = NULL;
660 NTSTATUS status = NT_STATUS_NO_MEMORY;
663 if (smbXcli_conn_has_async_calls(cli->conn)) {
665 * Can't use sync call while an async call is in flight
667 status = NT_STATUS_INVALID_PARAMETER;
670 ev = samba_tevent_context_init(frame);
674 req = cli_smb2_set_info_fnum_send(
686 ok = tevent_req_poll_ntstatus(req, ev, &status);
690 status = cli_smb2_set_info_fnum_recv(req);
696 struct cli_smb2_delete_on_close_state {
697 struct cli_state *cli;
702 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
704 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
705 struct tevent_context *ev,
706 struct cli_state *cli,
710 struct tevent_req *req = NULL;
711 struct cli_smb2_delete_on_close_state *state = NULL;
712 struct tevent_req *subreq = NULL;
713 uint8_t in_info_type;
714 uint8_t in_file_info_class;
716 req = tevent_req_create(mem_ctx, &state,
717 struct cli_smb2_delete_on_close_state);
724 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
725 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
728 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
729 /* Setup data array. */
730 SCVAL(&state->data[0], 0, flag ? 1 : 0);
731 state->inbuf.data = &state->data[0];
732 state->inbuf.length = 1;
734 subreq = cli_smb2_set_info_fnum_send(
743 if (tevent_req_nomem(subreq, req)) {
744 return tevent_req_post(req, ev);
746 tevent_req_set_callback(subreq,
747 cli_smb2_delete_on_close_done,
752 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
754 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
755 tevent_req_simple_finish_ntstatus(subreq, status);
758 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
760 struct cli_smb2_delete_on_close_state *state =
762 struct cli_smb2_delete_on_close_state);
765 if (tevent_req_is_nterror(req, &status)) {
766 state->cli->raw_status = status;
767 tevent_req_received(req);
771 state->cli->raw_status = NT_STATUS_OK;
772 tevent_req_received(req);
776 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
778 TALLOC_CTX *frame = talloc_stackframe();
779 struct tevent_context *ev;
780 struct tevent_req *req;
781 NTSTATUS status = NT_STATUS_NO_MEMORY;
783 if (smbXcli_conn_has_async_calls(cli->conn)) {
785 * Can't use sync call while an async call is in flight
787 status = NT_STATUS_INVALID_PARAMETER;
790 ev = samba_tevent_context_init(frame);
794 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
798 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
801 status = cli_smb2_delete_on_close_recv(req);
807 struct cli_smb2_mkdir_state {
808 struct tevent_context *ev;
809 struct cli_state *cli;
812 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
813 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
815 struct tevent_req *cli_smb2_mkdir_send(
817 struct tevent_context *ev,
818 struct cli_state *cli,
821 struct tevent_req *req = NULL, *subreq = NULL;
822 struct cli_smb2_mkdir_state *state = NULL;
824 req = tevent_req_create(
825 mem_ctx, &state, struct cli_smb2_mkdir_state);
832 /* Ensure this is a directory. */
833 subreq = cli_smb2_create_fnum_send(
838 (struct cli_smb2_create_flags){0}, /* create_flags */
839 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
840 FILE_READ_ATTRIBUTES, /* desired_access */
841 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
843 FILE_SHARE_WRITE, /* share_access */
844 FILE_CREATE, /* create_disposition */
845 FILE_DIRECTORY_FILE, /* create_options */
846 NULL); /* in_cblobs */
847 if (tevent_req_nomem(subreq, req)) {
848 return tevent_req_post(req, ev);
850 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
854 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
856 struct tevent_req *req = tevent_req_callback_data(
857 subreq, struct tevent_req);
858 struct cli_smb2_mkdir_state *state = tevent_req_data(
859 req, struct cli_smb2_mkdir_state);
861 uint16_t fnum = 0xffff;
863 status = cli_smb2_create_fnum_recv(
864 subreq, &fnum, NULL, NULL, NULL, NULL);
866 if (tevent_req_nterror(req, status)) {
871 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
872 if (tevent_req_nomem(subreq, req)) {
875 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
878 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
880 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
881 tevent_req_simple_finish_ntstatus(subreq, status);
884 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
886 return tevent_req_simple_recv_ntstatus(req);
889 struct cli_smb2_rmdir_state {
890 struct tevent_context *ev;
891 struct cli_state *cli;
893 const struct smb2_create_blobs *in_cblobs;
898 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
899 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
900 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
901 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
903 struct tevent_req *cli_smb2_rmdir_send(
905 struct tevent_context *ev,
906 struct cli_state *cli,
908 const struct smb2_create_blobs *in_cblobs)
910 struct tevent_req *req = NULL, *subreq = NULL;
911 struct cli_smb2_rmdir_state *state = NULL;
913 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
919 state->dname = dname;
920 state->in_cblobs = in_cblobs;
922 subreq = cli_smb2_create_fnum_send(
927 (struct cli_smb2_create_flags){0},
928 SMB2_IMPERSONATION_IMPERSONATION,
929 DELETE_ACCESS, /* desired_access */
930 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
931 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
932 FILE_OPEN, /* create_disposition */
933 FILE_DIRECTORY_FILE, /* create_options */
934 state->in_cblobs); /* in_cblobs */
935 if (tevent_req_nomem(subreq, req)) {
936 return tevent_req_post(req, ev);
938 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
942 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
944 struct tevent_req *req = tevent_req_callback_data(
945 subreq, struct tevent_req);
946 struct cli_smb2_rmdir_state *state = tevent_req_data(
947 req, struct cli_smb2_rmdir_state);
950 status = cli_smb2_create_fnum_recv(
951 subreq, &state->fnum, NULL, NULL, NULL, NULL);
954 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
956 * Naive option to match our SMB1 code. Assume the
957 * symlink path that tripped us up was the last
958 * component and try again. Eventually we will have to
959 * deal with the returned path unprocessed component. JRA.
961 subreq = cli_smb2_create_fnum_send(
966 (struct cli_smb2_create_flags){0},
967 SMB2_IMPERSONATION_IMPERSONATION,
968 DELETE_ACCESS, /* desired_access */
969 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
970 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
971 FILE_OPEN, /* create_disposition */
973 FILE_DELETE_ON_CLOSE|
974 FILE_OPEN_REPARSE_POINT, /* create_options */
975 state->in_cblobs); /* in_cblobs */
976 if (tevent_req_nomem(subreq, req)) {
979 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
983 if (tevent_req_nterror(req, status)) {
987 subreq = cli_smb2_delete_on_close_send(
988 state, state->ev, state->cli, state->fnum, true);
989 if (tevent_req_nomem(subreq, req)) {
992 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
995 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
997 struct tevent_req *req = tevent_req_callback_data(
998 subreq, struct tevent_req);
999 struct cli_smb2_rmdir_state *state = tevent_req_data(
1000 req, struct cli_smb2_rmdir_state);
1003 status = cli_smb2_create_fnum_recv(
1004 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1005 TALLOC_FREE(subreq);
1006 if (tevent_req_nterror(req, status)) {
1010 subreq = cli_smb2_delete_on_close_send(
1011 state, state->ev, state->cli, state->fnum, true);
1012 if (tevent_req_nomem(subreq, req)) {
1015 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1018 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1020 struct tevent_req *req = tevent_req_callback_data(
1021 subreq, struct tevent_req);
1022 struct cli_smb2_rmdir_state *state = tevent_req_data(
1023 req, struct cli_smb2_rmdir_state);
1025 state->status = cli_smb2_delete_on_close_recv(subreq);
1026 TALLOC_FREE(subreq);
1029 * Close the fd even if the set_disp failed
1032 subreq = cli_smb2_close_fnum_send(state,
1037 if (tevent_req_nomem(subreq, req)) {
1040 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1043 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1045 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1046 tevent_req_simple_finish_ntstatus(subreq, status);
1049 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1051 struct cli_smb2_rmdir_state *state = tevent_req_data(
1052 req, struct cli_smb2_rmdir_state);
1055 if (tevent_req_is_nterror(req, &status)) {
1058 return state->status;
1061 /***************************************************************
1062 Small wrapper that allows SMB2 to unlink a pathname.
1063 ***************************************************************/
1065 struct cli_smb2_unlink_state {
1066 struct tevent_context *ev;
1067 struct cli_state *cli;
1069 const struct smb2_create_blobs *in_cblobs;
1072 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1073 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1074 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1076 struct tevent_req *cli_smb2_unlink_send(
1077 TALLOC_CTX *mem_ctx,
1078 struct tevent_context *ev,
1079 struct cli_state *cli,
1081 const struct smb2_create_blobs *in_cblobs)
1083 struct tevent_req *req = NULL, *subreq = NULL;
1084 struct cli_smb2_unlink_state *state = NULL;
1086 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1092 state->fname = fname;
1093 state->in_cblobs = in_cblobs;
1095 subreq = cli_smb2_create_fnum_send(
1096 state, /* mem_ctx */
1097 state->ev, /* tevent_context */
1098 state->cli, /* cli_struct */
1099 state->fname, /* filename */
1100 (struct cli_smb2_create_flags){0},
1101 SMB2_IMPERSONATION_IMPERSONATION,
1102 DELETE_ACCESS, /* desired_access */
1103 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1106 FILE_SHARE_DELETE, /* share_access */
1107 FILE_OPEN, /* create_disposition */
1108 FILE_DELETE_ON_CLOSE, /* create_options */
1109 state->in_cblobs); /* in_cblobs */
1110 if (tevent_req_nomem(subreq, req)) {
1111 return tevent_req_post(req, ev);
1113 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1117 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1119 struct tevent_req *req = tevent_req_callback_data(
1120 subreq, struct tevent_req);
1121 struct cli_smb2_unlink_state *state = tevent_req_data(
1122 req, struct cli_smb2_unlink_state);
1123 uint16_t fnum = 0xffff;
1126 status = cli_smb2_create_fnum_recv(
1127 subreq, &fnum, NULL, NULL, NULL, NULL);
1128 TALLOC_FREE(subreq);
1130 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1131 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1133 * Naive option to match our SMB1 code. Assume the
1134 * symlink path that tripped us up was the last
1135 * component and try again. Eventually we will have to
1136 * deal with the returned path unprocessed component. JRA.
1138 subreq = cli_smb2_create_fnum_send(
1139 state, /* mem_ctx */
1140 state->ev, /* tevent_context */
1141 state->cli, /* cli_struct */
1142 state->fname, /* filename */
1143 (struct cli_smb2_create_flags){0},
1144 SMB2_IMPERSONATION_IMPERSONATION,
1145 DELETE_ACCESS, /* desired_access */
1146 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1149 FILE_SHARE_DELETE, /* share_access */
1150 FILE_OPEN, /* create_disposition */
1151 FILE_DELETE_ON_CLOSE|
1152 FILE_OPEN_REPARSE_POINT, /* create_options */
1153 state->in_cblobs); /* in_cblobs */
1154 if (tevent_req_nomem(subreq, req)) {
1157 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1161 if (tevent_req_nterror(req, status)) {
1166 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1167 if (tevent_req_nomem(subreq, req)) {
1170 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1173 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1175 struct tevent_req *req = tevent_req_callback_data(
1176 subreq, struct tevent_req);
1177 struct cli_smb2_unlink_state *state = tevent_req_data(
1178 req, struct cli_smb2_unlink_state);
1179 uint16_t fnum = 0xffff;
1182 status = cli_smb2_create_fnum_recv(
1183 subreq, &fnum, NULL, NULL, NULL, NULL);
1184 TALLOC_FREE(subreq);
1185 if (tevent_req_nterror(req, status)) {
1190 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1191 if (tevent_req_nomem(subreq, req)) {
1194 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1197 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1199 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1200 tevent_req_simple_finish_ntstatus(subreq, status);
1203 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1205 return tevent_req_simple_recv_ntstatus(req);
1208 /***************************************************************
1209 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1210 ***************************************************************/
1212 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1213 uint32_t dir_data_length,
1214 struct file_info *finfo,
1215 uint32_t *next_offset)
1217 struct smb3_file_posix_information info = {};
1219 enum ndr_err_code ndr_err;
1222 uint32_t _next_offset = 0;
1224 if (dir_data_length < 4) {
1225 return NT_STATUS_INFO_LENGTH_MISMATCH;
1228 _next_offset = IVAL(dir_data, 0);
1230 if (_next_offset > dir_data_length) {
1231 return NT_STATUS_INFO_LENGTH_MISMATCH;
1234 if (_next_offset != 0) {
1235 /* Ensure we only read what in this record. */
1236 dir_data_length = _next_offset;
1240 * Skip NextEntryOffset and FileIndex
1242 if (dir_data_length < 8) {
1243 return NT_STATUS_INFO_LENGTH_MISMATCH;
1246 dir_data_length -= 8;
1248 ndr_err = ndr_pull_struct_blob_noalloc(
1252 (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1254 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1255 return ndr_map_error2ntstatus(ndr_err);
1257 if (consumed > dir_data_length) {
1258 return NT_STATUS_INFO_LENGTH_MISMATCH;
1260 dir_data += consumed;
1261 dir_data_length -= consumed;
1263 finfo->btime_ts = interpret_long_date(info.creation_time);
1264 finfo->atime_ts = interpret_long_date(info.last_access_time);
1265 finfo->mtime_ts = interpret_long_date(info.last_write_time);
1266 finfo->ctime_ts = interpret_long_date(info.change_time);
1267 finfo->allocated_size = info.allocation_size;
1268 finfo->size = info.end_of_file;
1269 finfo->mode = info.file_attributes;
1270 finfo->ino = info.inode;
1271 finfo->st_ex_dev = info.device;
1272 finfo->st_ex_nlink = info.cc.nlinks;
1273 finfo->reparse_tag = info.cc.reparse_tag;
1274 finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
1275 sid_copy(&finfo->owner_sid, &info.cc.owner);
1276 sid_copy(&finfo->group_sid, &info.cc.group);
1278 if (dir_data_length < 4) {
1279 return NT_STATUS_INFO_LENGTH_MISMATCH;
1281 namelen = PULL_LE_U32(dir_data, 0);
1284 dir_data_length -= 4;
1286 if (namelen > dir_data_length) {
1287 return NT_STATUS_INFO_LENGTH_MISMATCH;
1290 ret = pull_string_talloc(finfo,
1292 FLAGS2_UNICODE_STRINGS,
1297 if (ret == (size_t)-1) {
1298 /* Bad conversion. */
1299 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1302 if (finfo->name == NULL) {
1303 /* Bad conversion. */
1304 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1307 *next_offset = _next_offset;
1308 return NT_STATUS_OK;
1311 /***************************************************************
1312 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1313 ***************************************************************/
1315 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1316 uint32_t dir_data_length,
1317 struct file_info *finfo,
1318 uint32_t *next_offset)
1324 if (dir_data_length < 4) {
1325 return NT_STATUS_INFO_LENGTH_MISMATCH;
1328 *next_offset = IVAL(dir_data, 0);
1330 if (*next_offset > dir_data_length) {
1331 return NT_STATUS_INFO_LENGTH_MISMATCH;
1334 if (*next_offset != 0) {
1335 /* Ensure we only read what in this record. */
1336 dir_data_length = *next_offset;
1339 if (dir_data_length < 105) {
1340 return NT_STATUS_INFO_LENGTH_MISMATCH;
1343 finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1344 finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1345 finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1346 finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1347 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1348 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1349 finfo->attr = IVAL(dir_data + 56, 0);
1350 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1351 namelen = IVAL(dir_data + 60,0);
1352 if (namelen > (dir_data_length - 104)) {
1353 return NT_STATUS_INFO_LENGTH_MISMATCH;
1355 finfo->reparse_tag = IVAL(dir_data + 64, 0);
1356 slen = CVAL(dir_data + 68, 0);
1358 return NT_STATUS_INFO_LENGTH_MISMATCH;
1360 ret = pull_string_talloc(finfo,
1362 FLAGS2_UNICODE_STRINGS,
1367 if (ret == (size_t)-1) {
1368 /* Bad conversion. */
1369 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1372 ret = pull_string_talloc(finfo,
1374 FLAGS2_UNICODE_STRINGS,
1379 if (ret == (size_t)-1) {
1380 /* Bad conversion. */
1381 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1384 if (finfo->name == NULL) {
1385 /* Bad conversion. */
1386 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1389 return NT_STATUS_OK;
1392 /*******************************************************************
1393 Given a filename - get its directory name
1394 ********************************************************************/
1396 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1404 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1407 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1418 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1421 (*parent)[len] = '\0';
1429 struct cli_smb2_list_dir_data {
1434 struct cli_smb2_list_state {
1435 struct tevent_context *ev;
1436 struct cli_state *cli;
1442 struct cli_smb2_list_dir_data *response;
1444 unsigned int info_level;
1447 static void cli_smb2_list_opened(struct tevent_req *subreq);
1448 static void cli_smb2_list_done(struct tevent_req *subreq);
1449 static void cli_smb2_list_closed(struct tevent_req *subreq);
1451 struct tevent_req *cli_smb2_list_send(
1452 TALLOC_CTX *mem_ctx,
1453 struct tevent_context *ev,
1454 struct cli_state *cli,
1455 const char *pathname,
1456 unsigned int info_level,
1459 struct tevent_req *req = NULL, *subreq = NULL;
1460 struct cli_smb2_list_state *state = NULL;
1461 char *parent = NULL;
1463 struct smb2_create_blobs *in_cblobs = NULL;
1465 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1471 state->status = NT_STATUS_OK;
1472 state->info_level = info_level;
1474 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1476 tevent_req_oom(req);
1477 return tevent_req_post(req, ev);
1480 if (smbXcli_conn_have_posix(cli->conn) && posix) {
1483 /* The mode MUST be 0 when opening an existing file/dir, and
1484 * will be ignored by the server.
1486 uint8_t linear_mode[4] = { 0 };
1487 DATA_BLOB blob = { .data=linear_mode,
1488 .length=sizeof(linear_mode) };
1490 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1491 if (in_cblobs == NULL) {
1495 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1496 SMB2_CREATE_TAG_POSIX, blob);
1497 if (tevent_req_nterror(req, status)) {
1498 tevent_req_nterror(req, status);
1499 return tevent_req_post(req, ev);
1503 subreq = cli_smb2_create_fnum_send(
1504 state, /* mem_ctx */
1508 (struct cli_smb2_create_flags){0}, /* create_flags */
1509 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1510 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1511 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1512 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1513 FILE_OPEN, /* create_disposition */
1514 FILE_DIRECTORY_FILE, /* create_options */
1515 in_cblobs); /* in_cblobs */
1516 TALLOC_FREE(in_cblobs);
1517 if (tevent_req_nomem(subreq, req)) {
1518 return tevent_req_post(req, ev);
1520 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1524 static void cli_smb2_list_opened(struct tevent_req *subreq)
1526 struct tevent_req *req = tevent_req_callback_data(
1527 subreq, struct tevent_req);
1528 struct cli_smb2_list_state *state = tevent_req_data(
1529 req, struct cli_smb2_list_state);
1532 status = cli_smb2_create_fnum_recv(
1533 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1534 TALLOC_FREE(subreq);
1535 if (tevent_req_nterror(req, status)) {
1540 * Make our caller get back to us via cli_smb2_list_recv(),
1541 * triggering the smb2_query_directory_send()
1543 tevent_req_defer_callback(req, state->ev);
1544 tevent_req_notify_callback(req);
1547 static void cli_smb2_list_done(struct tevent_req *subreq)
1549 struct tevent_req *req = tevent_req_callback_data(
1550 subreq, struct tevent_req);
1551 struct cli_smb2_list_state *state = tevent_req_data(
1552 req, struct cli_smb2_list_state);
1553 struct cli_smb2_list_dir_data *response = NULL;
1555 response = talloc(state, struct cli_smb2_list_dir_data);
1556 if (tevent_req_nomem(response, req)) {
1560 state->status = smb2cli_query_directory_recv(
1561 subreq, response, &response->data, &response->length);
1562 TALLOC_FREE(subreq);
1564 if (NT_STATUS_IS_OK(state->status)) {
1565 state->response = response;
1568 tevent_req_defer_callback(req, state->ev);
1569 tevent_req_notify_callback(req);
1573 TALLOC_FREE(response);
1575 subreq = cli_smb2_close_fnum_send(state,
1580 if (tevent_req_nomem(subreq, req)) {
1583 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1586 static void cli_smb2_list_closed(struct tevent_req *subreq)
1588 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1589 tevent_req_simple_finish_ntstatus(subreq, status);
1593 * Return the next finfo directory.
1595 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1596 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1597 * NT_STATUS_RETRY, which will then trigger the caller again when the
1598 * QUERY_DIRECTORY has returned with another buffer. This way we
1599 * guarantee that no asynchronous request is open after this call
1600 * returns an entry, so that other synchronous requests can be issued
1601 * on the same connection while the directory listing proceeds.
1603 NTSTATUS cli_smb2_list_recv(
1604 struct tevent_req *req,
1605 TALLOC_CTX *mem_ctx,
1606 struct file_info **pfinfo)
1608 struct cli_smb2_list_state *state = tevent_req_data(
1609 req, struct cli_smb2_list_state);
1610 struct cli_smb2_list_dir_data *response = NULL;
1611 struct file_info *finfo = NULL;
1613 uint32_t next_offset = 0;
1616 in_progress = tevent_req_is_in_progress(req);
1619 if (!tevent_req_is_nterror(req, &status)) {
1620 status = NT_STATUS_NO_MORE_FILES;
1625 response = state->response;
1626 if (response == NULL) {
1627 struct tevent_req *subreq = NULL;
1628 struct cli_state *cli = state->cli;
1629 struct smb2_hnd *ph = NULL;
1630 uint32_t max_trans, max_avail_len;
1633 if (!NT_STATUS_IS_OK(state->status)) {
1634 status = state->status;
1638 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1639 if (!NT_STATUS_IS_OK(status)) {
1643 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1644 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1646 max_trans = MIN(max_trans, max_avail_len);
1649 subreq = smb2cli_query_directory_send(
1650 state, /* mem_ctx */
1652 cli->conn, /* conn */
1653 cli->timeout, /* timeout_msec */
1654 cli->smb2.session, /* session */
1655 cli->smb2.tcon, /* tcon */
1656 state->info_level, /* level */
1659 ph->fid_persistent, /* fid_persistent */
1660 ph->fid_volatile, /* fid_volatile */
1661 state->mask, /* mask */
1662 max_trans); /* outbuf_len */
1663 if (subreq == NULL) {
1664 status = NT_STATUS_NO_MEMORY;
1667 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1668 return NT_STATUS_RETRY;
1671 SMB_ASSERT(response->length > state->offset);
1673 finfo = talloc_zero(mem_ctx, struct file_info);
1674 if (finfo == NULL) {
1675 status = NT_STATUS_NO_MEMORY;
1679 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1680 status = parse_finfo_posix_info(
1681 response->data + state->offset,
1682 response->length - state->offset,
1686 status = parse_finfo_id_both_directory_info(
1687 response->data + state->offset,
1688 response->length - state->offset,
1692 if (!NT_STATUS_IS_OK(status)) {
1696 status = is_bad_finfo_name(state->cli, finfo);
1697 if (!NT_STATUS_IS_OK(status)) {
1702 * parse_finfo_id_both_directory_info() checks for overflow,
1703 * no need to check again here.
1705 state->offset += next_offset;
1707 if (next_offset == 0) {
1708 TALLOC_FREE(state->response);
1711 tevent_req_defer_callback(req, state->ev);
1712 tevent_req_notify_callback(req);
1715 return NT_STATUS_OK;
1719 tevent_req_received(req);
1723 /***************************************************************
1724 Wrapper that allows SMB2 to query a path info (basic level).
1726 ***************************************************************/
1728 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1730 SMB_STRUCT_STAT *sbuf,
1731 uint32_t *attributes)
1734 struct smb_create_returns cr;
1735 uint16_t fnum = 0xffff;
1736 size_t namelen = strlen(name);
1738 if (smbXcli_conn_has_async_calls(cli->conn)) {
1740 * Can't use sync call while an async call is in flight
1742 return NT_STATUS_INVALID_PARAMETER;
1745 /* SMB2 is pickier about pathnames. Ensure it doesn't
1747 if (namelen > 0 && name[namelen-1] == '\\') {
1748 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1749 if (modname == NULL) {
1750 return NT_STATUS_NO_MEMORY;
1755 /* This is commonly used as a 'cd'. Try qpathinfo on
1756 a directory handle first. */
1758 status = cli_smb2_create_fnum(cli,
1760 (struct cli_smb2_create_flags){0},
1761 SMB2_IMPERSONATION_IMPERSONATION,
1762 FILE_READ_ATTRIBUTES, /* desired_access */
1763 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1764 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1765 FILE_OPEN, /* create_disposition */
1766 FILE_DIRECTORY_FILE, /* create_options */
1773 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1774 /* Maybe a file ? */
1775 status = cli_smb2_create_fnum(cli,
1777 (struct cli_smb2_create_flags){0},
1778 SMB2_IMPERSONATION_IMPERSONATION,
1779 FILE_READ_ATTRIBUTES, /* desired_access */
1780 0, /* file attributes */
1781 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1782 FILE_OPEN, /* create_disposition */
1783 0, /* create_options */
1791 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1792 /* Maybe a reparse point ? */
1793 status = cli_smb2_create_fnum(cli,
1795 (struct cli_smb2_create_flags){0},
1796 SMB2_IMPERSONATION_IMPERSONATION,
1797 FILE_READ_ATTRIBUTES, /* desired_access */
1798 0, /* file attributes */
1799 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1800 FILE_OPEN, /* create_disposition */
1801 FILE_OPEN_REPARSE_POINT, /* create_options */
1809 if (!NT_STATUS_IS_OK(status)) {
1813 status = cli_smb2_close_fnum(cli, fnum);
1817 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1818 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1819 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1820 sbuf->st_ex_size = cr.end_of_file;
1821 *attributes = cr.file_attributes;
1826 struct cli_smb2_query_info_fnum_state {
1830 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1832 struct tevent_req *cli_smb2_query_info_fnum_send(
1833 TALLOC_CTX *mem_ctx,
1834 struct tevent_context *ev,
1835 struct cli_state *cli,
1837 uint8_t in_info_type,
1838 uint8_t in_info_class,
1839 uint32_t in_max_output_length,
1840 const DATA_BLOB *in_input_buffer,
1841 uint32_t in_additional_info,
1844 struct tevent_req *req = NULL, *subreq = NULL;
1845 struct cli_smb2_query_info_fnum_state *state = NULL;
1846 struct smb2_hnd *ph = NULL;
1849 req = tevent_req_create(
1850 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1855 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1856 if (tevent_req_nterror(req, status)) {
1857 return tevent_req_post(req, ev);
1860 subreq = smb2cli_query_info_send(
1869 in_max_output_length,
1875 if (tevent_req_nomem(subreq, req)) {
1876 return tevent_req_post(req, ev);
1878 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1882 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1884 struct tevent_req *req = tevent_req_callback_data(
1885 subreq, struct tevent_req);
1886 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1887 req, struct cli_smb2_query_info_fnum_state);
1891 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1892 TALLOC_FREE(subreq);
1893 if (tevent_req_nterror(req, status)) {
1898 * We have to dup the memory here because outbuf.data is not
1899 * returned as a talloc object by smb2cli_query_info_recv.
1900 * It's a pointer into the received buffer.
1902 state->outbuf = data_blob_dup_talloc(state, outbuf);
1904 if ((outbuf.length != 0) &&
1905 tevent_req_nomem(state->outbuf.data, req)) {
1908 tevent_req_done(req);
1911 NTSTATUS cli_smb2_query_info_fnum_recv(
1912 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1914 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1915 req, struct cli_smb2_query_info_fnum_state);
1918 if (tevent_req_is_nterror(req, &status)) {
1921 *outbuf = (DATA_BLOB) {
1922 .data = talloc_move(mem_ctx, &state->outbuf.data),
1923 .length = state->outbuf.length,
1925 return NT_STATUS_OK;
1928 NTSTATUS cli_smb2_query_info_fnum(
1929 struct cli_state *cli,
1931 uint8_t in_info_type,
1932 uint8_t in_info_class,
1933 uint32_t in_max_output_length,
1934 const DATA_BLOB *in_input_buffer,
1935 uint32_t in_additional_info,
1937 TALLOC_CTX *mem_ctx,
1940 TALLOC_CTX *frame = talloc_stackframe();
1941 struct tevent_context *ev = NULL;
1942 struct tevent_req *req = NULL;
1943 NTSTATUS status = NT_STATUS_NO_MEMORY;
1946 if (smbXcli_conn_has_async_calls(cli->conn)) {
1948 * Can't use sync call while an async call is in flight
1950 status = NT_STATUS_INVALID_PARAMETER;
1953 ev = samba_tevent_context_init(frame);
1957 req = cli_smb2_query_info_fnum_send(
1964 in_max_output_length,
1971 ok = tevent_req_poll_ntstatus(req, ev, &status);
1975 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1981 /***************************************************************
1982 Helper function for pathname operations.
1983 ***************************************************************/
1985 struct get_fnum_from_path_state {
1986 struct tevent_context *ev;
1987 struct cli_state *cli;
1989 uint32_t desired_access;
1993 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1994 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1995 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1997 static struct tevent_req *get_fnum_from_path_send(
1998 TALLOC_CTX *mem_ctx,
1999 struct tevent_context *ev,
2000 struct cli_state *cli,
2002 uint32_t desired_access)
2004 struct tevent_req *req = NULL, *subreq = NULL;
2005 struct get_fnum_from_path_state *state = NULL;
2006 size_t namelen = strlen(name);
2008 req = tevent_req_create(
2009 mem_ctx, &state, struct get_fnum_from_path_state);
2016 state->desired_access = desired_access;
2019 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2022 if (namelen > 0 && name[namelen-1] == '\\') {
2023 state->name = talloc_strndup(state, name, namelen-1);
2024 if (tevent_req_nomem(state->name, req)) {
2025 return tevent_req_post(req, ev);
2029 subreq = cli_smb2_create_fnum_send(
2030 state, /* mem_ctx, */
2033 state->name, /* fname */
2034 (struct cli_smb2_create_flags){0}, /* create_flags */
2035 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2036 desired_access, /* desired_access */
2037 0, /* file_attributes */
2040 FILE_SHARE_DELETE, /* share_access */
2041 FILE_OPEN, /* create_disposition */
2042 0, /* create_options */
2043 NULL); /* in_cblobs */
2044 if (tevent_req_nomem(subreq, req)) {
2045 return tevent_req_post(req, ev);
2047 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2051 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2053 struct tevent_req *req = tevent_req_callback_data(
2054 subreq, struct tevent_req);
2055 struct get_fnum_from_path_state *state = tevent_req_data(
2056 req, struct get_fnum_from_path_state);
2059 status = cli_smb2_create_fnum_recv(
2060 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2061 TALLOC_FREE(subreq);
2063 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2064 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2066 * Naive option to match our SMB1 code. Assume the
2067 * symlink path that tripped us up was the last
2068 * component and try again. Eventually we will have to
2069 * deal with the returned path unprocessed component. JRA.
2071 subreq = cli_smb2_create_fnum_send(
2072 state, /* mem_ctx, */
2074 state->cli, /* cli */
2075 state->name, /* fname */
2076 (struct cli_smb2_create_flags){0}, /* create_flags */
2077 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2078 state->desired_access, /* desired_access */
2079 0, /* file_attributes */
2082 FILE_SHARE_DELETE, /* share_access */
2083 FILE_OPEN, /* create_disposition */
2084 FILE_OPEN_REPARSE_POINT, /* create_options */
2085 NULL); /* in_cblobs */
2086 if (tevent_req_nomem(subreq, req)) {
2089 tevent_req_set_callback(
2090 subreq, get_fnum_from_path_opened_reparse, req);
2094 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2095 subreq = cli_smb2_create_fnum_send(
2096 state, /* mem_ctx, */
2098 state->cli, /* cli */
2099 state->name, /* fname */
2100 (struct cli_smb2_create_flags){0}, /* create_flags */
2101 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2102 state->desired_access, /* desired_access */
2103 0, /* file_attributes */
2106 FILE_SHARE_DELETE, /* share_access */
2107 FILE_OPEN, /* create_disposition */
2108 FILE_DIRECTORY_FILE, /* create_options */
2109 NULL); /* in_cblobs */
2110 if (tevent_req_nomem(subreq, req)) {
2113 tevent_req_set_callback(
2114 subreq, get_fnum_from_path_opened_dir, req);
2118 if (tevent_req_nterror(req, status)) {
2121 tevent_req_done(req);
2124 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2126 struct tevent_req *req = tevent_req_callback_data(
2127 subreq, struct tevent_req);
2128 struct get_fnum_from_path_state *state = tevent_req_data(
2129 req, struct get_fnum_from_path_state);
2130 NTSTATUS status = cli_smb2_create_fnum_recv(
2131 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2132 tevent_req_simple_finish_ntstatus(subreq, status);
2135 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2137 /* Abstraction violation, but these two are just the same... */
2138 get_fnum_from_path_opened_reparse(subreq);
2141 static NTSTATUS get_fnum_from_path_recv(
2142 struct tevent_req *req, uint16_t *pfnum)
2144 struct get_fnum_from_path_state *state = tevent_req_data(
2145 req, struct get_fnum_from_path_state);
2146 NTSTATUS status = NT_STATUS_OK;
2148 if (!tevent_req_is_nterror(req, &status)) {
2149 *pfnum = state->fnum;
2151 tevent_req_received(req);
2155 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2157 uint32_t desired_access,
2160 TALLOC_CTX *frame = talloc_stackframe();
2161 struct tevent_context *ev = NULL;
2162 struct tevent_req *req = NULL;
2163 NTSTATUS status = NT_STATUS_NO_MEMORY;
2165 if (smbXcli_conn_has_async_calls(cli->conn)) {
2166 status = NT_STATUS_INVALID_PARAMETER;
2169 ev = samba_tevent_context_init(frame);
2173 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2177 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2180 status = get_fnum_from_path_recv(req, pfnum);
2186 struct cli_smb2_qpathinfo_state {
2187 struct tevent_context *ev;
2188 struct cli_state *cli;
2199 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2200 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2201 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2203 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2204 struct tevent_context *ev,
2205 struct cli_state *cli,
2211 struct tevent_req *req = NULL, *subreq = NULL;
2212 struct cli_smb2_qpathinfo_state *state = NULL;
2214 req = tevent_req_create(mem_ctx,
2216 struct cli_smb2_qpathinfo_state);
2222 state->level = level;
2223 state->min_rdata = min_rdata;
2224 state->max_rdata = max_rdata;
2226 subreq = get_fnum_from_path_send(state,
2230 FILE_READ_ATTRIBUTES);
2231 if (tevent_req_nomem(subreq, req)) {
2232 return tevent_req_post(req, ev);
2234 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2238 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2240 struct tevent_req *req =
2241 tevent_req_callback_data(subreq, struct tevent_req);
2242 struct cli_smb2_qpathinfo_state *state =
2243 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2246 status = get_fnum_from_path_recv(subreq, &state->fnum);
2247 TALLOC_FREE(subreq);
2248 if (tevent_req_nterror(req, status)) {
2252 subreq = cli_smb2_query_info_fnum_send(state,
2256 1, /* in_info_type */
2259 NULL, /* in_input_buffer */
2260 0, /* in_additional_info */
2262 if (tevent_req_nomem(subreq, req)) {
2265 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2268 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2270 struct tevent_req *req =
2271 tevent_req_callback_data(subreq, struct tevent_req);
2272 struct cli_smb2_qpathinfo_state *state =
2273 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2276 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2277 TALLOC_FREE(subreq);
2279 if (NT_STATUS_IS_OK(state->status) &&
2280 (state->out.length < state->min_rdata)) {
2281 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2284 subreq = cli_smb2_close_fnum_send(state,
2289 if (tevent_req_nomem(subreq, req)) {
2292 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2295 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2297 struct tevent_req *req =
2298 tevent_req_callback_data(subreq, struct tevent_req);
2299 struct cli_smb2_qpathinfo_state *state =
2300 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2303 status = cli_smb2_close_fnum_recv(subreq);
2304 TALLOC_FREE(subreq);
2305 if (tevent_req_nterror(req, status)) {
2308 if (tevent_req_nterror(req, state->status)) {
2311 tevent_req_done(req);
2314 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2315 TALLOC_CTX *mem_ctx,
2317 uint32_t *num_rdata)
2319 struct cli_smb2_qpathinfo_state *state =
2320 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2323 if (tevent_req_is_nterror(req, &status)) {
2327 *rdata = talloc_move(mem_ctx, &state->out.data);
2328 *num_rdata = state->out.length;
2329 tevent_req_received(req);
2330 return NT_STATUS_OK;
2333 /***************************************************************
2334 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2337 ***************************************************************/
2339 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2341 uint8_t in_info_type,
2342 uint8_t in_file_info_class,
2343 const DATA_BLOB *p_in_data)
2346 uint16_t fnum = 0xffff;
2347 TALLOC_CTX *frame = talloc_stackframe();
2349 if (smbXcli_conn_has_async_calls(cli->conn)) {
2351 * Can't use sync call while an async call is in flight
2353 status = NT_STATUS_INVALID_PARAMETER;
2357 status = get_fnum_from_path(cli,
2359 FILE_WRITE_ATTRIBUTES,
2362 if (!NT_STATUS_IS_OK(status)) {
2366 status = cli_smb2_set_info_fnum(
2371 p_in_data, /* in_input_buffer */
2372 0); /* in_additional_info */
2375 if (fnum != 0xffff) {
2376 cli_smb2_close_fnum(cli, fnum);
2379 cli->raw_status = status;
2386 /***************************************************************
2387 Wrapper that allows SMB2 to set pathname attributes.
2389 ***************************************************************/
2391 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2396 uint8_t inbuf_store[40];
2397 DATA_BLOB inbuf = data_blob_null;
2399 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2400 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2402 inbuf.data = inbuf_store;
2403 inbuf.length = sizeof(inbuf_store);
2404 data_blob_clear(&inbuf);
2407 * SMB1 uses attr == 0 to clear all attributes
2408 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2409 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2410 * request attribute change.
2412 * SMB2 uses exactly the reverse. Unfortunately as the
2413 * cli_setatr() ABI is exposed inside libsmbclient,
2414 * we must make the SMB2 cli_smb2_setatr() call
2415 * export the same ABI as the SMB1 cli_setatr()
2416 * which calls it. This means reversing the sense
2417 * of the requested attr argument if it's zero
2418 * or FILE_ATTRIBUTE_NORMAL.
2420 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2424 attr = FILE_ATTRIBUTE_NORMAL;
2425 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2429 SIVAL(inbuf.data, 32, attr);
2431 put_long_date((char *)inbuf.data + 16,mtime);
2433 /* Set all the other times to -1. */
2434 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2435 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2436 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2438 return cli_smb2_setpathinfo(cli,
2440 1, /* in_info_type */
2441 /* in_file_info_class */
2442 SMB_FILE_BASIC_INFORMATION - 1000,
2447 /***************************************************************
2448 Wrapper that allows SMB2 to set file handle times.
2450 ***************************************************************/
2452 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2458 uint8_t inbuf_store[40];
2459 DATA_BLOB inbuf = data_blob_null;
2462 if (smbXcli_conn_has_async_calls(cli->conn)) {
2464 * Can't use sync call while an async call is in flight
2466 return NT_STATUS_INVALID_PARAMETER;
2469 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2470 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2472 inbuf.data = inbuf_store;
2473 inbuf.length = sizeof(inbuf_store);
2474 data_blob_clear(&inbuf);
2476 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2477 if (change_time != 0) {
2478 put_long_date((char *)inbuf.data + 24, change_time);
2480 if (access_time != 0) {
2481 put_long_date((char *)inbuf.data + 8, access_time);
2483 if (write_time != 0) {
2484 put_long_date((char *)inbuf.data + 16, write_time);
2487 status = cli_smb2_set_info_fnum(cli,
2489 1, /* in_info_type */
2490 SMB_FILE_BASIC_INFORMATION -
2491 1000, /* in_file_info_class */
2492 &inbuf, /* in_input_buffer */
2493 0); /* in_additional_info */
2494 cli->raw_status = status;
2498 /***************************************************************
2499 Wrapper that allows SMB2 to query disk attributes (size).
2501 ***************************************************************/
2503 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2504 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2507 uint16_t fnum = 0xffff;
2508 DATA_BLOB outbuf = data_blob_null;
2509 uint32_t sectors_per_unit = 0;
2510 uint32_t bytes_per_sector = 0;
2511 uint64_t total_size = 0;
2512 uint64_t size_free = 0;
2513 TALLOC_CTX *frame = talloc_stackframe();
2515 if (smbXcli_conn_has_async_calls(cli->conn)) {
2517 * Can't use sync call while an async call is in flight
2519 status = NT_STATUS_INVALID_PARAMETER;
2523 /* First open the top level directory. */
2524 status = cli_smb2_create_fnum(cli,
2526 (struct cli_smb2_create_flags){0},
2527 SMB2_IMPERSONATION_IMPERSONATION,
2528 FILE_READ_ATTRIBUTES, /* desired_access */
2529 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2530 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2531 FILE_OPEN, /* create_disposition */
2532 FILE_DIRECTORY_FILE, /* create_options */
2539 if (!NT_STATUS_IS_OK(status)) {
2543 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2544 level 3 (SMB_FS_SIZE_INFORMATION). */
2546 status = cli_smb2_query_info_fnum(
2549 2, /* in_info_type */
2550 3, /* in_file_info_class */
2551 0xFFFF, /* in_max_output_length */
2552 NULL, /* in_input_buffer */
2553 0, /* in_additional_info */
2557 if (!NT_STATUS_IS_OK(status)) {
2561 /* Parse the reply. */
2562 if (outbuf.length != 24) {
2563 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2567 total_size = BVAL(outbuf.data, 0);
2568 size_free = BVAL(outbuf.data, 8);
2569 sectors_per_unit = IVAL(outbuf.data, 16);
2570 bytes_per_sector = IVAL(outbuf.data, 20);
2573 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2576 *total = total_size;
2582 status = NT_STATUS_OK;
2586 if (fnum != 0xffff) {
2587 cli_smb2_close_fnum(cli, fnum);
2590 cli->raw_status = status;
2596 /***************************************************************
2597 Wrapper that allows SMB2 to query file system sizes.
2599 ***************************************************************/
2601 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2602 uint64_t *total_allocation_units,
2603 uint64_t *caller_allocation_units,
2604 uint64_t *actual_allocation_units,
2605 uint64_t *sectors_per_allocation_unit,
2606 uint64_t *bytes_per_sector)
2609 uint16_t fnum = 0xffff;
2610 DATA_BLOB outbuf = data_blob_null;
2611 TALLOC_CTX *frame = talloc_stackframe();
2613 if (smbXcli_conn_has_async_calls(cli->conn)) {
2615 * Can't use sync call while an async call is in flight
2617 status = NT_STATUS_INVALID_PARAMETER;
2621 /* First open the top level directory. */
2623 cli_smb2_create_fnum(cli, "",
2624 (struct cli_smb2_create_flags){0},
2625 SMB2_IMPERSONATION_IMPERSONATION,
2626 FILE_READ_ATTRIBUTES, /* desired_access */
2627 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2628 FILE_SHARE_READ | FILE_SHARE_WRITE |
2629 FILE_SHARE_DELETE, /* share_access */
2630 FILE_OPEN, /* create_disposition */
2631 FILE_DIRECTORY_FILE, /* create_options */
2638 if (!NT_STATUS_IS_OK(status)) {
2642 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2643 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2645 status = cli_smb2_query_info_fnum(
2648 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2649 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2650 0xFFFF, /* in_max_output_length */
2651 NULL, /* in_input_buffer */
2652 0, /* in_additional_info */
2656 if (!NT_STATUS_IS_OK(status)) {
2660 if (outbuf.length < 32) {
2661 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2665 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2666 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2667 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2668 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2669 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2673 if (fnum != 0xffff) {
2674 cli_smb2_close_fnum(cli, fnum);
2677 cli->raw_status = status;
2683 /***************************************************************
2684 Wrapper that allows SMB2 to query file system attributes.
2686 ***************************************************************/
2688 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2691 uint16_t fnum = 0xffff;
2692 DATA_BLOB outbuf = data_blob_null;
2693 TALLOC_CTX *frame = talloc_stackframe();
2695 if (smbXcli_conn_has_async_calls(cli->conn)) {
2697 * Can't use sync call while an async call is in flight
2699 status = NT_STATUS_INVALID_PARAMETER;
2703 /* First open the top level directory. */
2705 cli_smb2_create_fnum(cli, "",
2706 (struct cli_smb2_create_flags){0},
2707 SMB2_IMPERSONATION_IMPERSONATION,
2708 FILE_READ_ATTRIBUTES, /* desired_access */
2709 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2710 FILE_SHARE_READ | FILE_SHARE_WRITE |
2711 FILE_SHARE_DELETE, /* share_access */
2712 FILE_OPEN, /* create_disposition */
2713 FILE_DIRECTORY_FILE, /* create_options */
2720 if (!NT_STATUS_IS_OK(status)) {
2724 status = cli_smb2_query_info_fnum(
2727 2, /* in_info_type */
2728 5, /* in_file_info_class */
2729 0xFFFF, /* in_max_output_length */
2730 NULL, /* in_input_buffer */
2731 0, /* in_additional_info */
2735 if (!NT_STATUS_IS_OK(status)) {
2739 if (outbuf.length < 12) {
2740 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2744 *fs_attr = IVAL(outbuf.data, 0);
2748 if (fnum != 0xffff) {
2749 cli_smb2_close_fnum(cli, fnum);
2752 cli->raw_status = status;
2758 /***************************************************************
2759 Wrapper that allows SMB2 to query file system volume info.
2761 ***************************************************************/
2763 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2764 TALLOC_CTX *mem_ctx,
2765 char **_volume_name,
2766 uint32_t *pserial_number,
2770 uint16_t fnum = 0xffff;
2771 DATA_BLOB outbuf = data_blob_null;
2773 char *volume_name = NULL;
2774 TALLOC_CTX *frame = talloc_stackframe();
2776 if (smbXcli_conn_has_async_calls(cli->conn)) {
2778 * Can't use sync call while an async call is in flight
2780 status = NT_STATUS_INVALID_PARAMETER;
2784 /* First open the top level directory. */
2786 cli_smb2_create_fnum(cli, "",
2787 (struct cli_smb2_create_flags){0},
2788 SMB2_IMPERSONATION_IMPERSONATION,
2789 FILE_READ_ATTRIBUTES, /* desired_access */
2790 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2791 FILE_SHARE_READ | FILE_SHARE_WRITE |
2792 FILE_SHARE_DELETE, /* share_access */
2793 FILE_OPEN, /* create_disposition */
2794 FILE_DIRECTORY_FILE, /* create_options */
2801 if (!NT_STATUS_IS_OK(status)) {
2805 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2806 level 1 (SMB_FS_VOLUME_INFORMATION). */
2808 status = cli_smb2_query_info_fnum(
2811 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2812 /* in_file_info_class */
2813 SMB_FS_VOLUME_INFORMATION - 1000,
2814 0xFFFF, /* in_max_output_length */
2815 NULL, /* in_input_buffer */
2816 0, /* in_additional_info */
2820 if (!NT_STATUS_IS_OK(status)) {
2824 if (outbuf.length < 24) {
2825 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2831 ts = interpret_long_date(BVAL(outbuf.data, 0));
2834 if (pserial_number) {
2835 *pserial_number = IVAL(outbuf.data,8);
2837 nlen = IVAL(outbuf.data,12);
2838 if (nlen + 18 < 18) {
2840 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2844 * The next check is safe as we know outbuf.length >= 24
2847 if (nlen > (outbuf.length - 18)) {
2848 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2852 pull_string_talloc(mem_ctx,
2853 (const char *)outbuf.data,
2859 if (volume_name == NULL) {
2860 status = map_nt_error_from_unix(errno);
2864 *_volume_name = volume_name;
2868 if (fnum != 0xffff) {
2869 cli_smb2_close_fnum(cli, fnum);
2872 cli->raw_status = status;
2878 struct cli_smb2_mxac_state {
2879 struct tevent_context *ev;
2880 struct cli_state *cli;
2882 struct smb2_create_blobs in_cblobs;
2888 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2889 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2891 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2892 struct tevent_context *ev,
2893 struct cli_state *cli,
2896 struct tevent_req *req = NULL, *subreq = NULL;
2897 struct cli_smb2_mxac_state *state = NULL;
2900 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2904 *state = (struct cli_smb2_mxac_state) {
2910 status = smb2_create_blob_add(state,
2912 SMB2_CREATE_TAG_MXAC,
2913 data_blob(NULL, 0));
2914 if (tevent_req_nterror(req, status)) {
2915 return tevent_req_post(req, ev);
2918 subreq = cli_smb2_create_fnum_send(
2923 (struct cli_smb2_create_flags){0},
2924 SMB2_IMPERSONATION_IMPERSONATION,
2925 FILE_READ_ATTRIBUTES,
2926 0, /* file attributes */
2927 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2929 0, /* create_options */
2931 if (tevent_req_nomem(subreq, req)) {
2932 return tevent_req_post(req, ev);
2934 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2938 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2940 struct tevent_req *req = tevent_req_callback_data(
2941 subreq, struct tevent_req);
2942 struct cli_smb2_mxac_state *state = tevent_req_data(
2943 req, struct cli_smb2_mxac_state);
2944 struct smb2_create_blobs out_cblobs = {0};
2945 struct smb2_create_blob *mxac_blob = NULL;
2948 status = cli_smb2_create_fnum_recv(
2949 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
2950 TALLOC_FREE(subreq);
2952 if (tevent_req_nterror(req, status)) {
2956 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
2957 if (mxac_blob == NULL) {
2958 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2961 if (mxac_blob->data.length != 8) {
2962 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2966 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
2967 state->mxac = IVAL(mxac_blob->data.data, 4);
2970 subreq = cli_smb2_close_fnum_send(state,
2975 if (tevent_req_nomem(subreq, req)) {
2978 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
2983 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
2985 struct tevent_req *req = tevent_req_callback_data(
2986 subreq, struct tevent_req);
2989 status = cli_smb2_close_fnum_recv(subreq);
2990 if (tevent_req_nterror(req, status)) {
2994 tevent_req_done(req);
2997 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
2999 struct cli_smb2_mxac_state *state = tevent_req_data(
3000 req, struct cli_smb2_mxac_state);
3003 if (tevent_req_is_nterror(req, &status)) {
3007 if (!NT_STATUS_IS_OK(state->status)) {
3008 return state->status;
3011 *mxac = state->mxac;
3012 return NT_STATUS_OK;
3015 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3019 TALLOC_CTX *frame = talloc_stackframe();
3020 struct tevent_context *ev = NULL;
3021 struct tevent_req *req = NULL;
3022 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3025 if (smbXcli_conn_has_async_calls(cli->conn)) {
3027 * Can't use sync call while an async call is in flight
3029 status = NT_STATUS_INVALID_PARAMETER;
3033 ev = samba_tevent_context_init(frame);
3037 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3041 ok = tevent_req_poll_ntstatus(req, ev, &status);
3045 status = cli_smb2_query_mxac_recv(req, _mxac);
3048 cli->raw_status = status;
3053 struct cli_smb2_rename_fnum_state {
3057 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3059 static struct tevent_req *cli_smb2_rename_fnum_send(
3060 TALLOC_CTX *mem_ctx,
3061 struct tevent_context *ev,
3062 struct cli_state *cli,
3064 const char *fname_dst,
3067 struct tevent_req *req = NULL, *subreq = NULL;
3068 struct cli_smb2_rename_fnum_state *state = NULL;
3069 size_t namelen = strlen(fname_dst);
3070 smb_ucs2_t *converted_str = NULL;
3071 size_t converted_size_bytes = 0;
3075 req = tevent_req_create(
3076 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3082 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3085 if (*fname_dst == '\\') {
3090 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3093 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3094 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3095 if (tevent_req_nomem(fname_dst, req)) {
3096 return tevent_req_post(req, ev);
3100 ok = push_ucs2_talloc(
3101 state, &converted_str, fname_dst, &converted_size_bytes);
3103 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3104 return tevent_req_post(req, ev);
3108 * W2K8 insists the dest name is not null terminated. Remove
3109 * the last 2 zero bytes and reduce the name length.
3111 if (converted_size_bytes < 2) {
3112 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3113 return tevent_req_post(req, ev);
3115 converted_size_bytes -= 2;
3117 inbuf_size = 20 + converted_size_bytes;
3118 if (inbuf_size < 20) {
3119 /* Integer wrap check. */
3120 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3121 return tevent_req_post(req, ev);
3125 * The Windows 10 SMB2 server has a minimum length
3126 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3127 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3128 * if the length is less. This isn't an alignment
3129 * issue as Windows client accepts happily 2-byte align
3130 * for larger target name sizes. Also the Windows 10
3131 * SMB1 server doesn't have this restriction.
3133 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3135 inbuf_size = MAX(inbuf_size, 24);
3137 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3138 if (tevent_req_nomem(state->inbuf.data, req)) {
3139 return tevent_req_post(req, ev);
3143 SCVAL(state->inbuf.data, 0, 1);
3146 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3147 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3149 TALLOC_FREE(converted_str);
3151 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3152 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3154 subreq = cli_smb2_set_info_fnum_send(
3155 state, /* mem_ctx */
3159 1, /* in_info_type */
3160 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3161 &state->inbuf, /* in_input_buffer */
3162 0); /* in_additional_info */
3163 if (tevent_req_nomem(subreq, req)) {
3164 return tevent_req_post(req, ev);
3166 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3170 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3172 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3173 tevent_req_simple_finish_ntstatus(subreq, status);
3176 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3178 return tevent_req_simple_recv_ntstatus(req);
3181 /***************************************************************
3182 Wrapper that allows SMB2 to rename a file.
3183 ***************************************************************/
3185 struct cli_smb2_rename_state {
3186 struct tevent_context *ev;
3187 struct cli_state *cli;
3188 const char *fname_dst;
3192 NTSTATUS rename_status;
3195 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3196 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3197 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3199 struct tevent_req *cli_smb2_rename_send(
3200 TALLOC_CTX *mem_ctx,
3201 struct tevent_context *ev,
3202 struct cli_state *cli,
3203 const char *fname_src,
3204 const char *fname_dst,
3207 struct tevent_req *req = NULL, *subreq = NULL;
3208 struct cli_smb2_rename_state *state = NULL;
3211 req = tevent_req_create(
3212 mem_ctx, &state, struct cli_smb2_rename_state);
3218 * Strip a MSDFS path from fname_dst if we were given one.
3220 status = cli_dfs_target_check(state,
3224 if (tevent_req_nterror(req, status)) {
3225 return tevent_req_post(req, ev);
3230 state->fname_dst = fname_dst;
3231 state->replace = replace;
3233 subreq = get_fnum_from_path_send(
3234 state, ev, cli, fname_src, DELETE_ACCESS);
3235 if (tevent_req_nomem(subreq, req)) {
3236 return tevent_req_post(req, ev);
3238 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3242 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3244 struct tevent_req *req = tevent_req_callback_data(
3245 subreq, struct tevent_req);
3246 struct cli_smb2_rename_state *state = tevent_req_data(
3247 req, struct cli_smb2_rename_state);
3250 status = get_fnum_from_path_recv(subreq, &state->fnum);
3251 TALLOC_FREE(subreq);
3252 if (tevent_req_nterror(req, status)) {
3256 subreq = cli_smb2_rename_fnum_send(
3263 if (tevent_req_nomem(subreq, req)) {
3266 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3269 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3271 struct tevent_req *req = tevent_req_callback_data(
3272 subreq, struct tevent_req);
3273 struct cli_smb2_rename_state *state = tevent_req_data(
3274 req, struct cli_smb2_rename_state);
3276 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3277 TALLOC_FREE(subreq);
3279 subreq = cli_smb2_close_fnum_send(state,
3284 if (tevent_req_nomem(subreq, req)) {
3287 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3290 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3292 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3293 tevent_req_simple_finish_ntstatus(subreq, status);
3296 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3298 struct cli_smb2_rename_state *state = tevent_req_data(
3299 req, struct cli_smb2_rename_state);
3300 NTSTATUS status = NT_STATUS_OK;
3302 if (!tevent_req_is_nterror(req, &status)) {
3303 status = state->rename_status;
3305 tevent_req_received(req);
3309 /***************************************************************
3310 Wrapper that allows SMB2 to set an EA on a fnum.
3312 ***************************************************************/
3314 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3316 const char *ea_name,
3321 DATA_BLOB inbuf = data_blob_null;
3323 char *ea_name_ascii = NULL;
3325 TALLOC_CTX *frame = talloc_stackframe();
3327 if (smbXcli_conn_has_async_calls(cli->conn)) {
3329 * Can't use sync call while an async call is in flight
3331 status = NT_STATUS_INVALID_PARAMETER;
3335 /* Marshall the SMB2 EA data. */
3336 if (ea_len > 0xFFFF) {
3337 status = NT_STATUS_INVALID_PARAMETER;
3341 if (!push_ascii_talloc(frame,
3345 status = NT_STATUS_INVALID_PARAMETER;
3349 if (namelen < 2 || namelen > 0xFF) {
3350 status = NT_STATUS_INVALID_PARAMETER;
3354 bloblen = 8 + ea_len + namelen;
3355 /* Round up to a 4 byte boundary. */
3356 bloblen = ((bloblen + 3)&~3);
3358 inbuf = data_blob_talloc_zero(frame, bloblen);
3359 if (inbuf.data == NULL) {
3360 status = NT_STATUS_NO_MEMORY;
3363 /* namelen doesn't include the NULL byte. */
3364 SCVAL(inbuf.data, 5, namelen - 1);
3365 SSVAL(inbuf.data, 6, ea_len);
3366 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3367 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3369 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3370 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3372 status = cli_smb2_set_info_fnum(
3375 1, /* in_info_type */
3376 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3377 &inbuf, /* in_input_buffer */
3378 0); /* in_additional_info */
3382 cli->raw_status = status;
3388 /***************************************************************
3389 Wrapper that allows SMB2 to set an EA on a pathname.
3391 ***************************************************************/
3393 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3395 const char *ea_name,
3400 uint16_t fnum = 0xffff;
3402 if (smbXcli_conn_has_async_calls(cli->conn)) {
3404 * Can't use sync call while an async call is in flight
3406 status = NT_STATUS_INVALID_PARAMETER;
3410 status = get_fnum_from_path(cli,
3415 if (!NT_STATUS_IS_OK(status)) {
3419 status = cli_set_ea_fnum(cli,
3424 if (!NT_STATUS_IS_OK(status)) {
3430 if (fnum != 0xffff) {
3431 cli_smb2_close_fnum(cli, fnum);
3434 cli->raw_status = status;
3439 /***************************************************************
3440 Wrapper that allows SMB2 to get an EA list on a pathname.
3442 ***************************************************************/
3444 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3448 struct ea_struct **pea_array)
3451 uint16_t fnum = 0xffff;
3452 DATA_BLOB outbuf = data_blob_null;
3453 struct ea_list *ea_list = NULL;
3454 struct ea_list *eal = NULL;
3455 size_t ea_count = 0;
3456 TALLOC_CTX *frame = talloc_stackframe();
3461 if (smbXcli_conn_has_async_calls(cli->conn)) {
3463 * Can't use sync call while an async call is in flight
3465 status = NT_STATUS_INVALID_PARAMETER;
3469 status = get_fnum_from_path(cli,
3474 if (!NT_STATUS_IS_OK(status)) {
3478 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3479 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3481 status = cli_smb2_query_info_fnum(
3484 1, /* in_info_type */
3485 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3486 0xFFFF, /* in_max_output_length */
3487 NULL, /* in_input_buffer */
3488 0, /* in_additional_info */
3493 if (!NT_STATUS_IS_OK(status)) {
3497 /* Parse the reply. */
3498 ea_list = read_nttrans_ea_list(ctx,
3499 (const char *)outbuf.data,
3501 if (ea_list == NULL) {
3502 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3506 /* Convert to an array. */
3507 for (eal = ea_list; eal; eal = eal->next) {
3512 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3513 if (*pea_array == NULL) {
3514 status = NT_STATUS_NO_MEMORY;
3518 for (eal = ea_list; eal; eal = eal->next) {
3519 (*pea_array)[ea_count++] = eal->ea;
3521 *pnum_eas = ea_count;
3526 if (fnum != 0xffff) {
3527 cli_smb2_close_fnum(cli, fnum);
3530 cli->raw_status = status;
3536 /***************************************************************
3537 Wrapper that allows SMB2 to get user quota.
3539 ***************************************************************/
3541 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3543 SMB_NTQUOTA_STRUCT *pqt)
3546 DATA_BLOB inbuf = data_blob_null;
3547 DATA_BLOB info_blob = data_blob_null;
3548 DATA_BLOB outbuf = data_blob_null;
3549 TALLOC_CTX *frame = talloc_stackframe();
3551 unsigned int offset;
3552 struct smb2_query_quota_info query = {0};
3553 struct file_get_quota_info info = {0};
3554 enum ndr_err_code err;
3555 struct ndr_push *ndr_push = NULL;
3557 if (smbXcli_conn_has_async_calls(cli->conn)) {
3559 * Can't use sync call while an async call is in flight
3561 status = NT_STATUS_INVALID_PARAMETER;
3565 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3567 query.return_single = 1;
3569 info.next_entry_offset = 0;
3570 info.sid_length = sid_len;
3571 info.sid = pqt->sid;
3573 err = ndr_push_struct_blob(
3577 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3579 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3580 status = NT_STATUS_INTERNAL_ERROR;
3584 query.sid_list_length = info_blob.length;
3585 ndr_push = ndr_push_init_ctx(frame);
3587 status = NT_STATUS_NO_MEMORY;
3591 err = ndr_push_smb2_query_quota_info(ndr_push,
3592 NDR_SCALARS | NDR_BUFFERS,
3595 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3596 status = NT_STATUS_INTERNAL_ERROR;
3600 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3603 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3604 status = NT_STATUS_INTERNAL_ERROR;
3607 inbuf.data = ndr_push->data;
3608 inbuf.length = ndr_push->offset;
3610 status = cli_smb2_query_info_fnum(
3613 4, /* in_info_type */
3614 0, /* in_file_info_class */
3615 0xFFFF, /* in_max_output_length */
3616 &inbuf, /* in_input_buffer */
3617 0, /* in_additional_info */
3622 if (!NT_STATUS_IS_OK(status)) {
3626 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3628 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3629 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3633 cli->raw_status = status;
3639 /***************************************************************
3640 Wrapper that allows SMB2 to list user quota.
3642 ***************************************************************/
3644 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3645 TALLOC_CTX *mem_ctx,
3647 SMB_NTQUOTA_LIST **pqt_list,
3651 DATA_BLOB inbuf = data_blob_null;
3652 DATA_BLOB outbuf = data_blob_null;
3653 TALLOC_CTX *frame = talloc_stackframe();
3654 struct smb2_query_quota_info info = {0};
3655 enum ndr_err_code err;
3657 if (smbXcli_conn_has_async_calls(cli->conn)) {
3659 * Can't use sync call while an async call is in flight
3661 status = NT_STATUS_INVALID_PARAMETER;
3665 info.restart_scan = first ? 1 : 0;
3667 err = ndr_push_struct_blob(
3671 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3673 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3674 status = NT_STATUS_INTERNAL_ERROR;
3678 status = cli_smb2_query_info_fnum(
3681 4, /* in_info_type */
3682 0, /* in_file_info_class */
3683 0xFFFF, /* in_max_output_length */
3684 &inbuf, /* in_input_buffer */
3685 0, /* in_additional_info */
3691 * safeguard against panic from calling parse_user_quota_list with
3694 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3695 status = NT_STATUS_NO_MORE_ENTRIES;
3698 if (!NT_STATUS_IS_OK(status)) {
3702 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3706 cli->raw_status = status;
3712 /***************************************************************
3713 Wrapper that allows SMB2 to get file system quota.
3715 ***************************************************************/
3717 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3719 SMB_NTQUOTA_STRUCT *pqt)
3722 DATA_BLOB outbuf = data_blob_null;
3723 TALLOC_CTX *frame = talloc_stackframe();
3725 if (smbXcli_conn_has_async_calls(cli->conn)) {
3727 * Can't use sync call while an async call is in flight
3729 status = NT_STATUS_INVALID_PARAMETER;
3733 status = cli_smb2_query_info_fnum(
3736 2, /* in_info_type */
3737 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3738 0xFFFF, /* in_max_output_length */
3739 NULL, /* in_input_buffer */
3740 0, /* in_additional_info */
3745 if (!NT_STATUS_IS_OK(status)) {
3749 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3752 cli->raw_status = status;
3758 /***************************************************************
3759 Wrapper that allows SMB2 to set user quota.
3761 ***************************************************************/
3763 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3765 SMB_NTQUOTA_LIST *qtl)
3768 DATA_BLOB inbuf = data_blob_null;
3769 TALLOC_CTX *frame = talloc_stackframe();
3771 if (smbXcli_conn_has_async_calls(cli->conn)) {
3773 * Can't use sync call while an async call is in flight
3775 status = NT_STATUS_INVALID_PARAMETER;
3779 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3780 if (!NT_STATUS_IS_OK(status)) {
3784 status = cli_smb2_set_info_fnum(
3787 4, /* in_info_type */
3788 0, /* in_file_info_class */
3789 &inbuf, /* in_input_buffer */
3790 0); /* in_additional_info */
3793 cli->raw_status = status;
3800 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3802 SMB_NTQUOTA_STRUCT *pqt)
3805 DATA_BLOB inbuf = data_blob_null;
3806 TALLOC_CTX *frame = talloc_stackframe();
3808 if (smbXcli_conn_has_async_calls(cli->conn)) {
3810 * Can't use sync call while an async call is in flight
3812 status = NT_STATUS_INVALID_PARAMETER;
3816 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3817 if (!NT_STATUS_IS_OK(status)) {
3821 status = cli_smb2_set_info_fnum(
3824 2, /* in_info_type */
3825 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3826 &inbuf, /* in_input_buffer */
3827 0); /* in_additional_info */
3829 cli->raw_status = status;
3835 struct cli_smb2_read_state {
3836 struct tevent_context *ev;
3837 struct cli_state *cli;
3838 struct smb2_hnd *ph;
3839 uint64_t start_offset;
3845 static void cli_smb2_read_done(struct tevent_req *subreq);
3847 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3848 struct tevent_context *ev,
3849 struct cli_state *cli,
3855 struct tevent_req *req, *subreq;
3856 struct cli_smb2_read_state *state;
3858 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3864 state->start_offset = (uint64_t)offset;
3865 state->size = (uint32_t)size;
3866 state->received = 0;
3869 status = map_fnum_to_smb2_handle(cli,
3872 if (tevent_req_nterror(req, status)) {
3873 return tevent_req_post(req, ev);
3876 subreq = smb2cli_read_send(state,
3879 state->cli->timeout,
3880 state->cli->smb2.session,
3881 state->cli->smb2.tcon,
3883 state->start_offset,
3884 state->ph->fid_persistent,
3885 state->ph->fid_volatile,
3886 0, /* minimum_count */
3887 0); /* remaining_bytes */
3889 if (tevent_req_nomem(subreq, req)) {
3890 return tevent_req_post(req, ev);
3892 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3896 static void cli_smb2_read_done(struct tevent_req *subreq)
3898 struct tevent_req *req = tevent_req_callback_data(
3899 subreq, struct tevent_req);
3900 struct cli_smb2_read_state *state = tevent_req_data(
3901 req, struct cli_smb2_read_state);
3904 status = smb2cli_read_recv(subreq, state,
3905 &state->buf, &state->received);
3906 if (tevent_req_nterror(req, status)) {
3910 if (state->received > state->size) {
3911 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3915 tevent_req_done(req);
3918 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3923 struct cli_smb2_read_state *state = tevent_req_data(
3924 req, struct cli_smb2_read_state);
3926 if (tevent_req_is_nterror(req, &status)) {
3927 state->cli->raw_status = status;
3931 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3932 * better make sure that you copy it away before you talloc_free(req).
3933 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3935 *received = (ssize_t)state->received;
3936 *rcvbuf = state->buf;
3937 state->cli->raw_status = NT_STATUS_OK;
3938 return NT_STATUS_OK;
3941 struct cli_smb2_write_state {
3942 struct tevent_context *ev;
3943 struct cli_state *cli;
3944 struct smb2_hnd *ph;
3952 static void cli_smb2_write_written(struct tevent_req *req);
3954 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3955 struct tevent_context *ev,
3956 struct cli_state *cli,
3964 struct tevent_req *req, *subreq = NULL;
3965 struct cli_smb2_write_state *state = NULL;
3967 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3973 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
3974 state->flags = (uint32_t)mode;
3976 state->offset = (uint64_t)offset;
3977 state->size = (uint32_t)size;
3980 status = map_fnum_to_smb2_handle(cli,
3983 if (tevent_req_nterror(req, status)) {
3984 return tevent_req_post(req, ev);
3987 subreq = smb2cli_write_send(state,
3990 state->cli->timeout,
3991 state->cli->smb2.session,
3992 state->cli->smb2.tcon,
3995 state->ph->fid_persistent,
3996 state->ph->fid_volatile,
3997 0, /* remaining_bytes */
3998 state->flags, /* flags */
4001 if (tevent_req_nomem(subreq, req)) {
4002 return tevent_req_post(req, ev);
4004 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4008 static void cli_smb2_write_written(struct tevent_req *subreq)
4010 struct tevent_req *req = tevent_req_callback_data(
4011 subreq, struct tevent_req);
4012 struct cli_smb2_write_state *state = tevent_req_data(
4013 req, struct cli_smb2_write_state);
4017 status = smb2cli_write_recv(subreq, &written);
4018 TALLOC_FREE(subreq);
4019 if (tevent_req_nterror(req, status)) {
4023 state->written = written;
4025 tevent_req_done(req);
4028 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4031 struct cli_smb2_write_state *state = tevent_req_data(
4032 req, struct cli_smb2_write_state);
4035 if (tevent_req_is_nterror(req, &status)) {
4036 state->cli->raw_status = status;
4037 tevent_req_received(req);
4041 if (pwritten != NULL) {
4042 *pwritten = (size_t)state->written;
4044 state->cli->raw_status = NT_STATUS_OK;
4045 tevent_req_received(req);
4046 return NT_STATUS_OK;
4049 /***************************************************************
4050 Wrapper that allows SMB2 async write using an fnum.
4051 This is mostly cut-and-paste from Volker's code inside
4052 source3/libsmb/clireadwrite.c, adapted for SMB2.
4054 Done this way so I can reuse all the logic inside cli_push()
4056 ***************************************************************/
4058 struct cli_smb2_writeall_state {
4059 struct tevent_context *ev;
4060 struct cli_state *cli;
4061 struct smb2_hnd *ph;
4069 static void cli_smb2_writeall_written(struct tevent_req *req);
4071 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4072 struct tevent_context *ev,
4073 struct cli_state *cli,
4081 struct tevent_req *req, *subreq = NULL;
4082 struct cli_smb2_writeall_state *state = NULL;
4087 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4093 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4094 state->flags = (uint32_t)mode;
4096 state->offset = (uint64_t)offset;
4097 state->size = (uint32_t)size;
4100 status = map_fnum_to_smb2_handle(cli,
4103 if (tevent_req_nterror(req, status)) {
4104 return tevent_req_post(req, ev);
4107 to_write = state->size;
4108 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4109 to_write = MIN(max_size, to_write);
4110 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4112 to_write = MIN(max_size, to_write);
4115 subreq = smb2cli_write_send(state,
4118 state->cli->timeout,
4119 state->cli->smb2.session,
4120 state->cli->smb2.tcon,
4123 state->ph->fid_persistent,
4124 state->ph->fid_volatile,
4125 0, /* remaining_bytes */
4126 state->flags, /* flags */
4127 state->buf + state->written);
4129 if (tevent_req_nomem(subreq, req)) {
4130 return tevent_req_post(req, ev);
4132 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4136 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4138 struct tevent_req *req = tevent_req_callback_data(
4139 subreq, struct tevent_req);
4140 struct cli_smb2_writeall_state *state = tevent_req_data(
4141 req, struct cli_smb2_writeall_state);
4143 uint32_t written, to_write;
4147 status = smb2cli_write_recv(subreq, &written);
4148 TALLOC_FREE(subreq);
4149 if (tevent_req_nterror(req, status)) {
4153 state->written += written;
4155 if (state->written > state->size) {
4156 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4160 to_write = state->size - state->written;
4162 if (to_write == 0) {
4163 tevent_req_done(req);
4167 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4168 to_write = MIN(max_size, to_write);
4169 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4171 to_write = MIN(max_size, to_write);
4174 subreq = smb2cli_write_send(state,
4177 state->cli->timeout,
4178 state->cli->smb2.session,
4179 state->cli->smb2.tcon,
4181 state->offset + state->written,
4182 state->ph->fid_persistent,
4183 state->ph->fid_volatile,
4184 0, /* remaining_bytes */
4185 state->flags, /* flags */
4186 state->buf + state->written);
4188 if (tevent_req_nomem(subreq, req)) {
4191 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4194 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4197 struct cli_smb2_writeall_state *state = tevent_req_data(
4198 req, struct cli_smb2_writeall_state);
4201 if (tevent_req_is_nterror(req, &status)) {
4202 state->cli->raw_status = status;
4205 if (pwritten != NULL) {
4206 *pwritten = (size_t)state->written;
4208 state->cli->raw_status = NT_STATUS_OK;
4209 return NT_STATUS_OK;
4212 struct cli_smb2_splice_state {
4213 struct tevent_context *ev;
4214 struct cli_state *cli;
4215 struct smb2_hnd *src_ph;
4216 struct smb2_hnd *dst_ph;
4217 int (*splice_cb)(off_t n, void *priv);
4224 struct req_resume_key_rsp resume_rsp;
4225 struct srv_copychunk_copy cc_copy;
4228 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4229 struct tevent_req *req);
4231 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4233 struct tevent_req *req = tevent_req_callback_data(
4234 subreq, struct tevent_req);
4235 struct cli_smb2_splice_state *state =
4236 tevent_req_data(req,
4237 struct cli_smb2_splice_state);
4238 struct smbXcli_conn *conn = state->cli->conn;
4239 DATA_BLOB out_input_buffer = data_blob_null;
4240 DATA_BLOB out_output_buffer = data_blob_null;
4241 struct srv_copychunk_rsp cc_copy_rsp;
4242 enum ndr_err_code ndr_ret;
4245 status = smb2cli_ioctl_recv(subreq, state,
4247 &out_output_buffer);
4248 TALLOC_FREE(subreq);
4249 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4250 state->resized) && tevent_req_nterror(req, status)) {
4254 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4255 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4256 if (ndr_ret != NDR_ERR_SUCCESS) {
4257 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4258 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4262 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4263 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4264 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4265 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4266 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4267 tevent_req_nterror(req, status)) {
4271 state->resized = true;
4272 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4273 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4275 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4276 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4277 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4278 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4281 state->src_offset += cc_copy_rsp.total_bytes_written;
4282 state->dst_offset += cc_copy_rsp.total_bytes_written;
4283 state->written += cc_copy_rsp.total_bytes_written;
4284 if (!state->splice_cb(state->written, state->priv)) {
4285 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4290 cli_splice_copychunk_send(state, req);
4293 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4294 struct tevent_req *req)
4296 struct tevent_req *subreq;
4297 enum ndr_err_code ndr_ret;
4298 struct smbXcli_conn *conn = state->cli->conn;
4299 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4300 off_t src_offset = state->src_offset;
4301 off_t dst_offset = state->dst_offset;
4302 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4303 state->size - state->written);
4304 DATA_BLOB in_input_buffer = data_blob_null;
4305 DATA_BLOB in_output_buffer = data_blob_null;
4307 if (state->size - state->written == 0) {
4308 tevent_req_done(req);
4312 cc_copy->chunk_count = 0;
4314 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4315 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4316 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4317 smb2cli_conn_cc_chunk_len(conn));
4318 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4319 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4322 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4323 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4324 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4325 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4328 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4329 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4330 cc_copy->chunk_count++;
4333 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4334 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4335 if (ndr_ret != NDR_ERR_SUCCESS) {
4336 DEBUG(0, ("failed to marshall copy chunk req\n"));
4337 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4341 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4342 state->cli->timeout,
4343 state->cli->smb2.session,
4344 state->cli->smb2.tcon,
4345 state->dst_ph->fid_persistent, /* in_fid_persistent */
4346 state->dst_ph->fid_volatile, /* in_fid_volatile */
4347 FSCTL_SRV_COPYCHUNK_WRITE,
4348 0, /* in_max_input_length */
4350 12, /* in_max_output_length */
4352 SMB2_IOCTL_FLAG_IS_FSCTL);
4353 if (tevent_req_nomem(subreq, req)) {
4356 tevent_req_set_callback(subreq,
4357 cli_splice_copychunk_done,
4361 static void cli_splice_key_done(struct tevent_req *subreq)
4363 struct tevent_req *req = tevent_req_callback_data(
4364 subreq, struct tevent_req);
4365 struct cli_smb2_splice_state *state =
4366 tevent_req_data(req,
4367 struct cli_smb2_splice_state);
4368 enum ndr_err_code ndr_ret;
4371 DATA_BLOB out_input_buffer = data_blob_null;
4372 DATA_BLOB out_output_buffer = data_blob_null;
4374 status = smb2cli_ioctl_recv(subreq, state,
4376 &out_output_buffer);
4377 TALLOC_FREE(subreq);
4378 if (tevent_req_nterror(req, status)) {
4382 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4383 state, &state->resume_rsp,
4384 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4385 if (ndr_ret != NDR_ERR_SUCCESS) {
4386 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4387 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4391 memcpy(&state->cc_copy.source_key,
4392 &state->resume_rsp.resume_key,
4393 sizeof state->resume_rsp.resume_key);
4395 cli_splice_copychunk_send(state, req);
4398 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4399 struct tevent_context *ev,
4400 struct cli_state *cli,
4401 uint16_t src_fnum, uint16_t dst_fnum,
4402 off_t size, off_t src_offset, off_t dst_offset,
4403 int (*splice_cb)(off_t n, void *priv),
4406 struct tevent_req *req;
4407 struct tevent_req *subreq;
4408 struct cli_smb2_splice_state *state;
4410 DATA_BLOB in_input_buffer = data_blob_null;
4411 DATA_BLOB in_output_buffer = data_blob_null;
4413 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4419 state->splice_cb = splice_cb;
4423 state->src_offset = src_offset;
4424 state->dst_offset = dst_offset;
4425 state->cc_copy.chunks = talloc_array(state,
4426 struct srv_copychunk,
4427 smb2cli_conn_cc_max_chunks(cli->conn));
4428 if (state->cc_copy.chunks == NULL) {
4432 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4433 if (tevent_req_nterror(req, status))
4434 return tevent_req_post(req, ev);
4436 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4437 if (tevent_req_nterror(req, status))
4438 return tevent_req_post(req, ev);
4440 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4444 state->src_ph->fid_persistent, /* in_fid_persistent */
4445 state->src_ph->fid_volatile, /* in_fid_volatile */
4446 FSCTL_SRV_REQUEST_RESUME_KEY,
4447 0, /* in_max_input_length */
4449 32, /* in_max_output_length */
4451 SMB2_IOCTL_FLAG_IS_FSCTL);
4452 if (tevent_req_nomem(subreq, req)) {
4455 tevent_req_set_callback(subreq,
4456 cli_splice_key_done,
4462 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4464 struct cli_smb2_splice_state *state = tevent_req_data(
4465 req, struct cli_smb2_splice_state);
4468 if (tevent_req_is_nterror(req, &status)) {
4469 state->cli->raw_status = status;
4470 tevent_req_received(req);
4473 if (written != NULL) {
4474 *written = state->written;
4476 state->cli->raw_status = NT_STATUS_OK;
4477 tevent_req_received(req);
4478 return NT_STATUS_OK;
4481 /***************************************************************
4482 SMB2 enum shadow copy data.
4483 ***************************************************************/
4485 struct cli_smb2_shadow_copy_data_fnum_state {
4486 struct cli_state *cli;
4488 struct smb2_hnd *ph;
4489 DATA_BLOB out_input_buffer;
4490 DATA_BLOB out_output_buffer;
4493 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4495 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4496 TALLOC_CTX *mem_ctx,
4497 struct tevent_context *ev,
4498 struct cli_state *cli,
4502 struct tevent_req *req, *subreq;
4503 struct cli_smb2_shadow_copy_data_fnum_state *state;
4506 req = tevent_req_create(mem_ctx, &state,
4507 struct cli_smb2_shadow_copy_data_fnum_state);
4515 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4516 if (tevent_req_nterror(req, status)) {
4517 return tevent_req_post(req, ev);
4521 * TODO. Under SMB2 we should send a zero max_output_length
4522 * ioctl to get the required size, then send another ioctl
4523 * to get the data, but the current SMB1 implementation just
4524 * does one roundtrip with a 64K buffer size. Do the same
4528 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4529 state->cli->timeout,
4530 state->cli->smb2.session,
4531 state->cli->smb2.tcon,
4532 state->ph->fid_persistent, /* in_fid_persistent */
4533 state->ph->fid_volatile, /* in_fid_volatile */
4534 FSCTL_GET_SHADOW_COPY_DATA,
4535 0, /* in_max_input_length */
4536 NULL, /* in_input_buffer */
4538 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4539 NULL, /* in_output_buffer */
4540 SMB2_IOCTL_FLAG_IS_FSCTL);
4542 if (tevent_req_nomem(subreq, req)) {
4543 return tevent_req_post(req, ev);
4545 tevent_req_set_callback(subreq,
4546 cli_smb2_shadow_copy_data_fnum_done,
4552 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4554 struct tevent_req *req = tevent_req_callback_data(
4555 subreq, struct tevent_req);
4556 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4557 req, struct cli_smb2_shadow_copy_data_fnum_state);
4560 status = smb2cli_ioctl_recv(subreq, state,
4561 &state->out_input_buffer,
4562 &state->out_output_buffer);
4563 tevent_req_simple_finish_ntstatus(subreq, status);
4566 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4567 TALLOC_CTX *mem_ctx,
4572 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4573 req, struct cli_smb2_shadow_copy_data_fnum_state);
4574 char **names = NULL;
4575 uint32_t num_names = 0;
4576 uint32_t num_names_returned = 0;
4577 uint32_t dlength = 0;
4579 uint8_t *endp = NULL;
4582 if (tevent_req_is_nterror(req, &status)) {
4586 if (state->out_output_buffer.length < 16) {
4587 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4590 num_names = IVAL(state->out_output_buffer.data, 0);
4591 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4592 dlength = IVAL(state->out_output_buffer.data, 8);
4594 if (num_names > 0x7FFFFFFF) {
4595 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4598 if (get_names == false) {
4599 *pnum_names = (int)num_names;
4600 return NT_STATUS_OK;
4602 if (num_names != num_names_returned) {
4603 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4605 if (dlength + 12 < 12) {
4606 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4609 * NB. The below is an allowable return if there are
4610 * more snapshots than the buffer size we told the
4611 * server we can receive. We currently don't support
4614 if (dlength + 12 > state->out_output_buffer.length) {
4615 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4617 if (state->out_output_buffer.length +
4618 (2 * sizeof(SHADOW_COPY_LABEL)) <
4619 state->out_output_buffer.length) {
4620 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4623 names = talloc_array(mem_ctx, char *, num_names_returned);
4624 if (names == NULL) {
4625 return NT_STATUS_NO_MEMORY;
4628 endp = state->out_output_buffer.data +
4629 state->out_output_buffer.length;
4631 for (i=0; i<num_names_returned; i++) {
4634 size_t converted_size;
4636 src = state->out_output_buffer.data + 12 +
4637 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4639 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4640 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4642 ret = convert_string_talloc(
4643 names, CH_UTF16LE, CH_UNIX,
4644 src, 2 * sizeof(SHADOW_COPY_LABEL),
4645 &names[i], &converted_size);
4648 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4651 *pnum_names = num_names;
4653 return NT_STATUS_OK;
4656 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4657 struct cli_state *cli,
4663 TALLOC_CTX *frame = talloc_stackframe();
4664 struct tevent_context *ev;
4665 struct tevent_req *req;
4666 NTSTATUS status = NT_STATUS_NO_MEMORY;
4668 if (smbXcli_conn_has_async_calls(cli->conn)) {
4670 * Can't use sync call while an async call is in flight
4672 status = NT_STATUS_INVALID_PARAMETER;
4675 ev = samba_tevent_context_init(frame);
4679 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4687 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4690 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4696 cli->raw_status = status;
4702 /***************************************************************
4703 Wrapper that allows SMB2 to truncate a file.
4705 ***************************************************************/
4707 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4712 uint8_t buf[8] = {0};
4713 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4714 TALLOC_CTX *frame = talloc_stackframe();
4716 if (smbXcli_conn_has_async_calls(cli->conn)) {
4718 * Can't use sync call while an async call is in flight
4720 status = NT_STATUS_INVALID_PARAMETER;
4724 SBVAL(buf, 0, newsize);
4726 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4727 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4729 status = cli_smb2_set_info_fnum(
4732 1, /* in_info_type */
4733 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4734 &inbuf, /* in_input_buffer */
4739 cli->raw_status = status;
4745 struct cli_smb2_notify_state {
4746 struct tevent_req *subreq;
4747 struct notify_change *changes;
4751 static void cli_smb2_notify_done(struct tevent_req *subreq);
4752 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4754 struct tevent_req *cli_smb2_notify_send(
4755 TALLOC_CTX *mem_ctx,
4756 struct tevent_context *ev,
4757 struct cli_state *cli,
4759 uint32_t buffer_size,
4760 uint32_t completion_filter,
4763 struct tevent_req *req = NULL;
4764 struct cli_smb2_notify_state *state = NULL;
4765 struct smb2_hnd *ph = NULL;
4768 req = tevent_req_create(mem_ctx, &state,
4769 struct cli_smb2_notify_state);
4774 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4775 if (tevent_req_nterror(req, status)) {
4776 return tevent_req_post(req, ev);
4779 state->subreq = smb2cli_notify_send(
4791 if (tevent_req_nomem(state->subreq, req)) {
4792 return tevent_req_post(req, ev);
4794 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4795 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4799 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4801 struct cli_smb2_notify_state *state = tevent_req_data(
4802 req, struct cli_smb2_notify_state);
4805 ok = tevent_req_cancel(state->subreq);
4809 static void cli_smb2_notify_done(struct tevent_req *subreq)
4811 struct tevent_req *req = tevent_req_callback_data(
4812 subreq, struct tevent_req);
4813 struct cli_smb2_notify_state *state = tevent_req_data(
4814 req, struct cli_smb2_notify_state);
4820 status = smb2cli_notify_recv(subreq, state, &base, &len);
4821 TALLOC_FREE(subreq);
4823 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4824 tevent_req_done(req);
4827 if (tevent_req_nterror(req, status)) {
4833 while (len - ofs >= 12) {
4834 struct notify_change *tmp;
4835 struct notify_change *c;
4836 uint32_t next_ofs = IVAL(base, ofs);
4837 uint32_t file_name_length = IVAL(base, ofs+8);
4841 tmp = talloc_realloc(
4844 struct notify_change,
4845 state->num_changes + 1);
4846 if (tevent_req_nomem(tmp, req)) {
4849 state->changes = tmp;
4850 c = &state->changes[state->num_changes];
4851 state->num_changes += 1;
4853 if (smb_buffer_oob(len, ofs, next_ofs) ||
4854 smb_buffer_oob(len, ofs+12, file_name_length)) {
4856 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4860 c->action = IVAL(base, ofs+4);
4862 ok = convert_string_talloc(
4872 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4876 if (next_ofs == 0) {
4882 tevent_req_done(req);
4885 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4886 TALLOC_CTX *mem_ctx,
4887 struct notify_change **pchanges,
4888 uint32_t *pnum_changes)
4890 struct cli_smb2_notify_state *state = tevent_req_data(
4891 req, struct cli_smb2_notify_state);
4894 if (tevent_req_is_nterror(req, &status)) {
4897 *pchanges = talloc_move(mem_ctx, &state->changes);
4898 *pnum_changes = state->num_changes;
4899 return NT_STATUS_OK;
4902 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4903 uint32_t buffer_size, uint32_t completion_filter,
4904 bool recursive, TALLOC_CTX *mem_ctx,
4905 struct notify_change **pchanges,
4906 uint32_t *pnum_changes)
4908 TALLOC_CTX *frame = talloc_stackframe();
4909 struct tevent_context *ev;
4910 struct tevent_req *req;
4911 NTSTATUS status = NT_STATUS_NO_MEMORY;
4913 if (smbXcli_conn_has_async_calls(cli->conn)) {
4915 * Can't use sync call while an async call is in flight
4917 status = NT_STATUS_INVALID_PARAMETER;
4920 ev = samba_tevent_context_init(frame);
4924 req = cli_smb2_notify_send(
4935 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4938 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4944 struct cli_smb2_fsctl_state {
4948 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4950 struct tevent_req *cli_smb2_fsctl_send(
4951 TALLOC_CTX *mem_ctx,
4952 struct tevent_context *ev,
4953 struct cli_state *cli,
4956 const DATA_BLOB *in,
4959 struct tevent_req *req = NULL, *subreq = NULL;
4960 struct cli_smb2_fsctl_state *state = NULL;
4961 struct smb2_hnd *ph = NULL;
4964 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4969 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4970 if (tevent_req_nterror(req, status)) {
4971 return tevent_req_post(req, ev);
4974 subreq = smb2cli_ioctl_send(
4984 0, /* in_max_input_length */
4988 SMB2_IOCTL_FLAG_IS_FSCTL);
4990 if (tevent_req_nomem(subreq, req)) {
4991 return tevent_req_post(req, ev);
4993 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
4997 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
4999 struct tevent_req *req = tevent_req_callback_data(
5000 subreq, struct tevent_req);
5001 struct cli_smb2_fsctl_state *state = tevent_req_data(
5002 req, struct cli_smb2_fsctl_state);
5005 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5006 tevent_req_simple_finish_ntstatus(subreq, status);
5009 NTSTATUS cli_smb2_fsctl_recv(
5010 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5012 struct cli_smb2_fsctl_state *state = tevent_req_data(
5013 req, struct cli_smb2_fsctl_state);
5014 NTSTATUS status = NT_STATUS_OK;
5016 if (tevent_req_is_nterror(req, &status)) {
5017 tevent_req_received(req);
5021 if (state->out.length == 0) {
5022 *out = (DATA_BLOB) { .data = NULL, };
5025 * Can't use talloc_move() here, the outblobs from
5026 * smb2cli_ioctl_recv() are not standalone talloc
5027 * objects but just peek into the larger buffers
5028 * received, hanging off "state".
5030 *out = data_blob_talloc(
5031 mem_ctx, state->out.data, state->out.length);
5032 if (out->data == NULL) {
5033 status = NT_STATUS_NO_MEMORY;
5037 tevent_req_received(req);
5038 return NT_STATUS_OK;