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 "lib/util/string_wrappers.h"
46 #include "lib/util/idtree.h"
49 uint64_t fid_persistent;
50 uint64_t fid_volatile;
54 * Handle mapping code.
57 /***************************************************************
58 Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
59 Ensures handle is owned by cli struct.
60 ***************************************************************/
62 static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
63 const struct smb2_hnd *ph, /* In */
64 uint16_t *pfnum) /* Out */
67 struct idr_context *idp = cli->smb2.open_handles;
68 struct smb2_hnd *owned_h = talloc_memdup(cli,
70 sizeof(struct smb2_hnd));
72 if (owned_h == NULL) {
73 return NT_STATUS_NO_MEMORY;
78 cli->smb2.open_handles = idr_init(cli);
79 if (cli->smb2.open_handles == NULL) {
81 return NT_STATUS_NO_MEMORY;
83 idp = cli->smb2.open_handles;
86 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
89 return NT_STATUS_NO_MEMORY;
92 *pfnum = (uint16_t)ret;
96 /***************************************************************
97 Return the smb2_hnd pointer associated with the given fnum.
98 ***************************************************************/
100 static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
101 uint16_t fnum, /* In */
102 struct smb2_hnd **pph) /* Out */
104 struct idr_context *idp = cli->smb2.open_handles;
107 return NT_STATUS_INVALID_PARAMETER;
109 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
111 return NT_STATUS_INVALID_HANDLE;
116 /***************************************************************
117 Delete the fnum to smb2_hnd mapping. Zeros out handle on
119 ***************************************************************/
121 static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
122 struct smb2_hnd **pph, /* In */
123 uint16_t fnum) /* In */
125 struct idr_context *idp = cli->smb2.open_handles;
129 return NT_STATUS_INVALID_PARAMETER;
132 ph = (struct smb2_hnd *)idr_find(idp, fnum);
134 return NT_STATUS_INVALID_PARAMETER;
136 idr_remove(idp, fnum);
141 /***************************************************************
143 ***************************************************************/
145 static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
147 if (create_flags.batch_oplock) {
148 return SMB2_OPLOCK_LEVEL_BATCH;
149 } else if (create_flags.exclusive_oplock) {
150 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
153 /* create_flags doesn't do a level2 request. */
154 return SMB2_OPLOCK_LEVEL_NONE;
157 /***************************************************************
158 If we're on a DFS share, ensure we convert to a full DFS path
159 if this hasn't already been done.
160 ***************************************************************/
162 static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
163 struct cli_state *cli,
166 bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
167 smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
168 bool is_already_dfs_path = false;
173 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
174 if (is_already_dfs_path) {
177 if (path[0] == '\0') {
178 return talloc_asprintf(ctx,
180 smbXcli_conn_remote_name(cli->conn),
183 while (*path == '\\') {
186 return talloc_asprintf(ctx,
188 smbXcli_conn_remote_name(cli->conn),
193 /***************************************************************
194 Small wrapper that allows SMB2 create to return a uint16_t fnum.
195 ***************************************************************/
197 struct cli_smb2_create_fnum_state {
198 struct cli_state *cli;
199 struct smb2_create_blobs in_cblobs;
200 struct smb2_create_blobs out_cblobs;
201 struct smb_create_returns cr;
202 struct symlink_reparse_struct *symlink;
204 struct tevent_req *subreq;
207 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
208 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
210 struct tevent_req *cli_smb2_create_fnum_send(
212 struct tevent_context *ev,
213 struct cli_state *cli,
214 const char *fname_in,
215 struct cli_smb2_create_flags create_flags,
216 uint32_t impersonation_level,
217 uint32_t desired_access,
218 uint32_t file_attributes,
219 uint32_t share_access,
220 uint32_t create_disposition,
221 uint32_t create_options,
222 const struct smb2_create_blobs *in_cblobs)
224 struct tevent_req *req, *subreq;
225 struct cli_smb2_create_fnum_state *state;
227 size_t fname_len = 0;
232 req = tevent_req_create(mem_ctx, &state,
233 struct cli_smb2_create_fnum_state);
239 fname = talloc_strdup(state, fname_in);
240 if (tevent_req_nomem(fname, req)) {
241 return tevent_req_post(req, ev);
244 if (cli->backup_intent) {
245 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
248 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
249 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
251 status = smb2_create_blob_add(
254 SMB2_CREATE_TAG_TWRP,
256 .data = (uint8_t *)&ntt,
257 .length = sizeof(ntt),
259 if (tevent_req_nterror(req, status)) {
260 return tevent_req_post(req, ev);
264 if (in_cblobs != NULL) {
266 for (i=0; i<in_cblobs->num_blobs; i++) {
267 struct smb2_create_blob *b = &in_cblobs->blobs[i];
268 status = smb2_create_blob_add(
269 state, &state->in_cblobs, b->tag, b->data);
270 if (!NT_STATUS_IS_OK(status)) {
271 tevent_req_nterror(req, status);
272 return tevent_req_post(req, ev);
277 fname = smb2_dfs_share_path(state, cli, fname);
278 if (tevent_req_nomem(fname, req)) {
279 return tevent_req_post(req, ev);
281 fname_len = strlen(fname);
283 /* SMB2 is pickier about pathnames. Ensure it doesn't
285 if (*fname == '\\') {
290 /* Or end in a '\' */
291 if (fname_len > 0 && fname[fname_len-1] == '\\') {
292 fname[fname_len-1] = '\0';
295 subreq = smb2cli_create_send(state, ev,
301 flags_to_smb2_oplock(create_flags),
309 if (tevent_req_nomem(subreq, req)) {
310 return tevent_req_post(req, ev);
312 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
314 state->subreq = subreq;
315 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
320 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
322 struct tevent_req *req = tevent_req_callback_data(
323 subreq, struct tevent_req);
324 struct cli_smb2_create_fnum_state *state = tevent_req_data(
325 req, struct cli_smb2_create_fnum_state);
329 status = smb2cli_create_recv(
332 &h.fid_volatile, &state->cr,
337 if (tevent_req_nterror(req, status)) {
341 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
342 if (tevent_req_nterror(req, status)) {
345 tevent_req_done(req);
348 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
350 struct cli_smb2_create_fnum_state *state = tevent_req_data(
351 req, struct cli_smb2_create_fnum_state);
352 return tevent_req_cancel(state->subreq);
355 NTSTATUS cli_smb2_create_fnum_recv(
356 struct tevent_req *req,
358 struct smb_create_returns *cr,
360 struct smb2_create_blobs *out_cblobs,
361 struct symlink_reparse_struct **symlink)
363 struct cli_smb2_create_fnum_state *state = tevent_req_data(
364 req, struct cli_smb2_create_fnum_state);
367 if (tevent_req_is_nterror(req, &status)) {
368 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
370 *symlink = talloc_move(mem_ctx, &state->symlink);
372 state->cli->raw_status = status;
376 *pfnum = state->fnum;
381 if (out_cblobs != NULL) {
382 *out_cblobs = (struct smb2_create_blobs) {
383 .num_blobs = state->out_cblobs.num_blobs,
384 .blobs = talloc_move(
385 mem_ctx, &state->out_cblobs.blobs),
388 state->cli->raw_status = NT_STATUS_OK;
392 NTSTATUS cli_smb2_create_fnum(
393 struct cli_state *cli,
395 struct cli_smb2_create_flags create_flags,
396 uint32_t impersonation_level,
397 uint32_t desired_access,
398 uint32_t file_attributes,
399 uint32_t share_access,
400 uint32_t create_disposition,
401 uint32_t create_options,
402 const struct smb2_create_blobs *in_cblobs,
404 struct smb_create_returns *cr,
406 struct smb2_create_blobs *out_cblobs)
408 TALLOC_CTX *frame = talloc_stackframe();
409 struct tevent_context *ev;
410 struct tevent_req *req;
411 NTSTATUS status = NT_STATUS_NO_MEMORY;
413 if (smbXcli_conn_has_async_calls(cli->conn)) {
415 * Can't use sync call while an async call is in flight
417 status = NT_STATUS_INVALID_PARAMETER;
420 ev = samba_tevent_context_init(frame);
424 req = cli_smb2_create_fnum_send(
440 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
443 status = cli_smb2_create_fnum_recv(
444 req, pfid, cr, mem_ctx, out_cblobs, NULL);
450 /***************************************************************
451 Small wrapper that allows SMB2 close to use a uint16_t fnum.
452 ***************************************************************/
454 struct cli_smb2_close_fnum_state {
455 struct cli_state *cli;
460 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
462 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
463 struct tevent_context *ev,
464 struct cli_state *cli,
467 struct tevent_req *req, *subreq;
468 struct cli_smb2_close_fnum_state *state;
471 req = tevent_req_create(mem_ctx, &state,
472 struct cli_smb2_close_fnum_state);
479 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
480 if (tevent_req_nterror(req, status)) {
481 return tevent_req_post(req, ev);
484 subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
485 cli->smb2.session, cli->smb2.tcon,
486 0, state->ph->fid_persistent,
487 state->ph->fid_volatile);
488 if (tevent_req_nomem(subreq, req)) {
489 return tevent_req_post(req, ev);
491 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
495 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
497 struct tevent_req *req = tevent_req_callback_data(
498 subreq, struct tevent_req);
499 struct cli_smb2_close_fnum_state *state = tevent_req_data(
500 req, struct cli_smb2_close_fnum_state);
503 status = smb2cli_close_recv(subreq);
504 if (tevent_req_nterror(req, status)) {
508 /* Delete the fnum -> handle mapping. */
509 status = delete_smb2_handle_mapping(state->cli, &state->ph,
511 if (tevent_req_nterror(req, status)) {
514 tevent_req_done(req);
517 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
519 struct cli_smb2_close_fnum_state *state = tevent_req_data(
520 req, struct cli_smb2_close_fnum_state);
521 NTSTATUS status = NT_STATUS_OK;
523 if (tevent_req_is_nterror(req, &status)) {
524 state->cli->raw_status = status;
526 tevent_req_received(req);
530 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
532 TALLOC_CTX *frame = talloc_stackframe();
533 struct tevent_context *ev;
534 struct tevent_req *req;
535 NTSTATUS status = NT_STATUS_NO_MEMORY;
537 if (smbXcli_conn_has_async_calls(cli->conn)) {
539 * Can't use sync call while an async call is in flight
541 status = NT_STATUS_INVALID_PARAMETER;
544 ev = samba_tevent_context_init(frame);
548 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
552 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
555 status = cli_smb2_close_fnum_recv(req);
561 struct cli_smb2_set_info_fnum_state {
565 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
567 struct tevent_req *cli_smb2_set_info_fnum_send(
569 struct tevent_context *ev,
570 struct cli_state *cli,
572 uint8_t in_info_type,
573 uint8_t in_info_class,
574 const DATA_BLOB *in_input_buffer,
575 uint32_t in_additional_info)
577 struct tevent_req *req = NULL, *subreq = NULL;
578 struct cli_smb2_set_info_fnum_state *state = NULL;
579 struct smb2_hnd *ph = NULL;
582 req = tevent_req_create(
583 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
588 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
589 if (tevent_req_nterror(req, status)) {
590 return tevent_req_post(req, ev);
593 subreq = smb2cli_set_info_send(
606 if (tevent_req_nomem(subreq, req)) {
607 return tevent_req_post(req, ev);
609 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
613 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
615 NTSTATUS status = smb2cli_set_info_recv(subreq);
616 tevent_req_simple_finish_ntstatus(subreq, status);
619 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
621 return tevent_req_simple_recv_ntstatus(req);
624 NTSTATUS cli_smb2_set_info_fnum(
625 struct cli_state *cli,
627 uint8_t in_info_type,
628 uint8_t in_info_class,
629 const DATA_BLOB *in_input_buffer,
630 uint32_t in_additional_info)
632 TALLOC_CTX *frame = talloc_stackframe();
633 struct tevent_context *ev = NULL;
634 struct tevent_req *req = NULL;
635 NTSTATUS status = NT_STATUS_NO_MEMORY;
638 if (smbXcli_conn_has_async_calls(cli->conn)) {
640 * Can't use sync call while an async call is in flight
642 status = NT_STATUS_INVALID_PARAMETER;
645 ev = samba_tevent_context_init(frame);
649 req = cli_smb2_set_info_fnum_send(
661 ok = tevent_req_poll_ntstatus(req, ev, &status);
665 status = cli_smb2_set_info_fnum_recv(req);
671 struct cli_smb2_delete_on_close_state {
672 struct cli_state *cli;
677 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
679 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
680 struct tevent_context *ev,
681 struct cli_state *cli,
685 struct tevent_req *req = NULL;
686 struct cli_smb2_delete_on_close_state *state = NULL;
687 struct tevent_req *subreq = NULL;
688 uint8_t in_info_type;
689 uint8_t in_file_info_class;
691 req = tevent_req_create(mem_ctx, &state,
692 struct cli_smb2_delete_on_close_state);
699 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
700 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
703 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
704 /* Setup data array. */
705 SCVAL(&state->data[0], 0, flag ? 1 : 0);
706 state->inbuf.data = &state->data[0];
707 state->inbuf.length = 1;
709 subreq = cli_smb2_set_info_fnum_send(
718 if (tevent_req_nomem(subreq, req)) {
719 return tevent_req_post(req, ev);
721 tevent_req_set_callback(subreq,
722 cli_smb2_delete_on_close_done,
727 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
729 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
730 tevent_req_simple_finish_ntstatus(subreq, status);
733 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
735 struct cli_smb2_delete_on_close_state *state =
737 struct cli_smb2_delete_on_close_state);
740 if (tevent_req_is_nterror(req, &status)) {
741 state->cli->raw_status = status;
742 tevent_req_received(req);
746 state->cli->raw_status = NT_STATUS_OK;
747 tevent_req_received(req);
751 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
753 TALLOC_CTX *frame = talloc_stackframe();
754 struct tevent_context *ev;
755 struct tevent_req *req;
756 NTSTATUS status = NT_STATUS_NO_MEMORY;
758 if (smbXcli_conn_has_async_calls(cli->conn)) {
760 * Can't use sync call while an async call is in flight
762 status = NT_STATUS_INVALID_PARAMETER;
765 ev = samba_tevent_context_init(frame);
769 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
773 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
776 status = cli_smb2_delete_on_close_recv(req);
782 struct cli_smb2_mkdir_state {
783 struct tevent_context *ev;
784 struct cli_state *cli;
787 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
788 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
790 struct tevent_req *cli_smb2_mkdir_send(
792 struct tevent_context *ev,
793 struct cli_state *cli,
796 struct tevent_req *req = NULL, *subreq = NULL;
797 struct cli_smb2_mkdir_state *state = NULL;
799 req = tevent_req_create(
800 mem_ctx, &state, struct cli_smb2_mkdir_state);
807 /* Ensure this is a directory. */
808 subreq = cli_smb2_create_fnum_send(
813 (struct cli_smb2_create_flags){0}, /* create_flags */
814 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
815 FILE_READ_ATTRIBUTES, /* desired_access */
816 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
818 FILE_SHARE_WRITE, /* share_access */
819 FILE_CREATE, /* create_disposition */
820 FILE_DIRECTORY_FILE, /* create_options */
821 NULL); /* in_cblobs */
822 if (tevent_req_nomem(subreq, req)) {
823 return tevent_req_post(req, ev);
825 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
829 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
831 struct tevent_req *req = tevent_req_callback_data(
832 subreq, struct tevent_req);
833 struct cli_smb2_mkdir_state *state = tevent_req_data(
834 req, struct cli_smb2_mkdir_state);
836 uint16_t fnum = 0xffff;
838 status = cli_smb2_create_fnum_recv(
839 subreq, &fnum, NULL, NULL, NULL, NULL);
841 if (tevent_req_nterror(req, status)) {
845 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
846 if (tevent_req_nomem(subreq, req)) {
849 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
852 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
854 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
855 tevent_req_simple_finish_ntstatus(subreq, status);
858 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
860 return tevent_req_simple_recv_ntstatus(req);
863 struct cli_smb2_rmdir_state {
864 struct tevent_context *ev;
865 struct cli_state *cli;
867 const struct smb2_create_blobs *in_cblobs;
872 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
873 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
874 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
875 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
877 struct tevent_req *cli_smb2_rmdir_send(
879 struct tevent_context *ev,
880 struct cli_state *cli,
882 const struct smb2_create_blobs *in_cblobs)
884 struct tevent_req *req = NULL, *subreq = NULL;
885 struct cli_smb2_rmdir_state *state = NULL;
887 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
893 state->dname = dname;
894 state->in_cblobs = in_cblobs;
896 subreq = cli_smb2_create_fnum_send(
901 (struct cli_smb2_create_flags){0},
902 SMB2_IMPERSONATION_IMPERSONATION,
903 DELETE_ACCESS, /* desired_access */
904 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
905 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
906 FILE_OPEN, /* create_disposition */
907 FILE_DIRECTORY_FILE, /* create_options */
908 state->in_cblobs); /* in_cblobs */
909 if (tevent_req_nomem(subreq, req)) {
910 return tevent_req_post(req, ev);
912 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
916 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
918 struct tevent_req *req = tevent_req_callback_data(
919 subreq, struct tevent_req);
920 struct cli_smb2_rmdir_state *state = tevent_req_data(
921 req, struct cli_smb2_rmdir_state);
924 status = cli_smb2_create_fnum_recv(
925 subreq, &state->fnum, NULL, NULL, NULL, NULL);
928 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
930 * Naive option to match our SMB1 code. Assume the
931 * symlink path that tripped us up was the last
932 * component and try again. Eventually we will have to
933 * deal with the returned path unprocessed component. JRA.
935 subreq = cli_smb2_create_fnum_send(
940 (struct cli_smb2_create_flags){0},
941 SMB2_IMPERSONATION_IMPERSONATION,
942 DELETE_ACCESS, /* desired_access */
943 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
944 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
945 FILE_OPEN, /* create_disposition */
947 FILE_DELETE_ON_CLOSE|
948 FILE_OPEN_REPARSE_POINT, /* create_options */
949 state->in_cblobs); /* in_cblobs */
950 if (tevent_req_nomem(subreq, req)) {
953 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
957 if (tevent_req_nterror(req, status)) {
961 subreq = cli_smb2_delete_on_close_send(
962 state, state->ev, state->cli, state->fnum, true);
963 if (tevent_req_nomem(subreq, req)) {
966 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
969 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
971 struct tevent_req *req = tevent_req_callback_data(
972 subreq, struct tevent_req);
973 struct cli_smb2_rmdir_state *state = tevent_req_data(
974 req, struct cli_smb2_rmdir_state);
977 status = cli_smb2_create_fnum_recv(
978 subreq, &state->fnum, NULL, NULL, NULL, NULL);
980 if (tevent_req_nterror(req, status)) {
984 subreq = cli_smb2_delete_on_close_send(
985 state, state->ev, state->cli, state->fnum, true);
986 if (tevent_req_nomem(subreq, req)) {
989 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
992 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
994 struct tevent_req *req = tevent_req_callback_data(
995 subreq, struct tevent_req);
996 struct cli_smb2_rmdir_state *state = tevent_req_data(
997 req, struct cli_smb2_rmdir_state);
999 state->status = cli_smb2_delete_on_close_recv(subreq);
1000 TALLOC_FREE(subreq);
1003 * Close the fd even if the set_disp failed
1006 subreq = cli_smb2_close_fnum_send(
1007 state, state->ev, state->cli, state->fnum);
1008 if (tevent_req_nomem(subreq, req)) {
1011 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1014 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1016 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1017 tevent_req_simple_finish_ntstatus(subreq, status);
1020 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1022 struct cli_smb2_rmdir_state *state = tevent_req_data(
1023 req, struct cli_smb2_rmdir_state);
1026 if (tevent_req_is_nterror(req, &status)) {
1029 return state->status;
1032 /***************************************************************
1033 Small wrapper that allows SMB2 to unlink a pathname.
1034 ***************************************************************/
1036 struct cli_smb2_unlink_state {
1037 struct tevent_context *ev;
1038 struct cli_state *cli;
1040 const struct smb2_create_blobs *in_cblobs;
1043 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1044 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1045 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1047 struct tevent_req *cli_smb2_unlink_send(
1048 TALLOC_CTX *mem_ctx,
1049 struct tevent_context *ev,
1050 struct cli_state *cli,
1052 const struct smb2_create_blobs *in_cblobs)
1054 struct tevent_req *req = NULL, *subreq = NULL;
1055 struct cli_smb2_unlink_state *state = NULL;
1057 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1063 state->fname = fname;
1064 state->in_cblobs = in_cblobs;
1066 subreq = cli_smb2_create_fnum_send(
1067 state, /* mem_ctx */
1068 state->ev, /* tevent_context */
1069 state->cli, /* cli_struct */
1070 state->fname, /* filename */
1071 (struct cli_smb2_create_flags){0},
1072 SMB2_IMPERSONATION_IMPERSONATION,
1073 DELETE_ACCESS, /* desired_access */
1074 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1077 FILE_SHARE_DELETE, /* share_access */
1078 FILE_OPEN, /* create_disposition */
1079 FILE_DELETE_ON_CLOSE, /* create_options */
1080 state->in_cblobs); /* in_cblobs */
1081 if (tevent_req_nomem(subreq, req)) {
1082 return tevent_req_post(req, ev);
1084 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1088 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1090 struct tevent_req *req = tevent_req_callback_data(
1091 subreq, struct tevent_req);
1092 struct cli_smb2_unlink_state *state = tevent_req_data(
1093 req, struct cli_smb2_unlink_state);
1094 uint16_t fnum = 0xffff;
1097 status = cli_smb2_create_fnum_recv(
1098 subreq, &fnum, NULL, NULL, NULL, NULL);
1099 TALLOC_FREE(subreq);
1101 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1102 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1104 * Naive option to match our SMB1 code. Assume the
1105 * symlink path that tripped us up was the last
1106 * component and try again. Eventually we will have to
1107 * deal with the returned path unprocessed component. JRA.
1109 subreq = cli_smb2_create_fnum_send(
1110 state, /* mem_ctx */
1111 state->ev, /* tevent_context */
1112 state->cli, /* cli_struct */
1113 state->fname, /* filename */
1114 (struct cli_smb2_create_flags){0},
1115 SMB2_IMPERSONATION_IMPERSONATION,
1116 DELETE_ACCESS, /* desired_access */
1117 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1120 FILE_SHARE_DELETE, /* share_access */
1121 FILE_OPEN, /* create_disposition */
1122 FILE_DELETE_ON_CLOSE|
1123 FILE_OPEN_REPARSE_POINT, /* create_options */
1124 state->in_cblobs); /* in_cblobs */
1125 if (tevent_req_nomem(subreq, req)) {
1128 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1132 if (tevent_req_nterror(req, status)) {
1136 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1137 if (tevent_req_nomem(subreq, req)) {
1140 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1143 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1145 struct tevent_req *req = tevent_req_callback_data(
1146 subreq, struct tevent_req);
1147 struct cli_smb2_unlink_state *state = tevent_req_data(
1148 req, struct cli_smb2_unlink_state);
1149 uint16_t fnum = 0xffff;
1152 status = cli_smb2_create_fnum_recv(
1153 subreq, &fnum, NULL, NULL, NULL, NULL);
1154 TALLOC_FREE(subreq);
1155 if (tevent_req_nterror(req, status)) {
1159 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1160 if (tevent_req_nomem(subreq, req)) {
1163 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1166 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1168 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1169 tevent_req_simple_finish_ntstatus(subreq, status);
1172 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1174 return tevent_req_simple_recv_ntstatus(req);
1177 static ssize_t sid_parse_wire(TALLOC_CTX *mem_ctx, const uint8_t *data,
1178 struct dom_sid *sid, size_t num_rdata)
1181 enum ndr_err_code ndr_err;
1182 DATA_BLOB in = data_blob_const(data, num_rdata);
1184 ndr_err = ndr_pull_struct_blob(&in,
1187 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
1188 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1192 sid_size = ndr_size_dom_sid(sid, 0);
1193 if (sid_size > num_rdata) {
1200 /***************************************************************
1201 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1202 ***************************************************************/
1204 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1205 uint32_t dir_data_length,
1206 struct file_info *finfo,
1207 uint32_t *next_offset)
1210 size_t slen = 0, slen2 = 0;
1212 uint32_t _next_offset = 0;
1214 if (dir_data_length < 4) {
1215 return NT_STATUS_INFO_LENGTH_MISMATCH;
1218 _next_offset = IVAL(dir_data, 0);
1220 if (_next_offset > dir_data_length) {
1221 return NT_STATUS_INFO_LENGTH_MISMATCH;
1224 if (_next_offset != 0) {
1225 /* Ensure we only read what in this record. */
1226 dir_data_length = _next_offset;
1229 if (dir_data_length < 92) {
1230 return NT_STATUS_INFO_LENGTH_MISMATCH;
1233 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1234 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1235 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1236 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1237 finfo->allocated_size = PULL_LE_U64(dir_data, 40);
1238 finfo->size = PULL_LE_U64(dir_data, 48);
1239 finfo->mode = PULL_LE_U32(dir_data, 56);
1240 finfo->ino = PULL_LE_U64(dir_data, 60);
1241 finfo->st_ex_dev = PULL_LE_U32(dir_data, 68);
1242 finfo->st_ex_nlink = PULL_LE_U32(dir_data, 76);
1243 finfo->reparse_tag = PULL_LE_U32(dir_data, 80);
1244 finfo->st_ex_mode = wire_perms_to_unix(PULL_LE_U32(dir_data, 84));
1246 slen = sid_parse_wire(finfo, dir_data+88, &finfo->owner_sid,
1247 dir_data_length-88);
1249 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1251 slen2 = sid_parse_wire(finfo, dir_data+88+slen, &finfo->group_sid,
1252 dir_data_length-88-slen);
1254 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1258 namelen = PULL_LE_U32(dir_data, 88+slen);
1259 ret = pull_string_talloc(finfo,
1261 FLAGS2_UNICODE_STRINGS,
1266 if (ret == (size_t)-1) {
1267 /* Bad conversion. */
1268 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1271 if (finfo->name == NULL) {
1272 /* Bad conversion. */
1273 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1276 *next_offset = _next_offset;
1277 return NT_STATUS_OK;
1280 /***************************************************************
1281 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1282 ***************************************************************/
1284 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1285 uint32_t dir_data_length,
1286 struct file_info *finfo,
1287 uint32_t *next_offset)
1293 if (dir_data_length < 4) {
1294 return NT_STATUS_INFO_LENGTH_MISMATCH;
1297 *next_offset = IVAL(dir_data, 0);
1299 if (*next_offset > dir_data_length) {
1300 return NT_STATUS_INFO_LENGTH_MISMATCH;
1303 if (*next_offset != 0) {
1304 /* Ensure we only read what in this record. */
1305 dir_data_length = *next_offset;
1308 if (dir_data_length < 105) {
1309 return NT_STATUS_INFO_LENGTH_MISMATCH;
1312 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1313 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1314 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1315 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1316 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1317 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1318 finfo->attr = IVAL(dir_data + 56, 0);
1319 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1320 namelen = IVAL(dir_data + 60,0);
1321 if (namelen > (dir_data_length - 104)) {
1322 return NT_STATUS_INFO_LENGTH_MISMATCH;
1324 slen = CVAL(dir_data + 68, 0);
1326 return NT_STATUS_INFO_LENGTH_MISMATCH;
1328 ret = pull_string_talloc(finfo,
1330 FLAGS2_UNICODE_STRINGS,
1335 if (ret == (size_t)-1) {
1336 /* Bad conversion. */
1337 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1340 ret = pull_string_talloc(finfo,
1342 FLAGS2_UNICODE_STRINGS,
1347 if (ret == (size_t)-1) {
1348 /* Bad conversion. */
1349 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1352 if (finfo->name == NULL) {
1353 /* Bad conversion. */
1354 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1357 return NT_STATUS_OK;
1360 /*******************************************************************
1361 Given a filename - get its directory name
1362 ********************************************************************/
1364 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1372 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1375 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1386 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1389 (*parent)[len] = '\0';
1397 struct cli_smb2_list_dir_data {
1402 struct cli_smb2_list_state {
1403 struct tevent_context *ev;
1404 struct cli_state *cli;
1410 struct cli_smb2_list_dir_data *response;
1412 unsigned int info_level;
1415 static void cli_smb2_list_opened(struct tevent_req *subreq);
1416 static void cli_smb2_list_done(struct tevent_req *subreq);
1417 static void cli_smb2_list_closed(struct tevent_req *subreq);
1419 struct tevent_req *cli_smb2_list_send(
1420 TALLOC_CTX *mem_ctx,
1421 struct tevent_context *ev,
1422 struct cli_state *cli,
1423 const char *pathname,
1424 unsigned int info_level,
1427 struct tevent_req *req = NULL, *subreq = NULL;
1428 struct cli_smb2_list_state *state = NULL;
1429 char *parent = NULL;
1431 struct smb2_create_blobs *in_cblobs = NULL;
1433 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1439 state->status = NT_STATUS_OK;
1440 state->info_level = info_level;
1442 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1444 tevent_req_oom(req);
1445 return tevent_req_post(req, ev);
1448 if (smbXcli_conn_have_posix(cli->conn) && posix) {
1451 /* The mode MUST be 0 when opening an existing file/dir, and
1452 * will be ignored by the server.
1454 uint8_t linear_mode[4] = { 0 };
1455 DATA_BLOB blob = { .data=linear_mode,
1456 .length=sizeof(linear_mode) };
1458 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1459 if (in_cblobs == NULL) {
1463 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1464 SMB2_CREATE_TAG_POSIX, blob);
1465 if (!NT_STATUS_IS_OK(status)) {
1466 tevent_req_nterror(req, status);
1467 return tevent_req_post(req, ev);
1471 subreq = cli_smb2_create_fnum_send(
1472 state, /* mem_ctx */
1476 (struct cli_smb2_create_flags){0}, /* create_flags */
1477 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1478 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1479 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1480 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1481 FILE_OPEN, /* create_disposition */
1482 FILE_DIRECTORY_FILE, /* create_options */
1483 in_cblobs); /* in_cblobs */
1484 TALLOC_FREE(in_cblobs);
1485 if (tevent_req_nomem(subreq, req)) {
1486 return tevent_req_post(req, ev);
1488 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1492 static void cli_smb2_list_opened(struct tevent_req *subreq)
1494 struct tevent_req *req = tevent_req_callback_data(
1495 subreq, struct tevent_req);
1496 struct cli_smb2_list_state *state = tevent_req_data(
1497 req, struct cli_smb2_list_state);
1500 status = cli_smb2_create_fnum_recv(
1501 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1502 TALLOC_FREE(subreq);
1503 if (tevent_req_nterror(req, status)) {
1508 * Make our caller get back to us via cli_smb2_list_recv(),
1509 * triggering the smb2_query_directory_send()
1511 tevent_req_defer_callback(req, state->ev);
1512 tevent_req_notify_callback(req);
1515 static void cli_smb2_list_done(struct tevent_req *subreq)
1517 struct tevent_req *req = tevent_req_callback_data(
1518 subreq, struct tevent_req);
1519 struct cli_smb2_list_state *state = tevent_req_data(
1520 req, struct cli_smb2_list_state);
1521 struct cli_smb2_list_dir_data *response = NULL;
1523 response = talloc(state, struct cli_smb2_list_dir_data);
1524 if (tevent_req_nomem(response, req)) {
1528 state->status = smb2cli_query_directory_recv(
1529 subreq, response, &response->data, &response->length);
1530 TALLOC_FREE(subreq);
1532 if (NT_STATUS_IS_OK(state->status)) {
1533 state->response = response;
1536 tevent_req_defer_callback(req, state->ev);
1537 tevent_req_notify_callback(req);
1541 TALLOC_FREE(response);
1543 subreq = cli_smb2_close_fnum_send(
1544 state, state->ev, state->cli, state->fnum);
1545 if (tevent_req_nomem(subreq, req)) {
1548 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1551 static void cli_smb2_list_closed(struct tevent_req *subreq)
1553 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1554 tevent_req_simple_finish_ntstatus(subreq, status);
1558 * Return the next finfo directory.
1560 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1561 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1562 * NT_STATUS_RETRY, which will then trigger the caller again when the
1563 * QUERY_DIRECTORY has returned with another buffer. This way we
1564 * guarantee that no asynchronous request is open after this call
1565 * returns an entry, so that other synchronous requests can be issued
1566 * on the same connection while the directory listing proceeds.
1568 NTSTATUS cli_smb2_list_recv(
1569 struct tevent_req *req,
1570 TALLOC_CTX *mem_ctx,
1571 struct file_info **pfinfo)
1573 struct cli_smb2_list_state *state = tevent_req_data(
1574 req, struct cli_smb2_list_state);
1575 struct cli_smb2_list_dir_data *response = NULL;
1576 struct file_info *finfo = NULL;
1578 uint32_t next_offset = 0;
1581 in_progress = tevent_req_is_in_progress(req);
1584 if (!tevent_req_is_nterror(req, &status)) {
1585 status = NT_STATUS_NO_MORE_FILES;
1590 response = state->response;
1591 if (response == NULL) {
1592 struct tevent_req *subreq = NULL;
1593 struct cli_state *cli = state->cli;
1594 struct smb2_hnd *ph = NULL;
1595 uint32_t max_trans, max_avail_len;
1598 if (!NT_STATUS_IS_OK(state->status)) {
1599 status = state->status;
1603 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1604 if (!NT_STATUS_IS_OK(status)) {
1608 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1609 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1611 max_trans = MIN(max_trans, max_avail_len);
1614 subreq = smb2cli_query_directory_send(
1615 state, /* mem_ctx */
1617 cli->conn, /* conn */
1618 cli->timeout, /* timeout_msec */
1619 cli->smb2.session, /* session */
1620 cli->smb2.tcon, /* tcon */
1621 state->info_level, /* level */
1624 ph->fid_persistent, /* fid_persistent */
1625 ph->fid_volatile, /* fid_volatile */
1626 state->mask, /* mask */
1627 max_trans); /* outbuf_len */
1628 if (subreq == NULL) {
1629 status = NT_STATUS_NO_MEMORY;
1632 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1633 return NT_STATUS_RETRY;
1636 SMB_ASSERT(response->length > state->offset);
1638 finfo = talloc_zero(mem_ctx, struct file_info);
1639 if (finfo == NULL) {
1640 status = NT_STATUS_NO_MEMORY;
1644 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1645 status = parse_finfo_posix_info(
1646 response->data + state->offset,
1647 response->length - state->offset,
1651 status = parse_finfo_id_both_directory_info(
1652 response->data + state->offset,
1653 response->length - state->offset,
1657 if (!NT_STATUS_IS_OK(status)) {
1661 status = is_bad_finfo_name(state->cli, finfo);
1662 if (!NT_STATUS_IS_OK(status)) {
1667 * parse_finfo_id_both_directory_info() checks for overflow,
1668 * no need to check again here.
1670 state->offset += next_offset;
1672 if (next_offset == 0) {
1673 TALLOC_FREE(state->response);
1676 tevent_req_defer_callback(req, state->ev);
1677 tevent_req_notify_callback(req);
1680 return NT_STATUS_OK;
1684 tevent_req_received(req);
1688 /***************************************************************
1689 Wrapper that allows SMB2 to query a path info (basic level).
1691 ***************************************************************/
1693 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1695 SMB_STRUCT_STAT *sbuf,
1696 uint32_t *attributes)
1699 struct smb_create_returns cr;
1700 uint16_t fnum = 0xffff;
1701 size_t namelen = strlen(name);
1703 if (smbXcli_conn_has_async_calls(cli->conn)) {
1705 * Can't use sync call while an async call is in flight
1707 return NT_STATUS_INVALID_PARAMETER;
1710 /* SMB2 is pickier about pathnames. Ensure it doesn't
1712 if (namelen > 0 && name[namelen-1] == '\\') {
1713 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1714 if (modname == NULL) {
1715 return NT_STATUS_NO_MEMORY;
1720 /* This is commonly used as a 'cd'. Try qpathinfo on
1721 a directory handle first. */
1723 status = cli_smb2_create_fnum(cli,
1725 (struct cli_smb2_create_flags){0},
1726 SMB2_IMPERSONATION_IMPERSONATION,
1727 FILE_READ_ATTRIBUTES, /* desired_access */
1728 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1729 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1730 FILE_OPEN, /* create_disposition */
1731 FILE_DIRECTORY_FILE, /* create_options */
1738 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1739 /* Maybe a file ? */
1740 status = cli_smb2_create_fnum(cli,
1742 (struct cli_smb2_create_flags){0},
1743 SMB2_IMPERSONATION_IMPERSONATION,
1744 FILE_READ_ATTRIBUTES, /* desired_access */
1745 0, /* file attributes */
1746 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1747 FILE_OPEN, /* create_disposition */
1748 0, /* create_options */
1756 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1757 /* Maybe a reparse point ? */
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 0, /* file attributes */
1764 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1765 FILE_OPEN, /* create_disposition */
1766 FILE_OPEN_REPARSE_POINT, /* create_options */
1774 if (!NT_STATUS_IS_OK(status)) {
1778 status = cli_smb2_close_fnum(cli, fnum);
1782 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1783 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1784 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1785 sbuf->st_ex_size = cr.end_of_file;
1786 *attributes = cr.file_attributes;
1791 struct cli_smb2_query_info_fnum_state {
1795 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1797 struct tevent_req *cli_smb2_query_info_fnum_send(
1798 TALLOC_CTX *mem_ctx,
1799 struct tevent_context *ev,
1800 struct cli_state *cli,
1802 uint8_t in_info_type,
1803 uint8_t in_info_class,
1804 uint32_t in_max_output_length,
1805 const DATA_BLOB *in_input_buffer,
1806 uint32_t in_additional_info,
1809 struct tevent_req *req = NULL, *subreq = NULL;
1810 struct cli_smb2_query_info_fnum_state *state = NULL;
1811 struct smb2_hnd *ph = NULL;
1814 req = tevent_req_create(
1815 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1820 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1821 if (tevent_req_nterror(req, status)) {
1822 return tevent_req_post(req, ev);
1825 subreq = smb2cli_query_info_send(
1834 in_max_output_length,
1840 if (tevent_req_nomem(subreq, req)) {
1841 return tevent_req_post(req, ev);
1843 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1847 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1849 struct tevent_req *req = tevent_req_callback_data(
1850 subreq, struct tevent_req);
1851 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1852 req, struct cli_smb2_query_info_fnum_state);
1856 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1857 TALLOC_FREE(subreq);
1858 if (tevent_req_nterror(req, status)) {
1863 * We have to dup the memory here because outbuf.data is not
1864 * returned as a talloc object by smb2cli_query_info_recv.
1865 * It's a pointer into the received buffer.
1867 state->outbuf = data_blob_dup_talloc(state, outbuf);
1869 if ((outbuf.length != 0) &&
1870 tevent_req_nomem(state->outbuf.data, req)) {
1873 tevent_req_done(req);
1876 NTSTATUS cli_smb2_query_info_fnum_recv(
1877 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1879 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1880 req, struct cli_smb2_query_info_fnum_state);
1883 if (tevent_req_is_nterror(req, &status)) {
1886 *outbuf = (DATA_BLOB) {
1887 .data = talloc_move(mem_ctx, &state->outbuf.data),
1888 .length = state->outbuf.length,
1890 return NT_STATUS_OK;
1893 NTSTATUS cli_smb2_query_info_fnum(
1894 struct cli_state *cli,
1896 uint8_t in_info_type,
1897 uint8_t in_info_class,
1898 uint32_t in_max_output_length,
1899 const DATA_BLOB *in_input_buffer,
1900 uint32_t in_additional_info,
1902 TALLOC_CTX *mem_ctx,
1905 TALLOC_CTX *frame = talloc_stackframe();
1906 struct tevent_context *ev = NULL;
1907 struct tevent_req *req = NULL;
1908 NTSTATUS status = NT_STATUS_NO_MEMORY;
1911 if (smbXcli_conn_has_async_calls(cli->conn)) {
1913 * Can't use sync call while an async call is in flight
1915 status = NT_STATUS_INVALID_PARAMETER;
1918 ev = samba_tevent_context_init(frame);
1922 req = cli_smb2_query_info_fnum_send(
1929 in_max_output_length,
1936 ok = tevent_req_poll_ntstatus(req, ev, &status);
1940 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1946 /***************************************************************
1947 Helper function for pathname operations.
1948 ***************************************************************/
1950 struct get_fnum_from_path_state {
1951 struct tevent_context *ev;
1952 struct cli_state *cli;
1954 uint32_t desired_access;
1958 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1959 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1960 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1962 static struct tevent_req *get_fnum_from_path_send(
1963 TALLOC_CTX *mem_ctx,
1964 struct tevent_context *ev,
1965 struct cli_state *cli,
1967 uint32_t desired_access)
1969 struct tevent_req *req = NULL, *subreq = NULL;
1970 struct get_fnum_from_path_state *state = NULL;
1971 size_t namelen = strlen(name);
1973 req = tevent_req_create(
1974 mem_ctx, &state, struct get_fnum_from_path_state);
1981 state->desired_access = desired_access;
1984 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
1987 if (namelen > 0 && name[namelen-1] == '\\') {
1988 state->name = talloc_strndup(state, name, namelen-1);
1989 if (tevent_req_nomem(state->name, req)) {
1990 return tevent_req_post(req, ev);
1994 subreq = cli_smb2_create_fnum_send(
1995 state, /* mem_ctx, */
1998 state->name, /* fname */
1999 (struct cli_smb2_create_flags){0}, /* create_flags */
2000 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2001 desired_access, /* desired_access */
2002 0, /* file_attributes */
2005 FILE_SHARE_DELETE, /* share_access */
2006 FILE_OPEN, /* create_disposition */
2007 0, /* create_options */
2008 NULL); /* in_cblobs */
2009 if (tevent_req_nomem(subreq, req)) {
2010 return tevent_req_post(req, ev);
2012 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2016 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2018 struct tevent_req *req = tevent_req_callback_data(
2019 subreq, struct tevent_req);
2020 struct get_fnum_from_path_state *state = tevent_req_data(
2021 req, struct get_fnum_from_path_state);
2024 status = cli_smb2_create_fnum_recv(
2025 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2026 TALLOC_FREE(subreq);
2028 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2029 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2031 * Naive option to match our SMB1 code. Assume the
2032 * symlink path that tripped us up was the last
2033 * component and try again. Eventually we will have to
2034 * deal with the returned path unprocessed component. JRA.
2036 subreq = cli_smb2_create_fnum_send(
2037 state, /* mem_ctx, */
2039 state->cli, /* cli */
2040 state->name, /* fname */
2041 (struct cli_smb2_create_flags){0}, /* create_flags */
2042 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2043 state->desired_access, /* desired_access */
2044 0, /* file_attributes */
2047 FILE_SHARE_DELETE, /* share_access */
2048 FILE_OPEN, /* create_disposition */
2049 FILE_OPEN_REPARSE_POINT, /* create_options */
2050 NULL); /* in_cblobs */
2051 if (tevent_req_nomem(subreq, req)) {
2054 tevent_req_set_callback(
2055 subreq, get_fnum_from_path_opened_reparse, req);
2059 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2060 subreq = cli_smb2_create_fnum_send(
2061 state, /* mem_ctx, */
2063 state->cli, /* cli */
2064 state->name, /* fname */
2065 (struct cli_smb2_create_flags){0}, /* create_flags */
2066 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2067 state->desired_access, /* desired_access */
2068 0, /* file_attributes */
2071 FILE_SHARE_DELETE, /* share_access */
2072 FILE_OPEN, /* create_disposition */
2073 FILE_DIRECTORY_FILE, /* create_options */
2074 NULL); /* in_cblobs */
2075 if (tevent_req_nomem(subreq, req)) {
2078 tevent_req_set_callback(
2079 subreq, get_fnum_from_path_opened_dir, req);
2083 if (tevent_req_nterror(req, status)) {
2086 tevent_req_done(req);
2089 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2091 struct tevent_req *req = tevent_req_callback_data(
2092 subreq, struct tevent_req);
2093 struct get_fnum_from_path_state *state = tevent_req_data(
2094 req, struct get_fnum_from_path_state);
2095 NTSTATUS status = cli_smb2_create_fnum_recv(
2096 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2097 tevent_req_simple_finish_ntstatus(subreq, status);
2100 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2102 /* Abstraction violation, but these two are just the same... */
2103 get_fnum_from_path_opened_reparse(subreq);
2106 static NTSTATUS get_fnum_from_path_recv(
2107 struct tevent_req *req, uint16_t *pfnum)
2109 struct get_fnum_from_path_state *state = tevent_req_data(
2110 req, struct get_fnum_from_path_state);
2111 NTSTATUS status = NT_STATUS_OK;
2113 if (!tevent_req_is_nterror(req, &status)) {
2114 *pfnum = state->fnum;
2116 tevent_req_received(req);
2120 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2122 uint32_t desired_access,
2125 TALLOC_CTX *frame = talloc_stackframe();
2126 struct tevent_context *ev = NULL;
2127 struct tevent_req *req = NULL;
2128 NTSTATUS status = NT_STATUS_NO_MEMORY;
2130 if (smbXcli_conn_has_async_calls(cli->conn)) {
2131 status = NT_STATUS_INVALID_PARAMETER;
2134 ev = samba_tevent_context_init(frame);
2138 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2142 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2145 status = get_fnum_from_path_recv(req, pfnum);
2151 /***************************************************************
2152 Wrapper that allows SMB2 to query a path info (ALTNAME level).
2154 ***************************************************************/
2156 NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
2161 DATA_BLOB outbuf = data_blob_null;
2162 uint16_t fnum = 0xffff;
2163 uint32_t altnamelen = 0;
2164 TALLOC_CTX *frame = talloc_stackframe();
2166 if (smbXcli_conn_has_async_calls(cli->conn)) {
2168 * Can't use sync call while an async call is in flight
2170 status = NT_STATUS_INVALID_PARAMETER;
2174 status = get_fnum_from_path(cli,
2176 FILE_READ_ATTRIBUTES,
2179 if (!NT_STATUS_IS_OK(status)) {
2183 status = cli_smb2_query_info_fnum(
2186 1, /* in_info_type */
2187 (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
2188 0xFFFF, /* in_max_output_length */
2189 NULL, /* in_input_buffer */
2190 0, /* in_additional_info */
2195 if (!NT_STATUS_IS_OK(status)) {
2199 /* Parse the reply. */
2200 if (outbuf.length < 4) {
2201 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2205 altnamelen = IVAL(outbuf.data, 0);
2206 if (altnamelen > outbuf.length - 4) {
2207 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2211 if (altnamelen > 0) {
2213 char *short_name = NULL;
2214 ret = pull_string_talloc(frame,
2216 FLAGS2_UNICODE_STRINGS,
2221 if (ret == (size_t)-1) {
2222 /* Bad conversion. */
2223 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2227 fstrcpy(alt_name, short_name);
2232 status = NT_STATUS_OK;
2236 if (fnum != 0xffff) {
2237 cli_smb2_close_fnum(cli, fnum);
2240 cli->raw_status = status;
2246 struct cli_smb2_qpathinfo_state {
2247 struct tevent_context *ev;
2248 struct cli_state *cli;
2259 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2260 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2261 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2263 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2264 struct tevent_context *ev,
2265 struct cli_state *cli,
2271 struct tevent_req *req = NULL, *subreq = NULL;
2272 struct cli_smb2_qpathinfo_state *state = NULL;
2274 req = tevent_req_create(mem_ctx,
2276 struct cli_smb2_qpathinfo_state);
2282 state->level = level;
2283 state->min_rdata = min_rdata;
2284 state->max_rdata = max_rdata;
2286 subreq = get_fnum_from_path_send(state,
2290 FILE_READ_ATTRIBUTES);
2291 if (tevent_req_nomem(subreq, req)) {
2292 return tevent_req_post(req, ev);
2294 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2298 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2300 struct tevent_req *req =
2301 tevent_req_callback_data(subreq, struct tevent_req);
2302 struct cli_smb2_qpathinfo_state *state =
2303 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2306 status = get_fnum_from_path_recv(subreq, &state->fnum);
2307 TALLOC_FREE(subreq);
2308 if (tevent_req_nterror(req, status)) {
2312 subreq = cli_smb2_query_info_fnum_send(state,
2316 1, /* in_info_type */
2319 NULL, /* in_input_buffer */
2320 0, /* in_additional_info */
2322 if (tevent_req_nomem(subreq, req)) {
2325 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2328 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2330 struct tevent_req *req =
2331 tevent_req_callback_data(subreq, struct tevent_req);
2332 struct cli_smb2_qpathinfo_state *state =
2333 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2336 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2337 TALLOC_FREE(subreq);
2339 if (NT_STATUS_IS_OK(state->status) &&
2340 (state->out.length < state->min_rdata)) {
2341 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2344 subreq = cli_smb2_close_fnum_send(state,
2348 if (tevent_req_nomem(subreq, req)) {
2351 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2354 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2356 struct tevent_req *req =
2357 tevent_req_callback_data(subreq, struct tevent_req);
2358 struct cli_smb2_qpathinfo_state *state =
2359 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2362 status = cli_smb2_close_fnum_recv(subreq);
2363 TALLOC_FREE(subreq);
2364 if (tevent_req_nterror(req, status)) {
2367 if (tevent_req_nterror(req, state->status)) {
2370 tevent_req_done(req);
2373 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2374 TALLOC_CTX *mem_ctx,
2376 uint32_t *num_rdata)
2378 struct cli_smb2_qpathinfo_state *state =
2379 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2382 if (tevent_req_is_nterror(req, &status)) {
2386 *rdata = talloc_move(mem_ctx, &state->out.data);
2387 *num_rdata = state->out.length;
2388 tevent_req_received(req);
2389 return NT_STATUS_OK;
2392 /***************************************************************
2393 Wrapper that allows SMB2 to get pathname attributes.
2395 ***************************************************************/
2397 NTSTATUS cli_smb2_getatr(struct cli_state *cli,
2404 uint16_t fnum = 0xffff;
2405 struct timespec write_time_ts;
2406 TALLOC_CTX *frame = talloc_stackframe();
2408 if (smbXcli_conn_has_async_calls(cli->conn)) {
2410 * Can't use sync call while an async call is in flight
2412 status = NT_STATUS_INVALID_PARAMETER;
2416 status = get_fnum_from_path(cli,
2418 FILE_READ_ATTRIBUTES,
2421 if (!NT_STATUS_IS_OK(status)) {
2425 status = cli_qfileinfo_basic(
2430 NULL, /* create_time */
2431 NULL, /* access_time */
2433 NULL, /* change_time */
2435 if (!NT_STATUS_IS_OK(status)) {
2438 if (write_time != NULL) {
2439 *write_time = write_time_ts.tv_sec;
2444 if (fnum != 0xffff) {
2445 cli_smb2_close_fnum(cli, fnum);
2448 cli->raw_status = status;
2454 struct cli_smb2_qpathinfo2_state {
2455 struct tevent_context *ev;
2456 struct cli_state *cli;
2459 NTSTATUS queryinfo_status;
2460 struct timespec create_time;
2461 struct timespec access_time;
2462 struct timespec write_time;
2463 struct timespec change_time;
2469 static void cli_smb2_qpathinfo2_opened(struct tevent_req *subreq);
2470 static void cli_smb2_qpathinfo2_done(struct tevent_req *subreq);
2471 static void cli_smb2_qpathinfo2_closed(struct tevent_req *subreq);
2473 struct tevent_req *cli_smb2_qpathinfo2_send(TALLOC_CTX *mem_ctx,
2474 struct tevent_context *ev,
2475 struct cli_state *cli,
2478 struct tevent_req *req = NULL, *subreq = NULL;
2479 struct cli_smb2_qpathinfo2_state *state = NULL;
2481 req = tevent_req_create(mem_ctx,
2483 struct cli_smb2_qpathinfo2_state);
2490 subreq = get_fnum_from_path_send(mem_ctx,
2494 FILE_READ_ATTRIBUTES);
2495 if (tevent_req_nomem(subreq, req)) {
2496 return tevent_req_post(req, ev);
2498 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_opened, req);
2502 static void cli_smb2_qpathinfo2_opened(struct tevent_req *subreq)
2504 struct tevent_req *req =
2505 tevent_req_callback_data(subreq, struct tevent_req);
2506 struct cli_smb2_qpathinfo2_state *state =
2507 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2510 status = get_fnum_from_path_recv(subreq, &state->fnum);
2511 TALLOC_FREE(subreq);
2512 if (tevent_req_nterror(req, status)) {
2516 subreq = cli_qfileinfo_basic_send(state,
2520 if (tevent_req_nomem(subreq, req)) {
2523 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_done, req);
2526 static void cli_smb2_qpathinfo2_done(struct tevent_req *subreq)
2528 struct tevent_req *req =
2529 tevent_req_callback_data(subreq, struct tevent_req);
2530 struct cli_smb2_qpathinfo2_state *state =
2531 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2533 state->queryinfo_status = cli_qfileinfo_basic_recv(subreq,
2536 &state->create_time,
2537 &state->access_time,
2539 &state->change_time,
2541 TALLOC_FREE(subreq);
2543 subreq = cli_smb2_close_fnum_send(state,
2547 if (tevent_req_nomem(subreq, req)) {
2550 tevent_req_set_callback(subreq, cli_smb2_qpathinfo2_closed, req);
2553 static void cli_smb2_qpathinfo2_closed(struct tevent_req *subreq)
2555 struct tevent_req *req =
2556 tevent_req_callback_data(subreq, struct tevent_req);
2557 struct cli_smb2_qpathinfo2_state *state =
2558 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2561 status = cli_smb2_close_fnum_recv(subreq);
2562 TALLOC_FREE(subreq);
2563 if (tevent_req_nterror(req, status)) {
2566 if (tevent_req_nterror(req, state->queryinfo_status)) {
2569 tevent_req_done(req);
2572 NTSTATUS cli_smb2_qpathinfo2_recv(struct tevent_req *req,
2573 struct timespec *create_time,
2574 struct timespec *access_time,
2575 struct timespec *write_time,
2576 struct timespec *change_time,
2581 struct cli_smb2_qpathinfo2_state *state =
2582 tevent_req_data(req, struct cli_smb2_qpathinfo2_state);
2585 if (tevent_req_is_nterror(req, &status)) {
2589 if (create_time != NULL) {
2590 *create_time = state->create_time;
2592 if (access_time != NULL) {
2593 *access_time = state->access_time;
2595 if (write_time != NULL) {
2596 *write_time = state->write_time;
2598 if (change_time != NULL) {
2599 *change_time = state->change_time;
2602 *attr = state->attr;
2605 *size = state->size;
2611 return NT_STATUS_OK;
2614 /***************************************************************
2615 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2618 ***************************************************************/
2620 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2622 uint8_t in_info_type,
2623 uint8_t in_file_info_class,
2624 const DATA_BLOB *p_in_data)
2627 uint16_t fnum = 0xffff;
2628 TALLOC_CTX *frame = talloc_stackframe();
2630 if (smbXcli_conn_has_async_calls(cli->conn)) {
2632 * Can't use sync call while an async call is in flight
2634 status = NT_STATUS_INVALID_PARAMETER;
2638 status = get_fnum_from_path(cli,
2640 FILE_WRITE_ATTRIBUTES,
2643 if (!NT_STATUS_IS_OK(status)) {
2647 status = cli_smb2_set_info_fnum(
2652 p_in_data, /* in_input_buffer */
2653 0); /* in_additional_info */
2656 if (fnum != 0xffff) {
2657 cli_smb2_close_fnum(cli, fnum);
2660 cli->raw_status = status;
2667 /***************************************************************
2668 Wrapper that allows SMB2 to set pathname attributes.
2670 ***************************************************************/
2672 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2677 uint8_t inbuf_store[40];
2678 DATA_BLOB inbuf = data_blob_null;
2680 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2681 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2683 inbuf.data = inbuf_store;
2684 inbuf.length = sizeof(inbuf_store);
2685 data_blob_clear(&inbuf);
2688 * SMB1 uses attr == 0 to clear all attributes
2689 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2690 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2691 * request attribute change.
2693 * SMB2 uses exactly the reverse. Unfortunately as the
2694 * cli_setatr() ABI is exposed inside libsmbclient,
2695 * we must make the SMB2 cli_smb2_setatr() call
2696 * export the same ABI as the SMB1 cli_setatr()
2697 * which calls it. This means reversing the sense
2698 * of the requested attr argument if it's zero
2699 * or FILE_ATTRIBUTE_NORMAL.
2701 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2705 attr = FILE_ATTRIBUTE_NORMAL;
2706 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2710 SIVAL(inbuf.data, 32, attr);
2712 put_long_date((char *)inbuf.data + 16,mtime);
2714 /* Set all the other times to -1. */
2715 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2716 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2717 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2719 return cli_smb2_setpathinfo(cli,
2721 1, /* in_info_type */
2722 /* in_file_info_class */
2723 SMB_FILE_BASIC_INFORMATION - 1000,
2728 /***************************************************************
2729 Wrapper that allows SMB2 to set file handle times.
2731 ***************************************************************/
2733 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2739 uint8_t inbuf_store[40];
2740 DATA_BLOB inbuf = data_blob_null;
2742 if (smbXcli_conn_has_async_calls(cli->conn)) {
2744 * Can't use sync call while an async call is in flight
2746 return NT_STATUS_INVALID_PARAMETER;
2749 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2750 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2752 inbuf.data = inbuf_store;
2753 inbuf.length = sizeof(inbuf_store);
2754 data_blob_clear(&inbuf);
2756 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2757 if (change_time != 0) {
2758 put_long_date((char *)inbuf.data + 24, change_time);
2760 if (access_time != 0) {
2761 put_long_date((char *)inbuf.data + 8, access_time);
2763 if (write_time != 0) {
2764 put_long_date((char *)inbuf.data + 16, write_time);
2767 cli->raw_status = cli_smb2_set_info_fnum(
2770 1, /* in_info_type */
2771 SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
2772 &inbuf, /* in_input_buffer */
2773 0); /* in_additional_info */
2775 return cli->raw_status;
2778 /***************************************************************
2779 Wrapper that allows SMB2 to query disk attributes (size).
2781 ***************************************************************/
2783 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2784 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2787 uint16_t fnum = 0xffff;
2788 DATA_BLOB outbuf = data_blob_null;
2789 uint32_t sectors_per_unit = 0;
2790 uint32_t bytes_per_sector = 0;
2791 uint64_t total_size = 0;
2792 uint64_t size_free = 0;
2793 TALLOC_CTX *frame = talloc_stackframe();
2795 if (smbXcli_conn_has_async_calls(cli->conn)) {
2797 * Can't use sync call while an async call is in flight
2799 status = NT_STATUS_INVALID_PARAMETER;
2803 /* First open the top level directory. */
2804 status = cli_smb2_create_fnum(cli,
2806 (struct cli_smb2_create_flags){0},
2807 SMB2_IMPERSONATION_IMPERSONATION,
2808 FILE_READ_ATTRIBUTES, /* desired_access */
2809 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2810 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2811 FILE_OPEN, /* create_disposition */
2812 FILE_DIRECTORY_FILE, /* create_options */
2819 if (!NT_STATUS_IS_OK(status)) {
2823 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2824 level 3 (SMB_FS_SIZE_INFORMATION). */
2826 status = cli_smb2_query_info_fnum(
2829 2, /* in_info_type */
2830 3, /* in_file_info_class */
2831 0xFFFF, /* in_max_output_length */
2832 NULL, /* in_input_buffer */
2833 0, /* in_additional_info */
2837 if (!NT_STATUS_IS_OK(status)) {
2841 /* Parse the reply. */
2842 if (outbuf.length != 24) {
2843 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2847 total_size = BVAL(outbuf.data, 0);
2848 size_free = BVAL(outbuf.data, 8);
2849 sectors_per_unit = IVAL(outbuf.data, 16);
2850 bytes_per_sector = IVAL(outbuf.data, 20);
2853 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2856 *total = total_size;
2862 status = NT_STATUS_OK;
2866 if (fnum != 0xffff) {
2867 cli_smb2_close_fnum(cli, fnum);
2870 cli->raw_status = status;
2876 /***************************************************************
2877 Wrapper that allows SMB2 to query file system sizes.
2879 ***************************************************************/
2881 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2882 uint64_t *total_allocation_units,
2883 uint64_t *caller_allocation_units,
2884 uint64_t *actual_allocation_units,
2885 uint64_t *sectors_per_allocation_unit,
2886 uint64_t *bytes_per_sector)
2889 uint16_t fnum = 0xffff;
2890 DATA_BLOB outbuf = data_blob_null;
2891 TALLOC_CTX *frame = talloc_stackframe();
2893 if (smbXcli_conn_has_async_calls(cli->conn)) {
2895 * Can't use sync call while an async call is in flight
2897 status = NT_STATUS_INVALID_PARAMETER;
2901 /* First open the top level directory. */
2903 cli_smb2_create_fnum(cli, "",
2904 (struct cli_smb2_create_flags){0},
2905 SMB2_IMPERSONATION_IMPERSONATION,
2906 FILE_READ_ATTRIBUTES, /* desired_access */
2907 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2908 FILE_SHARE_READ | FILE_SHARE_WRITE |
2909 FILE_SHARE_DELETE, /* share_access */
2910 FILE_OPEN, /* create_disposition */
2911 FILE_DIRECTORY_FILE, /* create_options */
2918 if (!NT_STATUS_IS_OK(status)) {
2922 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2923 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2925 status = cli_smb2_query_info_fnum(
2928 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2929 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2930 0xFFFF, /* in_max_output_length */
2931 NULL, /* in_input_buffer */
2932 0, /* in_additional_info */
2936 if (!NT_STATUS_IS_OK(status)) {
2940 if (outbuf.length < 32) {
2941 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2945 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2946 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2947 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2948 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2949 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2953 if (fnum != 0xffff) {
2954 cli_smb2_close_fnum(cli, fnum);
2957 cli->raw_status = status;
2963 /***************************************************************
2964 Wrapper that allows SMB2 to query file system attributes.
2966 ***************************************************************/
2968 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2971 uint16_t fnum = 0xffff;
2972 DATA_BLOB outbuf = data_blob_null;
2973 TALLOC_CTX *frame = talloc_stackframe();
2975 if (smbXcli_conn_has_async_calls(cli->conn)) {
2977 * Can't use sync call while an async call is in flight
2979 status = NT_STATUS_INVALID_PARAMETER;
2983 /* First open the top level directory. */
2985 cli_smb2_create_fnum(cli, "",
2986 (struct cli_smb2_create_flags){0},
2987 SMB2_IMPERSONATION_IMPERSONATION,
2988 FILE_READ_ATTRIBUTES, /* desired_access */
2989 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2990 FILE_SHARE_READ | FILE_SHARE_WRITE |
2991 FILE_SHARE_DELETE, /* share_access */
2992 FILE_OPEN, /* create_disposition */
2993 FILE_DIRECTORY_FILE, /* create_options */
3000 if (!NT_STATUS_IS_OK(status)) {
3004 status = cli_smb2_query_info_fnum(
3007 2, /* in_info_type */
3008 5, /* in_file_info_class */
3009 0xFFFF, /* in_max_output_length */
3010 NULL, /* in_input_buffer */
3011 0, /* in_additional_info */
3015 if (!NT_STATUS_IS_OK(status)) {
3019 if (outbuf.length < 12) {
3020 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3024 *fs_attr = IVAL(outbuf.data, 0);
3028 if (fnum != 0xffff) {
3029 cli_smb2_close_fnum(cli, fnum);
3032 cli->raw_status = status;
3038 /***************************************************************
3039 Wrapper that allows SMB2 to query file system volume info.
3041 ***************************************************************/
3043 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
3044 TALLOC_CTX *mem_ctx,
3045 char **_volume_name,
3046 uint32_t *pserial_number,
3050 uint16_t fnum = 0xffff;
3051 DATA_BLOB outbuf = data_blob_null;
3053 char *volume_name = NULL;
3054 TALLOC_CTX *frame = talloc_stackframe();
3056 if (smbXcli_conn_has_async_calls(cli->conn)) {
3058 * Can't use sync call while an async call is in flight
3060 status = NT_STATUS_INVALID_PARAMETER;
3064 /* First open the top level directory. */
3066 cli_smb2_create_fnum(cli, "",
3067 (struct cli_smb2_create_flags){0},
3068 SMB2_IMPERSONATION_IMPERSONATION,
3069 FILE_READ_ATTRIBUTES, /* desired_access */
3070 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
3071 FILE_SHARE_READ | FILE_SHARE_WRITE |
3072 FILE_SHARE_DELETE, /* share_access */
3073 FILE_OPEN, /* create_disposition */
3074 FILE_DIRECTORY_FILE, /* create_options */
3081 if (!NT_STATUS_IS_OK(status)) {
3085 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
3086 level 1 (SMB_FS_VOLUME_INFORMATION). */
3088 status = cli_smb2_query_info_fnum(
3091 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3092 /* in_file_info_class */
3093 SMB_FS_VOLUME_INFORMATION - 1000,
3094 0xFFFF, /* in_max_output_length */
3095 NULL, /* in_input_buffer */
3096 0, /* in_additional_info */
3100 if (!NT_STATUS_IS_OK(status)) {
3104 if (outbuf.length < 24) {
3105 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3111 ts = interpret_long_date((char *)outbuf.data);
3114 if (pserial_number) {
3115 *pserial_number = IVAL(outbuf.data,8);
3117 nlen = IVAL(outbuf.data,12);
3118 if (nlen + 18 < 18) {
3120 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3124 * The next check is safe as we know outbuf.length >= 24
3127 if (nlen > (outbuf.length - 18)) {
3128 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3132 pull_string_talloc(mem_ctx,
3133 (const char *)outbuf.data,
3139 if (volume_name == NULL) {
3140 status = map_nt_error_from_unix(errno);
3144 *_volume_name = volume_name;
3148 if (fnum != 0xffff) {
3149 cli_smb2_close_fnum(cli, fnum);
3152 cli->raw_status = status;
3158 struct cli_smb2_mxac_state {
3159 struct tevent_context *ev;
3160 struct cli_state *cli;
3162 struct smb2_create_blobs in_cblobs;
3168 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
3169 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
3171 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
3172 struct tevent_context *ev,
3173 struct cli_state *cli,
3176 struct tevent_req *req = NULL, *subreq = NULL;
3177 struct cli_smb2_mxac_state *state = NULL;
3180 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
3184 *state = (struct cli_smb2_mxac_state) {
3190 status = smb2_create_blob_add(state,
3192 SMB2_CREATE_TAG_MXAC,
3193 data_blob(NULL, 0));
3194 if (tevent_req_nterror(req, status)) {
3195 return tevent_req_post(req, ev);
3198 subreq = cli_smb2_create_fnum_send(
3203 (struct cli_smb2_create_flags){0},
3204 SMB2_IMPERSONATION_IMPERSONATION,
3205 FILE_READ_ATTRIBUTES,
3206 0, /* file attributes */
3207 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
3209 0, /* create_options */
3211 if (tevent_req_nomem(subreq, req)) {
3212 return tevent_req_post(req, ev);
3214 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
3218 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
3220 struct tevent_req *req = tevent_req_callback_data(
3221 subreq, struct tevent_req);
3222 struct cli_smb2_mxac_state *state = tevent_req_data(
3223 req, struct cli_smb2_mxac_state);
3224 struct smb2_create_blobs out_cblobs = {0};
3225 struct smb2_create_blob *mxac_blob = NULL;
3228 status = cli_smb2_create_fnum_recv(
3229 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
3230 TALLOC_FREE(subreq);
3232 if (tevent_req_nterror(req, status)) {
3236 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
3237 if (mxac_blob == NULL) {
3238 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3241 if (mxac_blob->data.length != 8) {
3242 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3246 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
3247 state->mxac = IVAL(mxac_blob->data.data, 4);
3250 subreq = cli_smb2_close_fnum_send(
3251 state, state->ev, state->cli, state->fnum);
3252 if (tevent_req_nomem(subreq, req)) {
3255 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
3260 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
3262 struct tevent_req *req = tevent_req_callback_data(
3263 subreq, struct tevent_req);
3266 status = cli_smb2_close_fnum_recv(subreq);
3267 if (tevent_req_nterror(req, status)) {
3271 tevent_req_done(req);
3274 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3276 struct cli_smb2_mxac_state *state = tevent_req_data(
3277 req, struct cli_smb2_mxac_state);
3280 if (tevent_req_is_nterror(req, &status)) {
3284 if (!NT_STATUS_IS_OK(state->status)) {
3285 return state->status;
3288 *mxac = state->mxac;
3289 return NT_STATUS_OK;
3292 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3296 TALLOC_CTX *frame = talloc_stackframe();
3297 struct tevent_context *ev = NULL;
3298 struct tevent_req *req = NULL;
3299 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3302 if (smbXcli_conn_has_async_calls(cli->conn)) {
3304 * Can't use sync call while an async call is in flight
3306 status = NT_STATUS_INVALID_PARAMETER;
3310 ev = samba_tevent_context_init(frame);
3314 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3318 ok = tevent_req_poll_ntstatus(req, ev, &status);
3322 status = cli_smb2_query_mxac_recv(req, _mxac);
3325 cli->raw_status = status;
3330 struct cli_smb2_rename_fnum_state {
3334 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3336 static struct tevent_req *cli_smb2_rename_fnum_send(
3337 TALLOC_CTX *mem_ctx,
3338 struct tevent_context *ev,
3339 struct cli_state *cli,
3341 const char *fname_dst,
3344 struct tevent_req *req = NULL, *subreq = NULL;
3345 struct cli_smb2_rename_fnum_state *state = NULL;
3346 size_t namelen = strlen(fname_dst);
3347 smb_ucs2_t *converted_str = NULL;
3348 size_t converted_size_bytes = 0;
3352 req = tevent_req_create(
3353 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3359 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3362 if (*fname_dst == '\\') {
3367 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3370 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3371 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3372 if (tevent_req_nomem(fname_dst, req)) {
3373 return tevent_req_post(req, ev);
3377 ok = push_ucs2_talloc(
3378 state, &converted_str, fname_dst, &converted_size_bytes);
3380 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3381 return tevent_req_post(req, ev);
3385 * W2K8 insists the dest name is not null terminated. Remove
3386 * the last 2 zero bytes and reduce the name length.
3388 if (converted_size_bytes < 2) {
3389 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3390 return tevent_req_post(req, ev);
3392 converted_size_bytes -= 2;
3394 inbuf_size = 20 + converted_size_bytes;
3395 if (inbuf_size < 20) {
3396 /* Integer wrap check. */
3397 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3398 return tevent_req_post(req, ev);
3402 * The Windows 10 SMB2 server has a minimum length
3403 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3404 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3405 * if the length is less. This isn't an alignment
3406 * issue as Windows client accepts happily 2-byte align
3407 * for larger target name sizes. Also the Windows 10
3408 * SMB1 server doesn't have this restriction.
3410 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3412 inbuf_size = MAX(inbuf_size, 24);
3414 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3415 if (tevent_req_nomem(state->inbuf.data, req)) {
3416 return tevent_req_post(req, ev);
3420 SCVAL(state->inbuf.data, 0, 1);
3423 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3424 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3426 TALLOC_FREE(converted_str);
3428 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3429 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3431 subreq = cli_smb2_set_info_fnum_send(
3432 state, /* mem_ctx */
3436 1, /* in_info_type */
3437 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3438 &state->inbuf, /* in_input_buffer */
3439 0); /* in_additional_info */
3440 if (tevent_req_nomem(subreq, req)) {
3441 return tevent_req_post(req, ev);
3443 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3447 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3449 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3450 tevent_req_simple_finish_ntstatus(subreq, status);
3453 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3455 return tevent_req_simple_recv_ntstatus(req);
3458 /***************************************************************
3459 Wrapper that allows SMB2 to rename a file.
3460 ***************************************************************/
3462 struct cli_smb2_rename_state {
3463 struct tevent_context *ev;
3464 struct cli_state *cli;
3465 const char *fname_dst;
3469 NTSTATUS rename_status;
3472 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3473 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3474 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3476 struct tevent_req *cli_smb2_rename_send(
3477 TALLOC_CTX *mem_ctx,
3478 struct tevent_context *ev,
3479 struct cli_state *cli,
3480 const char *fname_src,
3481 const char *fname_dst,
3484 struct tevent_req *req = NULL, *subreq = NULL;
3485 struct cli_smb2_rename_state *state = NULL;
3488 req = tevent_req_create(
3489 mem_ctx, &state, struct cli_smb2_rename_state);
3495 * Strip a MSDFS path from fname_dst if we were given one.
3497 status = cli_dfs_target_check(state,
3501 if (tevent_req_nterror(req, status)) {
3502 return tevent_req_post(req, ev);
3507 state->fname_dst = fname_dst;
3508 state->replace = replace;
3510 subreq = get_fnum_from_path_send(
3511 state, ev, cli, fname_src, DELETE_ACCESS);
3512 if (tevent_req_nomem(subreq, req)) {
3513 return tevent_req_post(req, ev);
3515 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3519 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3521 struct tevent_req *req = tevent_req_callback_data(
3522 subreq, struct tevent_req);
3523 struct cli_smb2_rename_state *state = tevent_req_data(
3524 req, struct cli_smb2_rename_state);
3527 status = get_fnum_from_path_recv(subreq, &state->fnum);
3528 TALLOC_FREE(subreq);
3529 if (tevent_req_nterror(req, status)) {
3533 subreq = cli_smb2_rename_fnum_send(
3540 if (tevent_req_nomem(subreq, req)) {
3543 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3546 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3548 struct tevent_req *req = tevent_req_callback_data(
3549 subreq, struct tevent_req);
3550 struct cli_smb2_rename_state *state = tevent_req_data(
3551 req, struct cli_smb2_rename_state);
3553 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3554 TALLOC_FREE(subreq);
3556 subreq = cli_smb2_close_fnum_send(
3557 state, state->ev, state->cli, state->fnum);
3558 if (tevent_req_nomem(subreq, req)) {
3561 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3564 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3566 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3567 tevent_req_simple_finish_ntstatus(subreq, status);
3570 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3572 struct cli_smb2_rename_state *state = tevent_req_data(
3573 req, struct cli_smb2_rename_state);
3574 NTSTATUS status = NT_STATUS_OK;
3576 if (!tevent_req_is_nterror(req, &status)) {
3577 status = state->rename_status;
3579 tevent_req_received(req);
3583 /***************************************************************
3584 Wrapper that allows SMB2 to set an EA on a fnum.
3586 ***************************************************************/
3588 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3590 const char *ea_name,
3595 DATA_BLOB inbuf = data_blob_null;
3597 char *ea_name_ascii = NULL;
3599 TALLOC_CTX *frame = talloc_stackframe();
3601 if (smbXcli_conn_has_async_calls(cli->conn)) {
3603 * Can't use sync call while an async call is in flight
3605 status = NT_STATUS_INVALID_PARAMETER;
3609 /* Marshall the SMB2 EA data. */
3610 if (ea_len > 0xFFFF) {
3611 status = NT_STATUS_INVALID_PARAMETER;
3615 if (!push_ascii_talloc(frame,
3619 status = NT_STATUS_INVALID_PARAMETER;
3623 if (namelen < 2 || namelen > 0xFF) {
3624 status = NT_STATUS_INVALID_PARAMETER;
3628 bloblen = 8 + ea_len + namelen;
3629 /* Round up to a 4 byte boundary. */
3630 bloblen = ((bloblen + 3)&~3);
3632 inbuf = data_blob_talloc_zero(frame, bloblen);
3633 if (inbuf.data == NULL) {
3634 status = NT_STATUS_NO_MEMORY;
3637 /* namelen doesn't include the NULL byte. */
3638 SCVAL(inbuf.data, 5, namelen - 1);
3639 SSVAL(inbuf.data, 6, ea_len);
3640 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3641 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3643 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3644 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3646 status = cli_smb2_set_info_fnum(
3649 1, /* in_info_type */
3650 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3651 &inbuf, /* in_input_buffer */
3652 0); /* in_additional_info */
3656 cli->raw_status = status;
3662 /***************************************************************
3663 Wrapper that allows SMB2 to set an EA on a pathname.
3665 ***************************************************************/
3667 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3669 const char *ea_name,
3674 uint16_t fnum = 0xffff;
3676 if (smbXcli_conn_has_async_calls(cli->conn)) {
3678 * Can't use sync call while an async call is in flight
3680 status = NT_STATUS_INVALID_PARAMETER;
3684 status = get_fnum_from_path(cli,
3689 if (!NT_STATUS_IS_OK(status)) {
3693 status = cli_set_ea_fnum(cli,
3698 if (!NT_STATUS_IS_OK(status)) {
3704 if (fnum != 0xffff) {
3705 cli_smb2_close_fnum(cli, fnum);
3708 cli->raw_status = status;
3713 /***************************************************************
3714 Wrapper that allows SMB2 to get an EA list on a pathname.
3716 ***************************************************************/
3718 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3722 struct ea_struct **pea_array)
3725 uint16_t fnum = 0xffff;
3726 DATA_BLOB outbuf = data_blob_null;
3727 struct ea_list *ea_list = NULL;
3728 struct ea_list *eal = NULL;
3729 size_t ea_count = 0;
3730 TALLOC_CTX *frame = talloc_stackframe();
3735 if (smbXcli_conn_has_async_calls(cli->conn)) {
3737 * Can't use sync call while an async call is in flight
3739 status = NT_STATUS_INVALID_PARAMETER;
3743 status = get_fnum_from_path(cli,
3748 if (!NT_STATUS_IS_OK(status)) {
3752 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3753 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3755 status = cli_smb2_query_info_fnum(
3758 1, /* in_info_type */
3759 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3760 0xFFFF, /* in_max_output_length */
3761 NULL, /* in_input_buffer */
3762 0, /* in_additional_info */
3767 if (!NT_STATUS_IS_OK(status)) {
3771 /* Parse the reply. */
3772 ea_list = read_nttrans_ea_list(ctx,
3773 (const char *)outbuf.data,
3775 if (ea_list == NULL) {
3776 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3780 /* Convert to an array. */
3781 for (eal = ea_list; eal; eal = eal->next) {
3786 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3787 if (*pea_array == NULL) {
3788 status = NT_STATUS_NO_MEMORY;
3792 for (eal = ea_list; eal; eal = eal->next) {
3793 (*pea_array)[ea_count++] = eal->ea;
3795 *pnum_eas = ea_count;
3800 if (fnum != 0xffff) {
3801 cli_smb2_close_fnum(cli, fnum);
3804 cli->raw_status = status;
3810 /***************************************************************
3811 Wrapper that allows SMB2 to get user quota.
3813 ***************************************************************/
3815 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3817 SMB_NTQUOTA_STRUCT *pqt)
3820 DATA_BLOB inbuf = data_blob_null;
3821 DATA_BLOB info_blob = data_blob_null;
3822 DATA_BLOB outbuf = data_blob_null;
3823 TALLOC_CTX *frame = talloc_stackframe();
3825 unsigned int offset;
3826 struct smb2_query_quota_info query = {0};
3827 struct file_get_quota_info info = {0};
3828 enum ndr_err_code err;
3829 struct ndr_push *ndr_push = NULL;
3831 if (smbXcli_conn_has_async_calls(cli->conn)) {
3833 * Can't use sync call while an async call is in flight
3835 status = NT_STATUS_INVALID_PARAMETER;
3839 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3841 query.return_single = 1;
3843 info.next_entry_offset = 0;
3844 info.sid_length = sid_len;
3845 info.sid = pqt->sid;
3847 err = ndr_push_struct_blob(
3851 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3853 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3854 status = NT_STATUS_INTERNAL_ERROR;
3858 query.sid_list_length = info_blob.length;
3859 ndr_push = ndr_push_init_ctx(frame);
3861 status = NT_STATUS_NO_MEMORY;
3865 err = ndr_push_smb2_query_quota_info(ndr_push,
3866 NDR_SCALARS | NDR_BUFFERS,
3869 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3870 status = NT_STATUS_INTERNAL_ERROR;
3874 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3877 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3878 status = NT_STATUS_INTERNAL_ERROR;
3881 inbuf.data = ndr_push->data;
3882 inbuf.length = ndr_push->offset;
3884 status = cli_smb2_query_info_fnum(
3887 4, /* in_info_type */
3888 0, /* in_file_info_class */
3889 0xFFFF, /* in_max_output_length */
3890 &inbuf, /* in_input_buffer */
3891 0, /* in_additional_info */
3896 if (!NT_STATUS_IS_OK(status)) {
3900 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3902 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3903 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3907 cli->raw_status = status;
3913 /***************************************************************
3914 Wrapper that allows SMB2 to list user quota.
3916 ***************************************************************/
3918 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3919 TALLOC_CTX *mem_ctx,
3921 SMB_NTQUOTA_LIST **pqt_list,
3925 DATA_BLOB inbuf = data_blob_null;
3926 DATA_BLOB outbuf = data_blob_null;
3927 TALLOC_CTX *frame = talloc_stackframe();
3928 struct smb2_query_quota_info info = {0};
3929 enum ndr_err_code err;
3931 if (smbXcli_conn_has_async_calls(cli->conn)) {
3933 * Can't use sync call while an async call is in flight
3935 status = NT_STATUS_INVALID_PARAMETER;
3939 info.restart_scan = first ? 1 : 0;
3941 err = ndr_push_struct_blob(
3945 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3947 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3948 status = NT_STATUS_INTERNAL_ERROR;
3952 status = cli_smb2_query_info_fnum(
3955 4, /* in_info_type */
3956 0, /* in_file_info_class */
3957 0xFFFF, /* in_max_output_length */
3958 &inbuf, /* in_input_buffer */
3959 0, /* in_additional_info */
3965 * safeguard against panic from calling parse_user_quota_list with
3968 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3969 status = NT_STATUS_NO_MORE_ENTRIES;
3972 if (!NT_STATUS_IS_OK(status)) {
3976 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3980 cli->raw_status = status;
3986 /***************************************************************
3987 Wrapper that allows SMB2 to get file system quota.
3989 ***************************************************************/
3991 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3993 SMB_NTQUOTA_STRUCT *pqt)
3996 DATA_BLOB outbuf = data_blob_null;
3997 TALLOC_CTX *frame = talloc_stackframe();
3999 if (smbXcli_conn_has_async_calls(cli->conn)) {
4001 * Can't use sync call while an async call is in flight
4003 status = NT_STATUS_INVALID_PARAMETER;
4007 status = cli_smb2_query_info_fnum(
4010 2, /* in_info_type */
4011 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
4012 0xFFFF, /* in_max_output_length */
4013 NULL, /* in_input_buffer */
4014 0, /* in_additional_info */
4019 if (!NT_STATUS_IS_OK(status)) {
4023 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
4026 cli->raw_status = status;
4032 /***************************************************************
4033 Wrapper that allows SMB2 to set user quota.
4035 ***************************************************************/
4037 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
4039 SMB_NTQUOTA_LIST *qtl)
4042 DATA_BLOB inbuf = data_blob_null;
4043 TALLOC_CTX *frame = talloc_stackframe();
4045 if (smbXcli_conn_has_async_calls(cli->conn)) {
4047 * Can't use sync call while an async call is in flight
4049 status = NT_STATUS_INVALID_PARAMETER;
4053 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
4054 if (!NT_STATUS_IS_OK(status)) {
4058 status = cli_smb2_set_info_fnum(
4061 4, /* in_info_type */
4062 0, /* in_file_info_class */
4063 &inbuf, /* in_input_buffer */
4064 0); /* in_additional_info */
4067 cli->raw_status = status;
4074 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
4076 SMB_NTQUOTA_STRUCT *pqt)
4079 DATA_BLOB inbuf = data_blob_null;
4080 TALLOC_CTX *frame = talloc_stackframe();
4082 if (smbXcli_conn_has_async_calls(cli->conn)) {
4084 * Can't use sync call while an async call is in flight
4086 status = NT_STATUS_INVALID_PARAMETER;
4090 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
4091 if (!NT_STATUS_IS_OK(status)) {
4095 status = cli_smb2_set_info_fnum(
4098 2, /* in_info_type */
4099 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
4100 &inbuf, /* in_input_buffer */
4101 0); /* in_additional_info */
4103 cli->raw_status = status;
4109 struct cli_smb2_read_state {
4110 struct tevent_context *ev;
4111 struct cli_state *cli;
4112 struct smb2_hnd *ph;
4113 uint64_t start_offset;
4119 static void cli_smb2_read_done(struct tevent_req *subreq);
4121 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
4122 struct tevent_context *ev,
4123 struct cli_state *cli,
4129 struct tevent_req *req, *subreq;
4130 struct cli_smb2_read_state *state;
4132 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
4138 state->start_offset = (uint64_t)offset;
4139 state->size = (uint32_t)size;
4140 state->received = 0;
4143 status = map_fnum_to_smb2_handle(cli,
4146 if (tevent_req_nterror(req, status)) {
4147 return tevent_req_post(req, ev);
4150 subreq = smb2cli_read_send(state,
4153 state->cli->timeout,
4154 state->cli->smb2.session,
4155 state->cli->smb2.tcon,
4157 state->start_offset,
4158 state->ph->fid_persistent,
4159 state->ph->fid_volatile,
4160 0, /* minimum_count */
4161 0); /* remaining_bytes */
4163 if (tevent_req_nomem(subreq, req)) {
4164 return tevent_req_post(req, ev);
4166 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
4170 static void cli_smb2_read_done(struct tevent_req *subreq)
4172 struct tevent_req *req = tevent_req_callback_data(
4173 subreq, struct tevent_req);
4174 struct cli_smb2_read_state *state = tevent_req_data(
4175 req, struct cli_smb2_read_state);
4178 status = smb2cli_read_recv(subreq, state,
4179 &state->buf, &state->received);
4180 if (tevent_req_nterror(req, status)) {
4184 if (state->received > state->size) {
4185 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4189 tevent_req_done(req);
4192 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
4197 struct cli_smb2_read_state *state = tevent_req_data(
4198 req, struct cli_smb2_read_state);
4200 if (tevent_req_is_nterror(req, &status)) {
4201 state->cli->raw_status = status;
4205 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
4206 * better make sure that you copy it away before you talloc_free(req).
4207 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
4209 *received = (ssize_t)state->received;
4210 *rcvbuf = state->buf;
4211 state->cli->raw_status = NT_STATUS_OK;
4212 return NT_STATUS_OK;
4215 struct cli_smb2_write_state {
4216 struct tevent_context *ev;
4217 struct cli_state *cli;
4218 struct smb2_hnd *ph;
4226 static void cli_smb2_write_written(struct tevent_req *req);
4228 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
4229 struct tevent_context *ev,
4230 struct cli_state *cli,
4238 struct tevent_req *req, *subreq = NULL;
4239 struct cli_smb2_write_state *state = NULL;
4241 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
4247 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4248 state->flags = (uint32_t)mode;
4250 state->offset = (uint64_t)offset;
4251 state->size = (uint32_t)size;
4254 status = map_fnum_to_smb2_handle(cli,
4257 if (tevent_req_nterror(req, status)) {
4258 return tevent_req_post(req, ev);
4261 subreq = smb2cli_write_send(state,
4264 state->cli->timeout,
4265 state->cli->smb2.session,
4266 state->cli->smb2.tcon,
4269 state->ph->fid_persistent,
4270 state->ph->fid_volatile,
4271 0, /* remaining_bytes */
4272 state->flags, /* flags */
4275 if (tevent_req_nomem(subreq, req)) {
4276 return tevent_req_post(req, ev);
4278 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4282 static void cli_smb2_write_written(struct tevent_req *subreq)
4284 struct tevent_req *req = tevent_req_callback_data(
4285 subreq, struct tevent_req);
4286 struct cli_smb2_write_state *state = tevent_req_data(
4287 req, struct cli_smb2_write_state);
4291 status = smb2cli_write_recv(subreq, &written);
4292 TALLOC_FREE(subreq);
4293 if (tevent_req_nterror(req, status)) {
4297 state->written = written;
4299 tevent_req_done(req);
4302 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4305 struct cli_smb2_write_state *state = tevent_req_data(
4306 req, struct cli_smb2_write_state);
4309 if (tevent_req_is_nterror(req, &status)) {
4310 state->cli->raw_status = status;
4311 tevent_req_received(req);
4315 if (pwritten != NULL) {
4316 *pwritten = (size_t)state->written;
4318 state->cli->raw_status = NT_STATUS_OK;
4319 tevent_req_received(req);
4320 return NT_STATUS_OK;
4323 /***************************************************************
4324 Wrapper that allows SMB2 async write using an fnum.
4325 This is mostly cut-and-paste from Volker's code inside
4326 source3/libsmb/clireadwrite.c, adapted for SMB2.
4328 Done this way so I can reuse all the logic inside cli_push()
4330 ***************************************************************/
4332 struct cli_smb2_writeall_state {
4333 struct tevent_context *ev;
4334 struct cli_state *cli;
4335 struct smb2_hnd *ph;
4343 static void cli_smb2_writeall_written(struct tevent_req *req);
4345 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4346 struct tevent_context *ev,
4347 struct cli_state *cli,
4355 struct tevent_req *req, *subreq = NULL;
4356 struct cli_smb2_writeall_state *state = NULL;
4361 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4367 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4368 state->flags = (uint32_t)mode;
4370 state->offset = (uint64_t)offset;
4371 state->size = (uint32_t)size;
4374 status = map_fnum_to_smb2_handle(cli,
4377 if (tevent_req_nterror(req, status)) {
4378 return tevent_req_post(req, ev);
4381 to_write = state->size;
4382 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4383 to_write = MIN(max_size, to_write);
4384 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4386 to_write = MIN(max_size, to_write);
4389 subreq = smb2cli_write_send(state,
4392 state->cli->timeout,
4393 state->cli->smb2.session,
4394 state->cli->smb2.tcon,
4397 state->ph->fid_persistent,
4398 state->ph->fid_volatile,
4399 0, /* remaining_bytes */
4400 state->flags, /* flags */
4401 state->buf + state->written);
4403 if (tevent_req_nomem(subreq, req)) {
4404 return tevent_req_post(req, ev);
4406 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4410 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4412 struct tevent_req *req = tevent_req_callback_data(
4413 subreq, struct tevent_req);
4414 struct cli_smb2_writeall_state *state = tevent_req_data(
4415 req, struct cli_smb2_writeall_state);
4417 uint32_t written, to_write;
4421 status = smb2cli_write_recv(subreq, &written);
4422 TALLOC_FREE(subreq);
4423 if (tevent_req_nterror(req, status)) {
4427 state->written += written;
4429 if (state->written > state->size) {
4430 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4434 to_write = state->size - state->written;
4436 if (to_write == 0) {
4437 tevent_req_done(req);
4441 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4442 to_write = MIN(max_size, to_write);
4443 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4445 to_write = MIN(max_size, to_write);
4448 subreq = smb2cli_write_send(state,
4451 state->cli->timeout,
4452 state->cli->smb2.session,
4453 state->cli->smb2.tcon,
4455 state->offset + state->written,
4456 state->ph->fid_persistent,
4457 state->ph->fid_volatile,
4458 0, /* remaining_bytes */
4459 state->flags, /* flags */
4460 state->buf + state->written);
4462 if (tevent_req_nomem(subreq, req)) {
4465 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4468 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4471 struct cli_smb2_writeall_state *state = tevent_req_data(
4472 req, struct cli_smb2_writeall_state);
4475 if (tevent_req_is_nterror(req, &status)) {
4476 state->cli->raw_status = status;
4479 if (pwritten != NULL) {
4480 *pwritten = (size_t)state->written;
4482 state->cli->raw_status = NT_STATUS_OK;
4483 return NT_STATUS_OK;
4486 struct cli_smb2_splice_state {
4487 struct tevent_context *ev;
4488 struct cli_state *cli;
4489 struct smb2_hnd *src_ph;
4490 struct smb2_hnd *dst_ph;
4491 int (*splice_cb)(off_t n, void *priv);
4498 struct req_resume_key_rsp resume_rsp;
4499 struct srv_copychunk_copy cc_copy;
4502 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4503 struct tevent_req *req);
4505 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4507 struct tevent_req *req = tevent_req_callback_data(
4508 subreq, struct tevent_req);
4509 struct cli_smb2_splice_state *state =
4510 tevent_req_data(req,
4511 struct cli_smb2_splice_state);
4512 struct smbXcli_conn *conn = state->cli->conn;
4513 DATA_BLOB out_input_buffer = data_blob_null;
4514 DATA_BLOB out_output_buffer = data_blob_null;
4515 struct srv_copychunk_rsp cc_copy_rsp;
4516 enum ndr_err_code ndr_ret;
4519 status = smb2cli_ioctl_recv(subreq, state,
4521 &out_output_buffer);
4522 TALLOC_FREE(subreq);
4523 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4524 state->resized) && tevent_req_nterror(req, status)) {
4528 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4529 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4530 if (ndr_ret != NDR_ERR_SUCCESS) {
4531 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4532 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4536 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4537 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4538 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4539 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4540 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4541 tevent_req_nterror(req, status)) {
4545 state->resized = true;
4546 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4547 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4549 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4550 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4551 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4552 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4555 state->src_offset += cc_copy_rsp.total_bytes_written;
4556 state->dst_offset += cc_copy_rsp.total_bytes_written;
4557 state->written += cc_copy_rsp.total_bytes_written;
4558 if (!state->splice_cb(state->written, state->priv)) {
4559 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4564 cli_splice_copychunk_send(state, req);
4567 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4568 struct tevent_req *req)
4570 struct tevent_req *subreq;
4571 enum ndr_err_code ndr_ret;
4572 struct smbXcli_conn *conn = state->cli->conn;
4573 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4574 off_t src_offset = state->src_offset;
4575 off_t dst_offset = state->dst_offset;
4576 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4577 state->size - state->written);
4578 DATA_BLOB in_input_buffer = data_blob_null;
4579 DATA_BLOB in_output_buffer = data_blob_null;
4581 if (state->size - state->written == 0) {
4582 tevent_req_done(req);
4586 cc_copy->chunk_count = 0;
4588 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4589 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4590 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4591 smb2cli_conn_cc_chunk_len(conn));
4592 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4593 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4596 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4597 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4598 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4599 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4602 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4603 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4604 cc_copy->chunk_count++;
4607 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4608 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4609 if (ndr_ret != NDR_ERR_SUCCESS) {
4610 DEBUG(0, ("failed to marshall copy chunk req\n"));
4611 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4615 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4616 state->cli->timeout,
4617 state->cli->smb2.session,
4618 state->cli->smb2.tcon,
4619 state->dst_ph->fid_persistent, /* in_fid_persistent */
4620 state->dst_ph->fid_volatile, /* in_fid_volatile */
4621 FSCTL_SRV_COPYCHUNK_WRITE,
4622 0, /* in_max_input_length */
4624 12, /* in_max_output_length */
4626 SMB2_IOCTL_FLAG_IS_FSCTL);
4627 if (tevent_req_nomem(subreq, req)) {
4630 tevent_req_set_callback(subreq,
4631 cli_splice_copychunk_done,
4635 static void cli_splice_key_done(struct tevent_req *subreq)
4637 struct tevent_req *req = tevent_req_callback_data(
4638 subreq, struct tevent_req);
4639 struct cli_smb2_splice_state *state =
4640 tevent_req_data(req,
4641 struct cli_smb2_splice_state);
4642 enum ndr_err_code ndr_ret;
4645 DATA_BLOB out_input_buffer = data_blob_null;
4646 DATA_BLOB out_output_buffer = data_blob_null;
4648 status = smb2cli_ioctl_recv(subreq, state,
4650 &out_output_buffer);
4651 TALLOC_FREE(subreq);
4652 if (tevent_req_nterror(req, status)) {
4656 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4657 state, &state->resume_rsp,
4658 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4659 if (ndr_ret != NDR_ERR_SUCCESS) {
4660 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4661 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4665 memcpy(&state->cc_copy.source_key,
4666 &state->resume_rsp.resume_key,
4667 sizeof state->resume_rsp.resume_key);
4669 cli_splice_copychunk_send(state, req);
4672 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4673 struct tevent_context *ev,
4674 struct cli_state *cli,
4675 uint16_t src_fnum, uint16_t dst_fnum,
4676 off_t size, off_t src_offset, off_t dst_offset,
4677 int (*splice_cb)(off_t n, void *priv),
4680 struct tevent_req *req;
4681 struct tevent_req *subreq;
4682 struct cli_smb2_splice_state *state;
4684 DATA_BLOB in_input_buffer = data_blob_null;
4685 DATA_BLOB in_output_buffer = data_blob_null;
4687 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4693 state->splice_cb = splice_cb;
4697 state->src_offset = src_offset;
4698 state->dst_offset = dst_offset;
4699 state->cc_copy.chunks = talloc_array(state,
4700 struct srv_copychunk,
4701 smb2cli_conn_cc_max_chunks(cli->conn));
4702 if (state->cc_copy.chunks == NULL) {
4706 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4707 if (tevent_req_nterror(req, status))
4708 return tevent_req_post(req, ev);
4710 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4711 if (tevent_req_nterror(req, status))
4712 return tevent_req_post(req, ev);
4714 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4718 state->src_ph->fid_persistent, /* in_fid_persistent */
4719 state->src_ph->fid_volatile, /* in_fid_volatile */
4720 FSCTL_SRV_REQUEST_RESUME_KEY,
4721 0, /* in_max_input_length */
4723 32, /* in_max_output_length */
4725 SMB2_IOCTL_FLAG_IS_FSCTL);
4726 if (tevent_req_nomem(subreq, req)) {
4729 tevent_req_set_callback(subreq,
4730 cli_splice_key_done,
4736 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4738 struct cli_smb2_splice_state *state = tevent_req_data(
4739 req, struct cli_smb2_splice_state);
4742 if (tevent_req_is_nterror(req, &status)) {
4743 state->cli->raw_status = status;
4744 tevent_req_received(req);
4747 if (written != NULL) {
4748 *written = state->written;
4750 state->cli->raw_status = NT_STATUS_OK;
4751 tevent_req_received(req);
4752 return NT_STATUS_OK;
4755 /***************************************************************
4756 SMB2 enum shadow copy data.
4757 ***************************************************************/
4759 struct cli_smb2_shadow_copy_data_fnum_state {
4760 struct cli_state *cli;
4762 struct smb2_hnd *ph;
4763 DATA_BLOB out_input_buffer;
4764 DATA_BLOB out_output_buffer;
4767 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4769 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4770 TALLOC_CTX *mem_ctx,
4771 struct tevent_context *ev,
4772 struct cli_state *cli,
4776 struct tevent_req *req, *subreq;
4777 struct cli_smb2_shadow_copy_data_fnum_state *state;
4780 req = tevent_req_create(mem_ctx, &state,
4781 struct cli_smb2_shadow_copy_data_fnum_state);
4789 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4790 if (tevent_req_nterror(req, status)) {
4791 return tevent_req_post(req, ev);
4795 * TODO. Under SMB2 we should send a zero max_output_length
4796 * ioctl to get the required size, then send another ioctl
4797 * to get the data, but the current SMB1 implementation just
4798 * does one roundtrip with a 64K buffer size. Do the same
4802 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4803 state->cli->timeout,
4804 state->cli->smb2.session,
4805 state->cli->smb2.tcon,
4806 state->ph->fid_persistent, /* in_fid_persistent */
4807 state->ph->fid_volatile, /* in_fid_volatile */
4808 FSCTL_GET_SHADOW_COPY_DATA,
4809 0, /* in_max_input_length */
4810 NULL, /* in_input_buffer */
4812 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4813 NULL, /* in_output_buffer */
4814 SMB2_IOCTL_FLAG_IS_FSCTL);
4816 if (tevent_req_nomem(subreq, req)) {
4817 return tevent_req_post(req, ev);
4819 tevent_req_set_callback(subreq,
4820 cli_smb2_shadow_copy_data_fnum_done,
4826 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4828 struct tevent_req *req = tevent_req_callback_data(
4829 subreq, struct tevent_req);
4830 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4831 req, struct cli_smb2_shadow_copy_data_fnum_state);
4834 status = smb2cli_ioctl_recv(subreq, state,
4835 &state->out_input_buffer,
4836 &state->out_output_buffer);
4837 tevent_req_simple_finish_ntstatus(subreq, status);
4840 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4841 TALLOC_CTX *mem_ctx,
4846 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4847 req, struct cli_smb2_shadow_copy_data_fnum_state);
4848 char **names = NULL;
4849 uint32_t num_names = 0;
4850 uint32_t num_names_returned = 0;
4851 uint32_t dlength = 0;
4853 uint8_t *endp = NULL;
4856 if (tevent_req_is_nterror(req, &status)) {
4860 if (state->out_output_buffer.length < 16) {
4861 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4864 num_names = IVAL(state->out_output_buffer.data, 0);
4865 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4866 dlength = IVAL(state->out_output_buffer.data, 8);
4868 if (num_names > 0x7FFFFFFF) {
4869 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4872 if (get_names == false) {
4873 *pnum_names = (int)num_names;
4874 return NT_STATUS_OK;
4876 if (num_names != num_names_returned) {
4877 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4879 if (dlength + 12 < 12) {
4880 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4883 * NB. The below is an allowable return if there are
4884 * more snapshots than the buffer size we told the
4885 * server we can receive. We currently don't support
4888 if (dlength + 12 > state->out_output_buffer.length) {
4889 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4891 if (state->out_output_buffer.length +
4892 (2 * sizeof(SHADOW_COPY_LABEL)) <
4893 state->out_output_buffer.length) {
4894 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4897 names = talloc_array(mem_ctx, char *, num_names_returned);
4898 if (names == NULL) {
4899 return NT_STATUS_NO_MEMORY;
4902 endp = state->out_output_buffer.data +
4903 state->out_output_buffer.length;
4905 for (i=0; i<num_names_returned; i++) {
4908 size_t converted_size;
4910 src = state->out_output_buffer.data + 12 +
4911 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4913 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4914 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4916 ret = convert_string_talloc(
4917 names, CH_UTF16LE, CH_UNIX,
4918 src, 2 * sizeof(SHADOW_COPY_LABEL),
4919 &names[i], &converted_size);
4922 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4925 *pnum_names = num_names;
4927 return NT_STATUS_OK;
4930 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4931 struct cli_state *cli,
4937 TALLOC_CTX *frame = talloc_stackframe();
4938 struct tevent_context *ev;
4939 struct tevent_req *req;
4940 NTSTATUS status = NT_STATUS_NO_MEMORY;
4942 if (smbXcli_conn_has_async_calls(cli->conn)) {
4944 * Can't use sync call while an async call is in flight
4946 status = NT_STATUS_INVALID_PARAMETER;
4949 ev = samba_tevent_context_init(frame);
4953 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4961 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4964 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4970 cli->raw_status = status;
4976 /***************************************************************
4977 Wrapper that allows SMB2 to truncate a file.
4979 ***************************************************************/
4981 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4986 uint8_t buf[8] = {0};
4987 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4988 TALLOC_CTX *frame = talloc_stackframe();
4990 if (smbXcli_conn_has_async_calls(cli->conn)) {
4992 * Can't use sync call while an async call is in flight
4994 status = NT_STATUS_INVALID_PARAMETER;
4998 SBVAL(buf, 0, newsize);
5000 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
5001 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
5003 status = cli_smb2_set_info_fnum(
5006 1, /* in_info_type */
5007 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
5008 &inbuf, /* in_input_buffer */
5013 cli->raw_status = status;
5019 struct cli_smb2_notify_state {
5020 struct tevent_req *subreq;
5021 struct notify_change *changes;
5025 static void cli_smb2_notify_done(struct tevent_req *subreq);
5026 static bool cli_smb2_notify_cancel(struct tevent_req *req);
5028 struct tevent_req *cli_smb2_notify_send(
5029 TALLOC_CTX *mem_ctx,
5030 struct tevent_context *ev,
5031 struct cli_state *cli,
5033 uint32_t buffer_size,
5034 uint32_t completion_filter,
5037 struct tevent_req *req = NULL;
5038 struct cli_smb2_notify_state *state = NULL;
5039 struct smb2_hnd *ph = NULL;
5042 req = tevent_req_create(mem_ctx, &state,
5043 struct cli_smb2_notify_state);
5048 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
5049 if (tevent_req_nterror(req, status)) {
5050 return tevent_req_post(req, ev);
5053 state->subreq = smb2cli_notify_send(
5065 if (tevent_req_nomem(state->subreq, req)) {
5066 return tevent_req_post(req, ev);
5068 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
5069 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
5073 static bool cli_smb2_notify_cancel(struct tevent_req *req)
5075 struct cli_smb2_notify_state *state = tevent_req_data(
5076 req, struct cli_smb2_notify_state);
5079 ok = tevent_req_cancel(state->subreq);
5083 static void cli_smb2_notify_done(struct tevent_req *subreq)
5085 struct tevent_req *req = tevent_req_callback_data(
5086 subreq, struct tevent_req);
5087 struct cli_smb2_notify_state *state = tevent_req_data(
5088 req, struct cli_smb2_notify_state);
5094 status = smb2cli_notify_recv(subreq, state, &base, &len);
5095 TALLOC_FREE(subreq);
5097 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
5098 tevent_req_done(req);
5101 if (tevent_req_nterror(req, status)) {
5107 while (len - ofs >= 12) {
5108 struct notify_change *tmp;
5109 struct notify_change *c;
5110 uint32_t next_ofs = IVAL(base, ofs);
5111 uint32_t file_name_length = IVAL(base, ofs+8);
5115 tmp = talloc_realloc(
5118 struct notify_change,
5119 state->num_changes + 1);
5120 if (tevent_req_nomem(tmp, req)) {
5123 state->changes = tmp;
5124 c = &state->changes[state->num_changes];
5125 state->num_changes += 1;
5127 if (smb_buffer_oob(len, ofs, next_ofs) ||
5128 smb_buffer_oob(len, ofs+12, file_name_length)) {
5130 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
5134 c->action = IVAL(base, ofs+4);
5136 ok = convert_string_talloc(
5146 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
5150 if (next_ofs == 0) {
5156 tevent_req_done(req);
5159 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
5160 TALLOC_CTX *mem_ctx,
5161 struct notify_change **pchanges,
5162 uint32_t *pnum_changes)
5164 struct cli_smb2_notify_state *state = tevent_req_data(
5165 req, struct cli_smb2_notify_state);
5168 if (tevent_req_is_nterror(req, &status)) {
5171 *pchanges = talloc_move(mem_ctx, &state->changes);
5172 *pnum_changes = state->num_changes;
5173 return NT_STATUS_OK;
5176 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
5177 uint32_t buffer_size, uint32_t completion_filter,
5178 bool recursive, TALLOC_CTX *mem_ctx,
5179 struct notify_change **pchanges,
5180 uint32_t *pnum_changes)
5182 TALLOC_CTX *frame = talloc_stackframe();
5183 struct tevent_context *ev;
5184 struct tevent_req *req;
5185 NTSTATUS status = NT_STATUS_NO_MEMORY;
5187 if (smbXcli_conn_has_async_calls(cli->conn)) {
5189 * Can't use sync call while an async call is in flight
5191 status = NT_STATUS_INVALID_PARAMETER;
5194 ev = samba_tevent_context_init(frame);
5198 req = cli_smb2_notify_send(
5209 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
5212 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
5218 struct cli_smb2_fsctl_state {
5222 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
5224 struct tevent_req *cli_smb2_fsctl_send(
5225 TALLOC_CTX *mem_ctx,
5226 struct tevent_context *ev,
5227 struct cli_state *cli,
5230 const DATA_BLOB *in,
5233 struct tevent_req *req = NULL, *subreq = NULL;
5234 struct cli_smb2_fsctl_state *state = NULL;
5235 struct smb2_hnd *ph = NULL;
5238 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
5243 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
5244 if (tevent_req_nterror(req, status)) {
5245 return tevent_req_post(req, ev);
5248 subreq = smb2cli_ioctl_send(
5258 0, /* in_max_input_length */
5262 SMB2_IOCTL_FLAG_IS_FSCTL);
5264 if (tevent_req_nomem(subreq, req)) {
5265 return tevent_req_post(req, ev);
5267 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
5271 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5273 struct tevent_req *req = tevent_req_callback_data(
5274 subreq, struct tevent_req);
5275 struct cli_smb2_fsctl_state *state = tevent_req_data(
5276 req, struct cli_smb2_fsctl_state);
5279 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5280 tevent_req_simple_finish_ntstatus(subreq, status);
5283 NTSTATUS cli_smb2_fsctl_recv(
5284 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5286 struct cli_smb2_fsctl_state *state = tevent_req_data(
5287 req, struct cli_smb2_fsctl_state);
5288 NTSTATUS status = NT_STATUS_OK;
5290 if (tevent_req_is_nterror(req, &status)) {
5291 tevent_req_received(req);
5295 if (state->out.length == 0) {
5296 *out = (DATA_BLOB) { .data = NULL, };
5299 * Can't use talloc_move() here, the outblobs from
5300 * smb2cli_ioctl_recv() are not standalone talloc
5301 * objects but just peek into the larger buffers
5302 * received, hanging off "state".
5304 *out = data_blob_talloc(
5305 mem_ctx, state->out.data, state->out.length);
5306 if (out->data == NULL) {
5307 status = NT_STATUS_NO_MEMORY;
5311 tevent_req_received(req);
5312 return NT_STATUS_OK;