2 Unix SMB/CIFS implementation.
4 Copyright (C) Jeremy Allison 2013
5 Copyright (C) Volker Lendecke 2013
6 Copyright (C) Stefan Metzmacher 2013
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 This code is a thin wrapper around the existing
24 cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
25 but allows the handles to be mapped to uint16_t fnums,
26 which are easier for smbclient to use.
31 #include "async_smb.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "cli_smb2_fnum.h"
36 #include "../libcli/smb/smb2_create_blob.h"
37 #include "libsmb/proto.h"
38 #include "lib/util/tevent_ntstatus.h"
39 #include "../libcli/security/security.h"
40 #include "../librpc/gen_ndr/ndr_security.h"
41 #include "lib/util_ea.h"
42 #include "librpc/gen_ndr/ndr_ioctl.h"
44 #include "librpc/gen_ndr/ndr_quota.h"
45 #include "librpc/gen_ndr/ndr_smb3posix.h"
46 #include "lib/util/string_wrappers.h"
47 #include "lib/util/idtree.h"
48 #include "libcli/smb/smb2_posix.h"
51 uint64_t fid_persistent
;
52 uint64_t fid_volatile
;
53 bool posix
; /* Opened with posix context */
57 * Handle mapping code.
60 /***************************************************************
61 Allocate a new fnum between 1 and 0xFFFE from an smb2 file id.
62 Ensures handle is owned by cli struct.
63 ***************************************************************/
65 static NTSTATUS
map_smb2_handle_to_fnum(struct cli_state
*cli
,
66 uint64_t fid_persistent
,
67 uint64_t fid_volatile
,
72 struct idr_context
*idp
= cli
->smb2
.open_handles
;
73 struct smb2_hnd
*owned_h
= NULL
;
75 owned_h
= talloc(cli
, struct smb2_hnd
);
76 if (owned_h
== NULL
) {
77 return NT_STATUS_NO_MEMORY
;
79 *owned_h
= (struct smb2_hnd
){
80 .fid_persistent
= fid_persistent
,
81 .fid_volatile
= fid_volatile
,
87 cli
->smb2
.open_handles
= idr_init(cli
);
88 if (cli
->smb2
.open_handles
== NULL
) {
90 return NT_STATUS_NO_MEMORY
;
92 idp
= cli
->smb2
.open_handles
;
95 ret
= idr_get_new_above(idp
, owned_h
, 1, 0xFFFE);
98 return NT_STATUS_NO_MEMORY
;
101 *pfnum
= (uint16_t)ret
;
105 /***************************************************************
106 Return the smb2_hnd pointer associated with the given fnum.
107 ***************************************************************/
109 static NTSTATUS
map_fnum_to_smb2_handle(struct cli_state
*cli
,
110 uint16_t fnum
, /* In */
111 struct smb2_hnd
**pph
) /* Out */
113 struct idr_context
*idp
= cli
->smb2
.open_handles
;
116 return NT_STATUS_INVALID_PARAMETER
;
118 *pph
= (struct smb2_hnd
*)idr_find(idp
, fnum
);
120 return NT_STATUS_INVALID_HANDLE
;
125 /***************************************************************
126 Delete the fnum to smb2_hnd mapping. Zeros out handle on
128 ***************************************************************/
130 static NTSTATUS
delete_smb2_handle_mapping(struct cli_state
*cli
,
131 struct smb2_hnd
**pph
, /* In */
132 uint16_t fnum
) /* In */
134 struct idr_context
*idp
= cli
->smb2
.open_handles
;
138 return NT_STATUS_INVALID_PARAMETER
;
141 ph
= (struct smb2_hnd
*)idr_find(idp
, fnum
);
143 return NT_STATUS_INVALID_PARAMETER
;
145 idr_remove(idp
, fnum
);
150 /***************************************************************
152 ***************************************************************/
154 static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags
)
156 if (create_flags
.batch_oplock
) {
157 return SMB2_OPLOCK_LEVEL_BATCH
;
158 } else if (create_flags
.exclusive_oplock
) {
159 return SMB2_OPLOCK_LEVEL_EXCLUSIVE
;
162 /* create_flags doesn't do a level2 request. */
163 return SMB2_OPLOCK_LEVEL_NONE
;
166 /***************************************************************
167 If we're on a DFS share, ensure we convert to a full DFS path
168 if this hasn't already been done.
169 ***************************************************************/
171 static char *smb2_dfs_share_path(TALLOC_CTX
*ctx
,
172 struct cli_state
*cli
,
175 bool is_dfs
= smbXcli_conn_dfs_supported(cli
->conn
) &&
176 smbXcli_tcon_is_dfs_share(cli
->smb2
.tcon
);
177 bool is_already_dfs_path
= false;
182 is_already_dfs_path
= cli_dfs_is_already_full_path(cli
, path
);
183 if (is_already_dfs_path
) {
186 if (path
[0] == '\0') {
187 return talloc_asprintf(ctx
,
189 smbXcli_conn_remote_name(cli
->conn
),
192 while (*path
== '\\') {
195 return talloc_asprintf(ctx
,
197 smbXcli_conn_remote_name(cli
->conn
),
202 /***************************************************************
203 Small wrapper that allows SMB2 create to return a uint16_t fnum.
204 ***************************************************************/
206 struct cli_smb2_create_fnum_state
{
207 struct tevent_context
*ev
;
208 struct cli_state
*cli
;
210 struct cli_smb2_create_flags create_flags
;
211 uint32_t impersonation_level
;
212 uint32_t desired_access
;
213 uint32_t file_attributes
;
214 uint32_t share_access
;
215 uint32_t create_disposition
;
216 uint32_t create_options
;
217 struct smb2_create_blobs in_cblobs
;
218 struct smb2_create_blobs out_cblobs
;
219 struct smb_create_returns cr
;
220 struct symlink_reparse_struct
*symlink
;
222 struct tevent_req
*subreq
;
225 static void cli_smb2_create_fnum_done(struct tevent_req
*subreq
);
226 static bool cli_smb2_create_fnum_cancel(struct tevent_req
*req
);
228 struct tevent_req
*cli_smb2_create_fnum_send(
230 struct tevent_context
*ev
,
231 struct cli_state
*cli
,
232 const char *fname_in
,
233 struct cli_smb2_create_flags create_flags
,
234 uint32_t impersonation_level
,
235 uint32_t desired_access
,
236 uint32_t file_attributes
,
237 uint32_t share_access
,
238 uint32_t create_disposition
,
239 uint32_t create_options
,
240 const struct smb2_create_blobs
*in_cblobs
)
242 struct tevent_req
*req
, *subreq
;
243 struct cli_smb2_create_fnum_state
*state
;
245 size_t fname_len
= 0;
250 req
= tevent_req_create(mem_ctx
, &state
,
251 struct cli_smb2_create_fnum_state
);
257 state
->create_flags
= create_flags
;
258 state
->impersonation_level
= impersonation_level
;
259 state
->desired_access
= desired_access
;
260 state
->file_attributes
= file_attributes
;
261 state
->share_access
= share_access
;
262 state
->create_disposition
= create_disposition
;
264 if (cli
->backup_intent
) {
265 create_options
|= FILE_OPEN_FOR_BACKUP_INTENT
;
267 state
->create_options
= create_options
;
269 fname
= talloc_strdup(state
, fname_in
);
270 if (tevent_req_nomem(fname
, req
)) {
271 return tevent_req_post(req
, ev
);
274 if (cli
->smb2
.client_smb311_posix
) {
275 uint8_t modebuf
[4] = {
280 smb2_create_blob_add(state
,
282 SMB2_CREATE_TAG_POSIX
,
285 .length
= sizeof(modebuf
),
287 if (tevent_req_nterror(req
, status
)) {
288 return tevent_req_post(req
, ev
);
292 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
293 have_twrp
= clistr_smb2_extract_snapshot_token(fname
, &ntt
);
295 status
= smb2_create_blob_add(
298 SMB2_CREATE_TAG_TWRP
,
300 .data
= (uint8_t *)&ntt
,
301 .length
= sizeof(ntt
),
303 if (tevent_req_nterror(req
, status
)) {
304 return tevent_req_post(req
, ev
);
308 if (in_cblobs
!= NULL
) {
310 for (i
=0; i
<in_cblobs
->num_blobs
; i
++) {
311 struct smb2_create_blob
*b
= &in_cblobs
->blobs
[i
];
312 status
= smb2_create_blob_add(
313 state
, &state
->in_cblobs
, b
->tag
, b
->data
);
314 if (!NT_STATUS_IS_OK(status
)) {
315 tevent_req_nterror(req
, status
);
316 return tevent_req_post(req
, ev
);
321 fname
= smb2_dfs_share_path(state
, cli
, fname
);
322 if (tevent_req_nomem(fname
, req
)) {
323 return tevent_req_post(req
, ev
);
325 fname_len
= strlen(fname
);
327 /* SMB2 is pickier about pathnames. Ensure it doesn't
329 if (*fname
== '\\') {
334 /* Or end in a '\' */
335 if (fname_len
> 0 && fname
[fname_len
-1] == '\\') {
339 state
->fname
= talloc_strndup(state
, fname
, fname_len
);
340 if (tevent_req_nomem(state
->fname
, req
)) {
341 return tevent_req_post(req
, ev
);
344 subreq
= smb2cli_create_send(state
,
351 flags_to_smb2_oplock(create_flags
),
359 if (tevent_req_nomem(subreq
, req
)) {
360 return tevent_req_post(req
, ev
);
362 tevent_req_set_callback(subreq
, cli_smb2_create_fnum_done
, req
);
364 state
->subreq
= subreq
;
365 tevent_req_set_cancel_fn(req
, cli_smb2_create_fnum_cancel
);
370 static void cli_smb2_create_fnum_done(struct tevent_req
*subreq
)
372 struct tevent_req
*req
= tevent_req_callback_data(
373 subreq
, struct tevent_req
);
374 struct cli_smb2_create_fnum_state
*state
= tevent_req_data(
375 req
, struct cli_smb2_create_fnum_state
);
376 uint64_t fid_persistent
, fid_volatile
;
377 struct smb2_create_blob
*posix
= NULL
;
378 struct cli_state
*cli
= state
->cli
;
381 status
= smb2cli_create_recv(subreq
,
390 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED
)) {
392 if (state
->create_options
& FILE_OPEN_REPARSE_POINT
) {
394 * Should not happen, but you never know...
397 req
, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED
);
401 state
->create_options
|= FILE_OPEN_REPARSE_POINT
;
403 subreq
= smb2cli_create_send(state
,
410 flags_to_smb2_oplock(
411 state
->create_flags
),
412 state
->impersonation_level
,
413 state
->desired_access
,
414 state
->file_attributes
,
416 state
->create_disposition
,
417 state
->create_options
,
419 if (tevent_req_nomem(subreq
, req
)) {
422 tevent_req_set_callback(subreq
,
423 cli_smb2_create_fnum_done
,
425 state
->subreq
= subreq
;
429 if (tevent_req_nterror(req
, status
)) {
433 posix
= smb2_create_blob_find(&state
->in_cblobs
,
434 SMB2_CREATE_TAG_POSIX
);
436 status
= map_smb2_handle_to_fnum(state
->cli
,
441 if (tevent_req_nterror(req
, status
)) {
444 tevent_req_done(req
);
447 static bool cli_smb2_create_fnum_cancel(struct tevent_req
*req
)
449 struct cli_smb2_create_fnum_state
*state
= tevent_req_data(
450 req
, struct cli_smb2_create_fnum_state
);
451 return tevent_req_cancel(state
->subreq
);
454 NTSTATUS
cli_smb2_create_fnum_recv(
455 struct tevent_req
*req
,
457 struct smb_create_returns
*cr
,
459 struct smb2_create_blobs
*out_cblobs
,
460 struct symlink_reparse_struct
**symlink
)
462 struct cli_smb2_create_fnum_state
*state
= tevent_req_data(
463 req
, struct cli_smb2_create_fnum_state
);
466 if (tevent_req_is_nterror(req
, &status
)) {
467 if (NT_STATUS_EQUAL(status
, NT_STATUS_STOPPED_ON_SYMLINK
) &&
469 *symlink
= talloc_move(mem_ctx
, &state
->symlink
);
474 *pfnum
= state
->fnum
;
479 if (out_cblobs
!= NULL
) {
480 *out_cblobs
= (struct smb2_create_blobs
) {
481 .num_blobs
= state
->out_cblobs
.num_blobs
,
482 .blobs
= talloc_move(
483 mem_ctx
, &state
->out_cblobs
.blobs
),
489 bool cli_smb2_fnum_is_posix(struct cli_state
*cli
, uint16_t fnum
)
491 struct smb2_hnd
*ph
= NULL
;
494 status
= map_fnum_to_smb2_handle(cli
, fnum
, &ph
);
495 if (!NT_STATUS_IS_OK(status
)) {
501 NTSTATUS
cli_smb2_create_fnum(
502 struct cli_state
*cli
,
504 struct cli_smb2_create_flags create_flags
,
505 uint32_t impersonation_level
,
506 uint32_t desired_access
,
507 uint32_t file_attributes
,
508 uint32_t share_access
,
509 uint32_t create_disposition
,
510 uint32_t create_options
,
511 const struct smb2_create_blobs
*in_cblobs
,
513 struct smb_create_returns
*cr
,
515 struct smb2_create_blobs
*out_cblobs
)
517 TALLOC_CTX
*frame
= talloc_stackframe();
518 struct tevent_context
*ev
;
519 struct tevent_req
*req
;
520 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
522 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
524 * Can't use sync call while an async call is in flight
526 status
= NT_STATUS_INVALID_PARAMETER
;
529 ev
= samba_tevent_context_init(frame
);
533 req
= cli_smb2_create_fnum_send(
549 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
552 status
= cli_smb2_create_fnum_recv(
553 req
, pfid
, cr
, mem_ctx
, out_cblobs
, NULL
);
559 /***************************************************************
560 Small wrapper that allows SMB2 close to use a uint16_t fnum.
561 ***************************************************************/
563 struct cli_smb2_close_fnum_state
{
564 struct cli_state
*cli
;
569 static void cli_smb2_close_fnum_done(struct tevent_req
*subreq
);
571 struct tevent_req
*cli_smb2_close_fnum_send(TALLOC_CTX
*mem_ctx
,
572 struct tevent_context
*ev
,
573 struct cli_state
*cli
,
577 struct tevent_req
*req
, *subreq
;
578 struct cli_smb2_close_fnum_state
*state
;
581 req
= tevent_req_create(mem_ctx
, &state
,
582 struct cli_smb2_close_fnum_state
);
589 status
= map_fnum_to_smb2_handle(cli
, fnum
, &state
->ph
);
590 if (tevent_req_nterror(req
, status
)) {
591 return tevent_req_post(req
, ev
);
594 subreq
= smb2cli_close_send(state
,
601 state
->ph
->fid_persistent
,
602 state
->ph
->fid_volatile
);
603 if (tevent_req_nomem(subreq
, req
)) {
604 return tevent_req_post(req
, ev
);
606 tevent_req_set_callback(subreq
, cli_smb2_close_fnum_done
, req
);
610 static void cli_smb2_close_fnum_done(struct tevent_req
*subreq
)
612 struct tevent_req
*req
= tevent_req_callback_data(
613 subreq
, struct tevent_req
);
614 struct cli_smb2_close_fnum_state
*state
= tevent_req_data(
615 req
, struct cli_smb2_close_fnum_state
);
618 status
= smb2cli_close_recv(subreq
);
619 if (tevent_req_nterror(req
, status
)) {
623 /* Delete the fnum -> handle mapping. */
624 status
= delete_smb2_handle_mapping(state
->cli
, &state
->ph
,
626 if (tevent_req_nterror(req
, status
)) {
629 tevent_req_done(req
);
632 NTSTATUS
cli_smb2_close_fnum_recv(struct tevent_req
*req
)
634 return tevent_req_simple_recv_ntstatus(req
);
637 NTSTATUS
cli_smb2_close_fnum(struct cli_state
*cli
, uint16_t fnum
)
639 TALLOC_CTX
*frame
= talloc_stackframe();
640 struct tevent_context
*ev
;
641 struct tevent_req
*req
;
642 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
644 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
646 * Can't use sync call while an async call is in flight
648 status
= NT_STATUS_INVALID_PARAMETER
;
651 ev
= samba_tevent_context_init(frame
);
655 req
= cli_smb2_close_fnum_send(frame
, ev
, cli
, fnum
, 0);
659 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
662 status
= cli_smb2_close_fnum_recv(req
);
668 struct cli_smb2_set_info_fnum_state
{
672 static void cli_smb2_set_info_fnum_done(struct tevent_req
*subreq
);
674 struct tevent_req
*cli_smb2_set_info_fnum_send(
676 struct tevent_context
*ev
,
677 struct cli_state
*cli
,
679 uint8_t in_info_type
,
680 uint8_t in_info_class
,
681 const DATA_BLOB
*in_input_buffer
,
682 uint32_t in_additional_info
)
684 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
685 struct cli_smb2_set_info_fnum_state
*state
= NULL
;
686 struct smb2_hnd
*ph
= NULL
;
689 req
= tevent_req_create(
690 mem_ctx
, &state
, struct cli_smb2_set_info_fnum_state
);
695 status
= map_fnum_to_smb2_handle(cli
, fnum
, &ph
);
696 if (tevent_req_nterror(req
, status
)) {
697 return tevent_req_post(req
, ev
);
700 subreq
= smb2cli_set_info_send(
713 if (tevent_req_nomem(subreq
, req
)) {
714 return tevent_req_post(req
, ev
);
716 tevent_req_set_callback(subreq
, cli_smb2_set_info_fnum_done
, req
);
720 static void cli_smb2_set_info_fnum_done(struct tevent_req
*subreq
)
722 NTSTATUS status
= smb2cli_set_info_recv(subreq
);
723 tevent_req_simple_finish_ntstatus(subreq
, status
);
726 NTSTATUS
cli_smb2_set_info_fnum_recv(struct tevent_req
*req
)
728 return tevent_req_simple_recv_ntstatus(req
);
731 NTSTATUS
cli_smb2_set_info_fnum(
732 struct cli_state
*cli
,
734 uint8_t in_info_type
,
735 uint8_t in_info_class
,
736 const DATA_BLOB
*in_input_buffer
,
737 uint32_t in_additional_info
)
739 TALLOC_CTX
*frame
= talloc_stackframe();
740 struct tevent_context
*ev
= NULL
;
741 struct tevent_req
*req
= NULL
;
742 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
745 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
747 * Can't use sync call while an async call is in flight
749 status
= NT_STATUS_INVALID_PARAMETER
;
752 ev
= samba_tevent_context_init(frame
);
756 req
= cli_smb2_set_info_fnum_send(
768 ok
= tevent_req_poll_ntstatus(req
, ev
, &status
);
772 status
= cli_smb2_set_info_fnum_recv(req
);
778 struct cli_smb2_delete_on_close_state
{
779 struct cli_state
*cli
;
784 static void cli_smb2_delete_on_close_done(struct tevent_req
*subreq
);
786 struct tevent_req
*cli_smb2_delete_on_close_send(TALLOC_CTX
*mem_ctx
,
787 struct tevent_context
*ev
,
788 struct cli_state
*cli
,
792 struct tevent_req
*req
= NULL
;
793 struct cli_smb2_delete_on_close_state
*state
= NULL
;
794 struct tevent_req
*subreq
= NULL
;
796 req
= tevent_req_create(mem_ctx
, &state
,
797 struct cli_smb2_delete_on_close_state
);
803 /* Setup data array. */
804 SCVAL(&state
->data
[0], 0, flag
? 1 : 0);
805 state
->inbuf
.data
= &state
->data
[0];
806 state
->inbuf
.length
= 1;
808 subreq
= cli_smb2_set_info_fnum_send(state
,
813 FSCC_FILE_DISPOSITION_INFORMATION
,
816 if (tevent_req_nomem(subreq
, req
)) {
817 return tevent_req_post(req
, ev
);
819 tevent_req_set_callback(subreq
,
820 cli_smb2_delete_on_close_done
,
825 static void cli_smb2_delete_on_close_done(struct tevent_req
*subreq
)
827 NTSTATUS status
= cli_smb2_set_info_fnum_recv(subreq
);
828 tevent_req_simple_finish_ntstatus(subreq
, status
);
831 NTSTATUS
cli_smb2_delete_on_close_recv(struct tevent_req
*req
)
833 return tevent_req_simple_recv_ntstatus(req
);
836 NTSTATUS
cli_smb2_delete_on_close(struct cli_state
*cli
, uint16_t fnum
, bool flag
)
838 TALLOC_CTX
*frame
= talloc_stackframe();
839 struct tevent_context
*ev
;
840 struct tevent_req
*req
;
841 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
843 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
845 * Can't use sync call while an async call is in flight
847 status
= NT_STATUS_INVALID_PARAMETER
;
850 ev
= samba_tevent_context_init(frame
);
854 req
= cli_smb2_delete_on_close_send(frame
, ev
, cli
, fnum
, flag
);
858 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
861 status
= cli_smb2_delete_on_close_recv(req
);
867 struct cli_smb2_mkdir_state
{
868 struct tevent_context
*ev
;
869 struct cli_state
*cli
;
872 static void cli_smb2_mkdir_opened(struct tevent_req
*subreq
);
873 static void cli_smb2_mkdir_closed(struct tevent_req
*subreq
);
875 struct tevent_req
*cli_smb2_mkdir_send(
877 struct tevent_context
*ev
,
878 struct cli_state
*cli
,
881 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
882 struct cli_smb2_mkdir_state
*state
= NULL
;
884 req
= tevent_req_create(
885 mem_ctx
, &state
, struct cli_smb2_mkdir_state
);
892 /* Ensure this is a directory. */
893 subreq
= cli_smb2_create_fnum_send(
898 (struct cli_smb2_create_flags
){0}, /* create_flags */
899 SMB2_IMPERSONATION_IMPERSONATION
, /* impersonation_level */
900 FILE_READ_ATTRIBUTES
, /* desired_access */
901 FILE_ATTRIBUTE_DIRECTORY
, /* file_attributes */
903 FILE_SHARE_WRITE
, /* share_access */
904 FILE_CREATE
, /* create_disposition */
905 FILE_DIRECTORY_FILE
, /* create_options */
906 NULL
); /* in_cblobs */
907 if (tevent_req_nomem(subreq
, req
)) {
908 return tevent_req_post(req
, ev
);
910 tevent_req_set_callback(subreq
, cli_smb2_mkdir_opened
, req
);
914 static void cli_smb2_mkdir_opened(struct tevent_req
*subreq
)
916 struct tevent_req
*req
= tevent_req_callback_data(
917 subreq
, struct tevent_req
);
918 struct cli_smb2_mkdir_state
*state
= tevent_req_data(
919 req
, struct cli_smb2_mkdir_state
);
921 uint16_t fnum
= 0xffff;
923 status
= cli_smb2_create_fnum_recv(
924 subreq
, &fnum
, NULL
, NULL
, NULL
, NULL
);
926 if (tevent_req_nterror(req
, status
)) {
931 cli_smb2_close_fnum_send(state
, state
->ev
, state
->cli
, fnum
, 0);
932 if (tevent_req_nomem(subreq
, req
)) {
935 tevent_req_set_callback(subreq
, cli_smb2_mkdir_closed
, req
);
938 static void cli_smb2_mkdir_closed(struct tevent_req
*subreq
)
940 NTSTATUS status
= cli_smb2_close_fnum_recv(subreq
);
941 tevent_req_simple_finish_ntstatus(subreq
, status
);
944 NTSTATUS
cli_smb2_mkdir_recv(struct tevent_req
*req
)
946 return tevent_req_simple_recv_ntstatus(req
);
949 struct cli_smb2_rmdir_state
{
950 struct tevent_context
*ev
;
951 struct cli_state
*cli
;
953 const struct smb2_create_blobs
*in_cblobs
;
958 static void cli_smb2_rmdir_opened1(struct tevent_req
*subreq
);
959 static void cli_smb2_rmdir_opened2(struct tevent_req
*subreq
);
960 static void cli_smb2_rmdir_disp_set(struct tevent_req
*subreq
);
961 static void cli_smb2_rmdir_closed(struct tevent_req
*subreq
);
963 struct tevent_req
*cli_smb2_rmdir_send(
965 struct tevent_context
*ev
,
966 struct cli_state
*cli
,
968 const struct smb2_create_blobs
*in_cblobs
)
970 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
971 struct cli_smb2_rmdir_state
*state
= NULL
;
973 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_rmdir_state
);
979 state
->dname
= dname
;
980 state
->in_cblobs
= in_cblobs
;
982 subreq
= cli_smb2_create_fnum_send(
987 (struct cli_smb2_create_flags
){0},
988 SMB2_IMPERSONATION_IMPERSONATION
,
989 DELETE_ACCESS
, /* desired_access */
990 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
991 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, /* share_access */
992 FILE_OPEN
, /* create_disposition */
993 FILE_DIRECTORY_FILE
, /* create_options */
994 state
->in_cblobs
); /* in_cblobs */
995 if (tevent_req_nomem(subreq
, req
)) {
996 return tevent_req_post(req
, ev
);
998 tevent_req_set_callback(subreq
, cli_smb2_rmdir_opened1
, req
);
1002 static void cli_smb2_rmdir_opened1(struct tevent_req
*subreq
)
1004 struct tevent_req
*req
= tevent_req_callback_data(
1005 subreq
, struct tevent_req
);
1006 struct cli_smb2_rmdir_state
*state
= tevent_req_data(
1007 req
, struct cli_smb2_rmdir_state
);
1010 status
= cli_smb2_create_fnum_recv(
1011 subreq
, &state
->fnum
, NULL
, NULL
, NULL
, NULL
);
1012 TALLOC_FREE(subreq
);
1014 if (NT_STATUS_EQUAL(status
, NT_STATUS_STOPPED_ON_SYMLINK
)) {
1016 * Naive option to match our SMB1 code. Assume the
1017 * symlink path that tripped us up was the last
1018 * component and try again. Eventually we will have to
1019 * deal with the returned path unprocessed component. JRA.
1021 subreq
= cli_smb2_create_fnum_send(
1026 (struct cli_smb2_create_flags
){0},
1027 SMB2_IMPERSONATION_IMPERSONATION
,
1028 DELETE_ACCESS
, /* desired_access */
1029 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
1030 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
1031 FILE_OPEN
, /* create_disposition */
1032 FILE_DIRECTORY_FILE
|
1033 FILE_DELETE_ON_CLOSE
|
1034 FILE_OPEN_REPARSE_POINT
, /* create_options */
1035 state
->in_cblobs
); /* in_cblobs */
1036 if (tevent_req_nomem(subreq
, req
)) {
1039 tevent_req_set_callback(subreq
, cli_smb2_rmdir_opened2
, req
);
1043 if (tevent_req_nterror(req
, status
)) {
1047 subreq
= cli_smb2_delete_on_close_send(
1048 state
, state
->ev
, state
->cli
, state
->fnum
, true);
1049 if (tevent_req_nomem(subreq
, req
)) {
1052 tevent_req_set_callback(subreq
, cli_smb2_rmdir_disp_set
, req
);
1055 static void cli_smb2_rmdir_opened2(struct tevent_req
*subreq
)
1057 struct tevent_req
*req
= tevent_req_callback_data(
1058 subreq
, struct tevent_req
);
1059 struct cli_smb2_rmdir_state
*state
= tevent_req_data(
1060 req
, struct cli_smb2_rmdir_state
);
1063 status
= cli_smb2_create_fnum_recv(
1064 subreq
, &state
->fnum
, NULL
, NULL
, NULL
, NULL
);
1065 TALLOC_FREE(subreq
);
1066 if (tevent_req_nterror(req
, status
)) {
1070 subreq
= cli_smb2_delete_on_close_send(
1071 state
, state
->ev
, state
->cli
, state
->fnum
, true);
1072 if (tevent_req_nomem(subreq
, req
)) {
1075 tevent_req_set_callback(subreq
, cli_smb2_rmdir_disp_set
, req
);
1078 static void cli_smb2_rmdir_disp_set(struct tevent_req
*subreq
)
1080 struct tevent_req
*req
= tevent_req_callback_data(
1081 subreq
, struct tevent_req
);
1082 struct cli_smb2_rmdir_state
*state
= tevent_req_data(
1083 req
, struct cli_smb2_rmdir_state
);
1085 state
->status
= cli_smb2_delete_on_close_recv(subreq
);
1086 TALLOC_FREE(subreq
);
1089 * Close the fd even if the set_disp failed
1092 subreq
= cli_smb2_close_fnum_send(state
,
1097 if (tevent_req_nomem(subreq
, req
)) {
1100 tevent_req_set_callback(subreq
, cli_smb2_rmdir_closed
, req
);
1103 static void cli_smb2_rmdir_closed(struct tevent_req
*subreq
)
1105 NTSTATUS status
= cli_smb2_close_fnum_recv(subreq
);
1106 tevent_req_simple_finish_ntstatus(subreq
, status
);
1109 NTSTATUS
cli_smb2_rmdir_recv(struct tevent_req
*req
)
1111 struct cli_smb2_rmdir_state
*state
= tevent_req_data(
1112 req
, struct cli_smb2_rmdir_state
);
1115 if (tevent_req_is_nterror(req
, &status
)) {
1118 return state
->status
;
1121 /***************************************************************
1122 Small wrapper that allows SMB2 to unlink a pathname.
1123 ***************************************************************/
1125 struct cli_smb2_unlink_state
{
1126 struct tevent_context
*ev
;
1127 struct cli_state
*cli
;
1129 const struct smb2_create_blobs
*in_cblobs
;
1132 static void cli_smb2_unlink_opened1(struct tevent_req
*subreq
);
1133 static void cli_smb2_unlink_opened2(struct tevent_req
*subreq
);
1134 static void cli_smb2_unlink_closed(struct tevent_req
*subreq
);
1136 struct tevent_req
*cli_smb2_unlink_send(
1137 TALLOC_CTX
*mem_ctx
,
1138 struct tevent_context
*ev
,
1139 struct cli_state
*cli
,
1141 const struct smb2_create_blobs
*in_cblobs
)
1143 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
1144 struct cli_smb2_unlink_state
*state
= NULL
;
1146 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_unlink_state
);
1152 state
->fname
= fname
;
1153 state
->in_cblobs
= in_cblobs
;
1155 subreq
= cli_smb2_create_fnum_send(
1156 state
, /* mem_ctx */
1157 state
->ev
, /* tevent_context */
1158 state
->cli
, /* cli_struct */
1159 state
->fname
, /* filename */
1160 (struct cli_smb2_create_flags
){0},
1161 SMB2_IMPERSONATION_IMPERSONATION
,
1162 DELETE_ACCESS
, /* desired_access */
1163 FILE_ATTRIBUTE_NORMAL
, /* file attributes */
1166 FILE_SHARE_DELETE
, /* share_access */
1167 FILE_OPEN
, /* create_disposition */
1168 FILE_DELETE_ON_CLOSE
, /* create_options */
1169 state
->in_cblobs
); /* in_cblobs */
1170 if (tevent_req_nomem(subreq
, req
)) {
1171 return tevent_req_post(req
, ev
);
1173 tevent_req_set_callback(subreq
, cli_smb2_unlink_opened1
, req
);
1177 static void cli_smb2_unlink_opened1(struct tevent_req
*subreq
)
1179 struct tevent_req
*req
= tevent_req_callback_data(
1180 subreq
, struct tevent_req
);
1181 struct cli_smb2_unlink_state
*state
= tevent_req_data(
1182 req
, struct cli_smb2_unlink_state
);
1183 uint16_t fnum
= 0xffff;
1186 status
= cli_smb2_create_fnum_recv(
1187 subreq
, &fnum
, NULL
, NULL
, NULL
, NULL
);
1188 TALLOC_FREE(subreq
);
1190 if (NT_STATUS_EQUAL(status
, NT_STATUS_STOPPED_ON_SYMLINK
) ||
1191 NT_STATUS_EQUAL(status
, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED
)) {
1193 * Naive option to match our SMB1 code. Assume the
1194 * symlink path that tripped us up was the last
1195 * component and try again. Eventually we will have to
1196 * deal with the returned path unprocessed component. JRA.
1198 subreq
= cli_smb2_create_fnum_send(
1199 state
, /* mem_ctx */
1200 state
->ev
, /* tevent_context */
1201 state
->cli
, /* cli_struct */
1202 state
->fname
, /* filename */
1203 (struct cli_smb2_create_flags
){0},
1204 SMB2_IMPERSONATION_IMPERSONATION
,
1205 DELETE_ACCESS
, /* desired_access */
1206 FILE_ATTRIBUTE_NORMAL
, /* file attributes */
1209 FILE_SHARE_DELETE
, /* share_access */
1210 FILE_OPEN
, /* create_disposition */
1211 FILE_DELETE_ON_CLOSE
|
1212 FILE_OPEN_REPARSE_POINT
, /* create_options */
1213 state
->in_cblobs
); /* in_cblobs */
1214 if (tevent_req_nomem(subreq
, req
)) {
1217 tevent_req_set_callback(subreq
, cli_smb2_unlink_opened2
, req
);
1221 if (tevent_req_nterror(req
, status
)) {
1226 cli_smb2_close_fnum_send(state
, state
->ev
, state
->cli
, fnum
, 0);
1227 if (tevent_req_nomem(subreq
, req
)) {
1230 tevent_req_set_callback(subreq
, cli_smb2_unlink_closed
, req
);
1233 static void cli_smb2_unlink_opened2(struct tevent_req
*subreq
)
1235 struct tevent_req
*req
= tevent_req_callback_data(
1236 subreq
, struct tevent_req
);
1237 struct cli_smb2_unlink_state
*state
= tevent_req_data(
1238 req
, struct cli_smb2_unlink_state
);
1239 uint16_t fnum
= 0xffff;
1242 status
= cli_smb2_create_fnum_recv(
1243 subreq
, &fnum
, NULL
, NULL
, NULL
, NULL
);
1244 TALLOC_FREE(subreq
);
1245 if (tevent_req_nterror(req
, status
)) {
1250 cli_smb2_close_fnum_send(state
, state
->ev
, state
->cli
, fnum
, 0);
1251 if (tevent_req_nomem(subreq
, req
)) {
1254 tevent_req_set_callback(subreq
, cli_smb2_unlink_closed
, req
);
1257 static void cli_smb2_unlink_closed(struct tevent_req
*subreq
)
1259 NTSTATUS status
= cli_smb2_close_fnum_recv(subreq
);
1260 tevent_req_simple_finish_ntstatus(subreq
, status
);
1263 NTSTATUS
cli_smb2_unlink_recv(struct tevent_req
*req
)
1265 return tevent_req_simple_recv_ntstatus(req
);
1268 /***************************************************************
1269 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1270 ***************************************************************/
1272 static NTSTATUS
parse_finfo_posix_info(const uint8_t *dir_data
,
1273 uint32_t dir_data_length
,
1274 struct file_info
*finfo
,
1275 uint32_t *next_offset
)
1277 struct smb3_file_posix_information info
= {};
1279 enum ndr_err_code ndr_err
;
1282 uint32_t _next_offset
= 0;
1284 if (dir_data_length
< 4) {
1285 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1288 _next_offset
= IVAL(dir_data
, 0);
1290 if (_next_offset
> dir_data_length
) {
1291 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1294 if (_next_offset
!= 0) {
1295 /* Ensure we only read what in this record. */
1296 dir_data_length
= _next_offset
;
1300 * Skip NextEntryOffset and FileIndex
1302 if (dir_data_length
< 8) {
1303 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1306 dir_data_length
-= 8;
1308 ndr_err
= ndr_pull_struct_blob_noalloc(
1312 (ndr_pull_flags_fn_t
)ndr_pull_smb3_file_posix_information
,
1314 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1315 return ndr_map_error2ntstatus(ndr_err
);
1317 if (consumed
> dir_data_length
) {
1318 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1320 dir_data
+= consumed
;
1321 dir_data_length
-= consumed
;
1323 finfo
->btime_ts
= interpret_long_date(info
.creation_time
);
1324 finfo
->atime_ts
= interpret_long_date(info
.last_access_time
);
1325 finfo
->mtime_ts
= interpret_long_date(info
.last_write_time
);
1326 finfo
->ctime_ts
= interpret_long_date(info
.change_time
);
1327 finfo
->allocated_size
= info
.allocation_size
;
1328 finfo
->size
= info
.end_of_file
;
1329 finfo
->attr
= info
.file_attributes
;
1330 finfo
->ino
= info
.inode
;
1331 finfo
->st_ex_dev
= info
.device
;
1332 finfo
->st_ex_nlink
= info
.cc
.nlinks
;
1333 finfo
->reparse_tag
= info
.cc
.reparse_tag
;
1334 finfo
->st_ex_mode
= wire_mode_to_unix(info
.cc
.posix_mode
);
1335 sid_copy(&finfo
->owner_sid
, &info
.cc
.owner
);
1336 sid_copy(&finfo
->group_sid
, &info
.cc
.group
);
1338 if (dir_data_length
< 4) {
1339 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1341 namelen
= PULL_LE_U32(dir_data
, 0);
1344 dir_data_length
-= 4;
1346 if (namelen
> dir_data_length
) {
1347 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1350 ret
= pull_string_talloc(finfo
,
1352 FLAGS2_UNICODE_STRINGS
,
1357 if (ret
== (size_t)-1) {
1358 /* Bad conversion. */
1359 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1362 if (finfo
->name
== NULL
) {
1363 /* Bad conversion. */
1364 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1367 *next_offset
= _next_offset
;
1368 return NT_STATUS_OK
;
1371 /***************************************************************
1372 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1373 ***************************************************************/
1375 static NTSTATUS
parse_finfo_id_both_directory_info(const uint8_t *dir_data
,
1376 uint32_t dir_data_length
,
1377 struct file_info
*finfo
,
1378 uint32_t *next_offset
)
1384 if (dir_data_length
< 4) {
1385 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1388 *next_offset
= IVAL(dir_data
, 0);
1390 if (*next_offset
> dir_data_length
) {
1391 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1394 if (*next_offset
!= 0) {
1395 /* Ensure we only read what in this record. */
1396 dir_data_length
= *next_offset
;
1399 if (dir_data_length
< 105) {
1400 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1403 finfo
->btime_ts
= interpret_long_date(BVAL(dir_data
, 8));
1404 finfo
->atime_ts
= interpret_long_date(BVAL(dir_data
, 16));
1405 finfo
->mtime_ts
= interpret_long_date(BVAL(dir_data
, 24));
1406 finfo
->ctime_ts
= interpret_long_date(BVAL(dir_data
, 32));
1407 finfo
->size
= BVAL(dir_data
+ 40, 0);
1408 finfo
->allocated_size
= BVAL(dir_data
+ 48, 0);
1409 finfo
->attr
= IVAL(dir_data
+ 56, 0);
1410 finfo
->ino
= BVAL(dir_data
+ 96, 0);
1411 namelen
= IVAL(dir_data
+ 60,0);
1412 if (namelen
> (dir_data_length
- 104)) {
1413 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1415 finfo
->reparse_tag
= IVAL(dir_data
+ 64, 0);
1416 slen
= CVAL(dir_data
+ 68, 0);
1418 return NT_STATUS_INFO_LENGTH_MISMATCH
;
1420 ret
= pull_string_talloc(finfo
,
1422 FLAGS2_UNICODE_STRINGS
,
1427 if (ret
== (size_t)-1) {
1428 /* Bad conversion. */
1429 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1432 ret
= pull_string_talloc(finfo
,
1434 FLAGS2_UNICODE_STRINGS
,
1439 if (ret
== (size_t)-1) {
1440 /* Bad conversion. */
1441 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1444 if (finfo
->name
== NULL
) {
1445 /* Bad conversion. */
1446 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
1449 return NT_STATUS_OK
;
1452 /*******************************************************************
1453 Given a filename - get its directory name
1454 ********************************************************************/
1456 static bool windows_parent_dirname(TALLOC_CTX
*mem_ctx
,
1464 p
= strrchr_m(dir
, '\\'); /* Find final '\\', if any */
1467 if (!(*parent
= talloc_strdup(mem_ctx
, "\\"))) {
1478 if (!(*parent
= (char *)talloc_memdup(mem_ctx
, dir
, len
+1))) {
1481 (*parent
)[len
] = '\0';
1489 struct cli_smb2_list_dir_data
{
1494 struct cli_smb2_list_state
{
1495 struct tevent_context
*ev
;
1496 struct cli_state
*cli
;
1502 struct cli_smb2_list_dir_data
*response
;
1504 unsigned int info_level
;
1507 static void cli_smb2_list_opened(struct tevent_req
*subreq
);
1508 static void cli_smb2_list_done(struct tevent_req
*subreq
);
1509 static void cli_smb2_list_closed(struct tevent_req
*subreq
);
1511 struct tevent_req
*cli_smb2_list_send(
1512 TALLOC_CTX
*mem_ctx
,
1513 struct tevent_context
*ev
,
1514 struct cli_state
*cli
,
1515 const char *pathname
,
1516 unsigned int info_level
)
1518 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
1519 struct cli_smb2_list_state
*state
= NULL
;
1520 char *parent
= NULL
;
1522 struct smb2_create_blobs
*in_cblobs
= NULL
;
1524 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_list_state
);
1530 state
->status
= NT_STATUS_OK
;
1531 state
->info_level
= info_level
;
1533 ok
= windows_parent_dirname(state
, pathname
, &parent
, &state
->mask
);
1535 tevent_req_oom(req
);
1536 return tevent_req_post(req
, ev
);
1539 if (smbXcli_conn_have_posix(cli
->conn
) &&
1540 info_level
== SMB2_FIND_POSIX_INFORMATION
)
1544 /* The mode MUST be 0 when opening an existing file/dir, and
1545 * will be ignored by the server.
1547 uint8_t linear_mode
[4] = { 0 };
1548 DATA_BLOB blob
= { .data
=linear_mode
,
1549 .length
=sizeof(linear_mode
) };
1551 in_cblobs
= talloc_zero(mem_ctx
, struct smb2_create_blobs
);
1552 if (in_cblobs
== NULL
) {
1556 status
= smb2_create_blob_add(in_cblobs
, in_cblobs
,
1557 SMB2_CREATE_TAG_POSIX
, blob
);
1558 if (tevent_req_nterror(req
, status
)) {
1559 tevent_req_nterror(req
, status
);
1560 return tevent_req_post(req
, ev
);
1564 subreq
= cli_smb2_create_fnum_send(
1565 state
, /* mem_ctx */
1569 (struct cli_smb2_create_flags
){0}, /* create_flags */
1570 SMB2_IMPERSONATION_IMPERSONATION
, /* impersonation_level */
1571 SEC_DIR_LIST
|SEC_DIR_READ_ATTRIBUTE
, /* desired_access */
1572 FILE_ATTRIBUTE_DIRECTORY
, /* file_attributes */
1573 FILE_SHARE_READ
|FILE_SHARE_WRITE
, /* share_access */
1574 FILE_OPEN
, /* create_disposition */
1575 FILE_DIRECTORY_FILE
, /* create_options */
1576 in_cblobs
); /* in_cblobs */
1577 TALLOC_FREE(in_cblobs
);
1578 if (tevent_req_nomem(subreq
, req
)) {
1579 return tevent_req_post(req
, ev
);
1581 tevent_req_set_callback(subreq
, cli_smb2_list_opened
, req
);
1585 static void cli_smb2_list_opened(struct tevent_req
*subreq
)
1587 struct tevent_req
*req
= tevent_req_callback_data(
1588 subreq
, struct tevent_req
);
1589 struct cli_smb2_list_state
*state
= tevent_req_data(
1590 req
, struct cli_smb2_list_state
);
1593 status
= cli_smb2_create_fnum_recv(
1594 subreq
, &state
->fnum
, NULL
, NULL
, NULL
, NULL
);
1595 TALLOC_FREE(subreq
);
1596 if (tevent_req_nterror(req
, status
)) {
1601 * Make our caller get back to us via cli_smb2_list_recv(),
1602 * triggering the smb2_query_directory_send()
1604 tevent_req_defer_callback(req
, state
->ev
);
1605 tevent_req_notify_callback(req
);
1608 static void cli_smb2_list_done(struct tevent_req
*subreq
)
1610 struct tevent_req
*req
= tevent_req_callback_data(
1611 subreq
, struct tevent_req
);
1612 struct cli_smb2_list_state
*state
= tevent_req_data(
1613 req
, struct cli_smb2_list_state
);
1614 struct cli_smb2_list_dir_data
*response
= NULL
;
1616 response
= talloc(state
, struct cli_smb2_list_dir_data
);
1617 if (tevent_req_nomem(response
, req
)) {
1621 state
->status
= smb2cli_query_directory_recv(
1622 subreq
, response
, &response
->data
, &response
->length
);
1623 TALLOC_FREE(subreq
);
1625 if (NT_STATUS_IS_OK(state
->status
)) {
1626 state
->response
= response
;
1629 tevent_req_defer_callback(req
, state
->ev
);
1630 tevent_req_notify_callback(req
);
1634 TALLOC_FREE(response
);
1636 subreq
= cli_smb2_close_fnum_send(state
,
1641 if (tevent_req_nomem(subreq
, req
)) {
1644 tevent_req_set_callback(subreq
, cli_smb2_list_closed
, req
);
1647 static void cli_smb2_list_closed(struct tevent_req
*subreq
)
1649 NTSTATUS status
= cli_smb2_close_fnum_recv(subreq
);
1650 tevent_req_simple_finish_ntstatus(subreq
, status
);
1654 * Return the next finfo directory.
1656 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1657 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1658 * NT_STATUS_RETRY, which will then trigger the caller again when the
1659 * QUERY_DIRECTORY has returned with another buffer. This way we
1660 * guarantee that no asynchronous request is open after this call
1661 * returns an entry, so that other synchronous requests can be issued
1662 * on the same connection while the directory listing proceeds.
1664 NTSTATUS
cli_smb2_list_recv(
1665 struct tevent_req
*req
,
1666 TALLOC_CTX
*mem_ctx
,
1667 struct file_info
**pfinfo
)
1669 struct cli_smb2_list_state
*state
= tevent_req_data(
1670 req
, struct cli_smb2_list_state
);
1671 struct cli_smb2_list_dir_data
*response
= NULL
;
1672 struct file_info
*finfo
= NULL
;
1674 uint32_t next_offset
= 0;
1677 in_progress
= tevent_req_is_in_progress(req
);
1680 if (!tevent_req_is_nterror(req
, &status
)) {
1681 status
= NT_STATUS_NO_MORE_FILES
;
1686 response
= state
->response
;
1687 if (response
== NULL
) {
1688 struct tevent_req
*subreq
= NULL
;
1689 struct cli_state
*cli
= state
->cli
;
1690 struct smb2_hnd
*ph
= NULL
;
1691 uint32_t max_trans
, max_avail_len
;
1694 if (!NT_STATUS_IS_OK(state
->status
)) {
1695 status
= state
->status
;
1699 status
= map_fnum_to_smb2_handle(cli
, state
->fnum
, &ph
);
1700 if (!NT_STATUS_IS_OK(status
)) {
1704 max_trans
= smb2cli_conn_max_trans_size(cli
->conn
);
1705 ok
= smb2cli_conn_req_possible(cli
->conn
, &max_avail_len
);
1707 max_trans
= MIN(max_trans
, max_avail_len
);
1710 subreq
= smb2cli_query_directory_send(
1711 state
, /* mem_ctx */
1713 cli
->conn
, /* conn */
1714 cli
->timeout
, /* timeout_msec */
1715 cli
->smb2
.session
, /* session */
1716 cli
->smb2
.tcon
, /* tcon */
1717 state
->info_level
, /* level */
1720 ph
->fid_persistent
, /* fid_persistent */
1721 ph
->fid_volatile
, /* fid_volatile */
1722 state
->mask
, /* mask */
1723 max_trans
); /* outbuf_len */
1724 if (subreq
== NULL
) {
1725 status
= NT_STATUS_NO_MEMORY
;
1728 tevent_req_set_callback(subreq
, cli_smb2_list_done
, req
);
1729 return NT_STATUS_RETRY
;
1732 SMB_ASSERT(response
->length
> state
->offset
);
1734 finfo
= talloc_zero(mem_ctx
, struct file_info
);
1735 if (finfo
== NULL
) {
1736 status
= NT_STATUS_NO_MEMORY
;
1740 if (state
->info_level
== SMB2_FIND_POSIX_INFORMATION
) {
1741 status
= parse_finfo_posix_info(
1742 response
->data
+ state
->offset
,
1743 response
->length
- state
->offset
,
1747 status
= parse_finfo_id_both_directory_info(
1748 response
->data
+ state
->offset
,
1749 response
->length
- state
->offset
,
1753 if (!NT_STATUS_IS_OK(status
)) {
1757 status
= is_bad_finfo_name(state
->cli
, finfo
);
1758 if (!NT_STATUS_IS_OK(status
)) {
1763 * parse_finfo_id_both_directory_info() checks for overflow,
1764 * no need to check again here.
1766 state
->offset
+= next_offset
;
1768 if (next_offset
== 0) {
1769 TALLOC_FREE(state
->response
);
1772 tevent_req_defer_callback(req
, state
->ev
);
1773 tevent_req_notify_callback(req
);
1776 return NT_STATUS_OK
;
1780 tevent_req_received(req
);
1784 /***************************************************************
1785 Wrapper that allows SMB2 to query a path info (basic level).
1787 ***************************************************************/
1789 NTSTATUS
cli_smb2_qpathinfo_basic(struct cli_state
*cli
,
1791 SMB_STRUCT_STAT
*sbuf
,
1792 uint32_t *attributes
)
1795 struct smb_create_returns cr
;
1796 uint16_t fnum
= 0xffff;
1797 size_t namelen
= strlen(name
);
1799 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
1801 * Can't use sync call while an async call is in flight
1803 return NT_STATUS_INVALID_PARAMETER
;
1806 /* SMB2 is pickier about pathnames. Ensure it doesn't
1808 if (namelen
> 0 && name
[namelen
-1] == '\\') {
1809 char *modname
= talloc_strndup(talloc_tos(), name
, namelen
-1);
1810 if (modname
== NULL
) {
1811 return NT_STATUS_NO_MEMORY
;
1816 /* This is commonly used as a 'cd'. Try qpathinfo on
1817 a directory handle first. */
1819 status
= cli_smb2_create_fnum(cli
,
1821 (struct cli_smb2_create_flags
){0},
1822 SMB2_IMPERSONATION_IMPERSONATION
,
1823 FILE_READ_ATTRIBUTES
, /* desired_access */
1824 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
1825 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, /* share_access */
1826 FILE_OPEN
, /* create_disposition */
1827 FILE_DIRECTORY_FILE
, /* create_options */
1834 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_A_DIRECTORY
)) {
1835 /* Maybe a file ? */
1836 status
= cli_smb2_create_fnum(cli
,
1838 (struct cli_smb2_create_flags
){0},
1839 SMB2_IMPERSONATION_IMPERSONATION
,
1840 FILE_READ_ATTRIBUTES
, /* desired_access */
1841 0, /* file attributes */
1842 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, /* share_access */
1843 FILE_OPEN
, /* create_disposition */
1844 0, /* create_options */
1852 if (NT_STATUS_EQUAL(status
, NT_STATUS_STOPPED_ON_SYMLINK
)) {
1853 /* Maybe a reparse point ? */
1854 status
= cli_smb2_create_fnum(cli
,
1856 (struct cli_smb2_create_flags
){0},
1857 SMB2_IMPERSONATION_IMPERSONATION
,
1858 FILE_READ_ATTRIBUTES
, /* desired_access */
1859 0, /* file attributes */
1860 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, /* share_access */
1861 FILE_OPEN
, /* create_disposition */
1862 FILE_OPEN_REPARSE_POINT
, /* create_options */
1870 if (!NT_STATUS_IS_OK(status
)) {
1874 status
= cli_smb2_close_fnum(cli
, fnum
);
1878 sbuf
->st_ex_atime
= nt_time_to_unix_timespec(cr
.last_access_time
);
1879 sbuf
->st_ex_mtime
= nt_time_to_unix_timespec(cr
.last_write_time
);
1880 sbuf
->st_ex_ctime
= nt_time_to_unix_timespec(cr
.change_time
);
1881 sbuf
->st_ex_size
= cr
.end_of_file
;
1882 *attributes
= cr
.file_attributes
;
1887 struct cli_smb2_query_info_fnum_state
{
1891 static void cli_smb2_query_info_fnum_done(struct tevent_req
*subreq
);
1893 struct tevent_req
*cli_smb2_query_info_fnum_send(
1894 TALLOC_CTX
*mem_ctx
,
1895 struct tevent_context
*ev
,
1896 struct cli_state
*cli
,
1898 uint8_t in_info_type
,
1899 uint8_t in_info_class
,
1900 uint32_t in_max_output_length
,
1901 const DATA_BLOB
*in_input_buffer
,
1902 uint32_t in_additional_info
,
1905 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
1906 struct cli_smb2_query_info_fnum_state
*state
= NULL
;
1907 struct smb2_hnd
*ph
= NULL
;
1910 req
= tevent_req_create(
1911 mem_ctx
, &state
, struct cli_smb2_query_info_fnum_state
);
1916 status
= map_fnum_to_smb2_handle(cli
, fnum
, &ph
);
1917 if (tevent_req_nterror(req
, status
)) {
1918 return tevent_req_post(req
, ev
);
1921 subreq
= smb2cli_query_info_send(
1930 in_max_output_length
,
1936 if (tevent_req_nomem(subreq
, req
)) {
1937 return tevent_req_post(req
, ev
);
1939 tevent_req_set_callback(subreq
, cli_smb2_query_info_fnum_done
, req
);
1943 static void cli_smb2_query_info_fnum_done(struct tevent_req
*subreq
)
1945 struct tevent_req
*req
= tevent_req_callback_data(
1946 subreq
, struct tevent_req
);
1947 struct cli_smb2_query_info_fnum_state
*state
= tevent_req_data(
1948 req
, struct cli_smb2_query_info_fnum_state
);
1952 status
= smb2cli_query_info_recv(subreq
, state
, &outbuf
);
1953 TALLOC_FREE(subreq
);
1954 if (tevent_req_nterror(req
, status
)) {
1959 * We have to dup the memory here because outbuf.data is not
1960 * returned as a talloc object by smb2cli_query_info_recv.
1961 * It's a pointer into the received buffer.
1963 state
->outbuf
= data_blob_dup_talloc(state
, outbuf
);
1965 if ((outbuf
.length
!= 0) &&
1966 tevent_req_nomem(state
->outbuf
.data
, req
)) {
1969 tevent_req_done(req
);
1972 NTSTATUS
cli_smb2_query_info_fnum_recv(
1973 struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
, DATA_BLOB
*outbuf
)
1975 struct cli_smb2_query_info_fnum_state
*state
= tevent_req_data(
1976 req
, struct cli_smb2_query_info_fnum_state
);
1979 if (tevent_req_is_nterror(req
, &status
)) {
1982 *outbuf
= (DATA_BLOB
) {
1983 .data
= talloc_move(mem_ctx
, &state
->outbuf
.data
),
1984 .length
= state
->outbuf
.length
,
1986 tevent_req_received(req
);
1987 return NT_STATUS_OK
;
1990 NTSTATUS
cli_smb2_query_info_fnum(
1991 struct cli_state
*cli
,
1993 uint8_t in_info_type
,
1994 uint8_t in_info_class
,
1995 uint32_t in_max_output_length
,
1996 const DATA_BLOB
*in_input_buffer
,
1997 uint32_t in_additional_info
,
1999 TALLOC_CTX
*mem_ctx
,
2002 TALLOC_CTX
*frame
= talloc_stackframe();
2003 struct tevent_context
*ev
= NULL
;
2004 struct tevent_req
*req
= NULL
;
2005 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
2008 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2010 * Can't use sync call while an async call is in flight
2012 status
= NT_STATUS_INVALID_PARAMETER
;
2015 ev
= samba_tevent_context_init(frame
);
2019 req
= cli_smb2_query_info_fnum_send(
2026 in_max_output_length
,
2033 ok
= tevent_req_poll_ntstatus(req
, ev
, &status
);
2037 status
= cli_smb2_query_info_fnum_recv(req
, mem_ctx
, outbuf
);
2043 /***************************************************************
2044 Helper function for pathname operations.
2045 ***************************************************************/
2047 struct get_fnum_from_path_state
{
2048 struct tevent_context
*ev
;
2049 struct cli_state
*cli
;
2051 uint32_t desired_access
;
2055 static void get_fnum_from_path_opened_file(struct tevent_req
*subreq
);
2056 static void get_fnum_from_path_opened_reparse(struct tevent_req
*subreq
);
2057 static void get_fnum_from_path_opened_dir(struct tevent_req
*subreq
);
2059 static struct tevent_req
*get_fnum_from_path_send(
2060 TALLOC_CTX
*mem_ctx
,
2061 struct tevent_context
*ev
,
2062 struct cli_state
*cli
,
2064 uint32_t desired_access
)
2066 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
2067 struct get_fnum_from_path_state
*state
= NULL
;
2068 size_t namelen
= strlen(name
);
2070 req
= tevent_req_create(
2071 mem_ctx
, &state
, struct get_fnum_from_path_state
);
2078 state
->desired_access
= desired_access
;
2081 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2084 if (namelen
> 0 && name
[namelen
-1] == '\\') {
2085 state
->name
= talloc_strndup(state
, name
, namelen
-1);
2086 if (tevent_req_nomem(state
->name
, req
)) {
2087 return tevent_req_post(req
, ev
);
2091 subreq
= cli_smb2_create_fnum_send(
2092 state
, /* mem_ctx, */
2095 state
->name
, /* fname */
2096 (struct cli_smb2_create_flags
){0}, /* create_flags */
2097 SMB2_IMPERSONATION_IMPERSONATION
, /* impersonation_level */
2098 desired_access
, /* desired_access */
2099 0, /* file_attributes */
2102 FILE_SHARE_DELETE
, /* share_access */
2103 FILE_OPEN
, /* create_disposition */
2104 0, /* create_options */
2105 NULL
); /* in_cblobs */
2106 if (tevent_req_nomem(subreq
, req
)) {
2107 return tevent_req_post(req
, ev
);
2109 tevent_req_set_callback(subreq
, get_fnum_from_path_opened_file
, req
);
2113 static void get_fnum_from_path_opened_file(struct tevent_req
*subreq
)
2115 struct tevent_req
*req
= tevent_req_callback_data(
2116 subreq
, struct tevent_req
);
2117 struct get_fnum_from_path_state
*state
= tevent_req_data(
2118 req
, struct get_fnum_from_path_state
);
2121 status
= cli_smb2_create_fnum_recv(
2122 subreq
, &state
->fnum
, NULL
, NULL
, NULL
, NULL
);
2123 TALLOC_FREE(subreq
);
2125 if (NT_STATUS_EQUAL(status
, NT_STATUS_STOPPED_ON_SYMLINK
) ||
2126 NT_STATUS_EQUAL(status
, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED
)) {
2128 * Naive option to match our SMB1 code. Assume the
2129 * symlink path that tripped us up was the last
2130 * component and try again. Eventually we will have to
2131 * deal with the returned path unprocessed component. JRA.
2133 subreq
= cli_smb2_create_fnum_send(
2134 state
, /* mem_ctx, */
2136 state
->cli
, /* cli */
2137 state
->name
, /* fname */
2138 (struct cli_smb2_create_flags
){0}, /* create_flags */
2139 SMB2_IMPERSONATION_IMPERSONATION
, /* impersonation */
2140 state
->desired_access
, /* desired_access */
2141 0, /* file_attributes */
2144 FILE_SHARE_DELETE
, /* share_access */
2145 FILE_OPEN
, /* create_disposition */
2146 FILE_OPEN_REPARSE_POINT
, /* create_options */
2147 NULL
); /* in_cblobs */
2148 if (tevent_req_nomem(subreq
, req
)) {
2151 tevent_req_set_callback(
2152 subreq
, get_fnum_from_path_opened_reparse
, req
);
2156 if (NT_STATUS_EQUAL(status
, NT_STATUS_FILE_IS_A_DIRECTORY
)) {
2157 subreq
= cli_smb2_create_fnum_send(
2158 state
, /* mem_ctx, */
2160 state
->cli
, /* cli */
2161 state
->name
, /* fname */
2162 (struct cli_smb2_create_flags
){0}, /* create_flags */
2163 SMB2_IMPERSONATION_IMPERSONATION
, /* impersonation */
2164 state
->desired_access
, /* desired_access */
2165 0, /* file_attributes */
2168 FILE_SHARE_DELETE
, /* share_access */
2169 FILE_OPEN
, /* create_disposition */
2170 FILE_DIRECTORY_FILE
, /* create_options */
2171 NULL
); /* in_cblobs */
2172 if (tevent_req_nomem(subreq
, req
)) {
2175 tevent_req_set_callback(
2176 subreq
, get_fnum_from_path_opened_dir
, req
);
2180 if (tevent_req_nterror(req
, status
)) {
2183 tevent_req_done(req
);
2186 static void get_fnum_from_path_opened_reparse(struct tevent_req
*subreq
)
2188 struct tevent_req
*req
= tevent_req_callback_data(
2189 subreq
, struct tevent_req
);
2190 struct get_fnum_from_path_state
*state
= tevent_req_data(
2191 req
, struct get_fnum_from_path_state
);
2192 NTSTATUS status
= cli_smb2_create_fnum_recv(
2193 subreq
, &state
->fnum
, NULL
, NULL
, NULL
, NULL
);
2194 tevent_req_simple_finish_ntstatus(subreq
, status
);
2197 static void get_fnum_from_path_opened_dir(struct tevent_req
*subreq
)
2199 /* Abstraction violation, but these two are just the same... */
2200 get_fnum_from_path_opened_reparse(subreq
);
2203 static NTSTATUS
get_fnum_from_path_recv(
2204 struct tevent_req
*req
, uint16_t *pfnum
)
2206 struct get_fnum_from_path_state
*state
= tevent_req_data(
2207 req
, struct get_fnum_from_path_state
);
2208 NTSTATUS status
= NT_STATUS_OK
;
2210 if (!tevent_req_is_nterror(req
, &status
)) {
2211 *pfnum
= state
->fnum
;
2213 tevent_req_received(req
);
2217 static NTSTATUS
get_fnum_from_path(struct cli_state
*cli
,
2219 uint32_t desired_access
,
2222 TALLOC_CTX
*frame
= talloc_stackframe();
2223 struct tevent_context
*ev
= NULL
;
2224 struct tevent_req
*req
= NULL
;
2225 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
2227 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2228 status
= NT_STATUS_INVALID_PARAMETER
;
2231 ev
= samba_tevent_context_init(frame
);
2235 req
= get_fnum_from_path_send(frame
, ev
, cli
, name
, desired_access
);
2239 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
2242 status
= get_fnum_from_path_recv(req
, pfnum
);
2248 struct cli_smb2_qpathinfo_state
{
2249 struct tevent_context
*ev
;
2250 struct cli_state
*cli
;
2261 static void cli_smb2_qpathinfo_opened(struct tevent_req
*subreq
);
2262 static void cli_smb2_qpathinfo_done(struct tevent_req
*subreq
);
2263 static void cli_smb2_qpathinfo_closed(struct tevent_req
*subreq
);
2265 struct tevent_req
*cli_smb2_qpathinfo_send(TALLOC_CTX
*mem_ctx
,
2266 struct tevent_context
*ev
,
2267 struct cli_state
*cli
,
2273 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
2274 struct cli_smb2_qpathinfo_state
*state
= NULL
;
2276 req
= tevent_req_create(mem_ctx
,
2278 struct cli_smb2_qpathinfo_state
);
2284 state
->level
= level
;
2285 state
->min_rdata
= min_rdata
;
2286 state
->max_rdata
= max_rdata
;
2288 subreq
= get_fnum_from_path_send(state
,
2292 FILE_READ_ATTRIBUTES
);
2293 if (tevent_req_nomem(subreq
, req
)) {
2294 return tevent_req_post(req
, ev
);
2296 tevent_req_set_callback(subreq
, cli_smb2_qpathinfo_opened
, req
);
2300 static void cli_smb2_qpathinfo_opened(struct tevent_req
*subreq
)
2302 struct tevent_req
*req
=
2303 tevent_req_callback_data(subreq
, struct tevent_req
);
2304 struct cli_smb2_qpathinfo_state
*state
=
2305 tevent_req_data(req
, struct cli_smb2_qpathinfo_state
);
2308 status
= get_fnum_from_path_recv(subreq
, &state
->fnum
);
2309 TALLOC_FREE(subreq
);
2310 if (tevent_req_nterror(req
, status
)) {
2314 subreq
= cli_smb2_query_info_fnum_send(state
,
2321 NULL
, /* in_input_buffer */
2322 0, /* in_additional_info */
2324 if (tevent_req_nomem(subreq
, req
)) {
2327 tevent_req_set_callback(subreq
, cli_smb2_qpathinfo_done
, req
);
2330 static void cli_smb2_qpathinfo_done(struct tevent_req
*subreq
)
2332 struct tevent_req
*req
=
2333 tevent_req_callback_data(subreq
, struct tevent_req
);
2334 struct cli_smb2_qpathinfo_state
*state
=
2335 tevent_req_data(req
, struct cli_smb2_qpathinfo_state
);
2338 cli_smb2_query_info_fnum_recv(subreq
, state
, &state
->out
);
2339 TALLOC_FREE(subreq
);
2341 if (NT_STATUS_IS_OK(state
->status
) &&
2342 (state
->out
.length
< state
->min_rdata
)) {
2343 state
->status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2346 subreq
= cli_smb2_close_fnum_send(state
,
2351 if (tevent_req_nomem(subreq
, req
)) {
2354 tevent_req_set_callback(subreq
, cli_smb2_qpathinfo_closed
, req
);
2357 static void cli_smb2_qpathinfo_closed(struct tevent_req
*subreq
)
2359 struct tevent_req
*req
=
2360 tevent_req_callback_data(subreq
, struct tevent_req
);
2361 struct cli_smb2_qpathinfo_state
*state
=
2362 tevent_req_data(req
, struct cli_smb2_qpathinfo_state
);
2365 status
= cli_smb2_close_fnum_recv(subreq
);
2366 TALLOC_FREE(subreq
);
2367 if (tevent_req_nterror(req
, status
)) {
2370 if (tevent_req_nterror(req
, state
->status
)) {
2373 tevent_req_done(req
);
2376 NTSTATUS
cli_smb2_qpathinfo_recv(struct tevent_req
*req
,
2377 TALLOC_CTX
*mem_ctx
,
2379 uint32_t *num_rdata
)
2381 struct cli_smb2_qpathinfo_state
*state
=
2382 tevent_req_data(req
, struct cli_smb2_qpathinfo_state
);
2385 if (tevent_req_is_nterror(req
, &status
)) {
2389 *rdata
= talloc_move(mem_ctx
, &state
->out
.data
);
2390 *num_rdata
= state
->out
.length
;
2391 tevent_req_received(req
);
2392 return NT_STATUS_OK
;
2395 /***************************************************************
2396 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2399 ***************************************************************/
2401 NTSTATUS
cli_smb2_setpathinfo(struct cli_state
*cli
,
2403 uint8_t in_info_type
,
2404 uint8_t in_file_info_class
,
2405 const DATA_BLOB
*p_in_data
)
2408 uint16_t fnum
= 0xffff;
2409 TALLOC_CTX
*frame
= talloc_stackframe();
2411 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2413 * Can't use sync call while an async call is in flight
2415 status
= NT_STATUS_INVALID_PARAMETER
;
2419 status
= get_fnum_from_path(cli
,
2421 FILE_WRITE_ATTRIBUTES
,
2424 if (!NT_STATUS_IS_OK(status
)) {
2428 status
= cli_smb2_set_info_fnum(
2433 p_in_data
, /* in_input_buffer */
2434 0); /* in_additional_info */
2437 if (fnum
!= 0xffff) {
2438 cli_smb2_close_fnum(cli
, fnum
);
2446 /***************************************************************
2447 Wrapper that allows SMB2 to set pathname attributes.
2449 ***************************************************************/
2451 NTSTATUS
cli_smb2_setatr(struct cli_state
*cli
,
2456 uint8_t inbuf_store
[40];
2457 DATA_BLOB inbuf
= data_blob_null
;
2459 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2460 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2462 inbuf
.data
= inbuf_store
;
2463 inbuf
.length
= sizeof(inbuf_store
);
2464 data_blob_clear(&inbuf
);
2467 * SMB1 uses attr == 0 to clear all attributes
2468 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2469 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2470 * request attribute change.
2472 * SMB2 uses exactly the reverse. Unfortunately as the
2473 * cli_setatr() ABI is exposed inside libsmbclient,
2474 * we must make the SMB2 cli_smb2_setatr() call
2475 * export the same ABI as the SMB1 cli_setatr()
2476 * which calls it. This means reversing the sense
2477 * of the requested attr argument if it's zero
2478 * or FILE_ATTRIBUTE_NORMAL.
2480 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2484 attr
= FILE_ATTRIBUTE_NORMAL
;
2485 } else if (attr
== FILE_ATTRIBUTE_NORMAL
) {
2489 SIVAL(inbuf
.data
, 32, attr
);
2491 put_long_date((char *)inbuf
.data
+ 16,mtime
);
2493 /* Set all the other times to -1. */
2494 SBVAL(inbuf
.data
, 0, 0xFFFFFFFFFFFFFFFFLL
);
2495 SBVAL(inbuf
.data
, 8, 0xFFFFFFFFFFFFFFFFLL
);
2496 SBVAL(inbuf
.data
, 24, 0xFFFFFFFFFFFFFFFFLL
);
2498 return cli_smb2_setpathinfo(
2501 SMB2_0_INFO_FILE
, /* in_info_type */
2502 FSCC_FILE_BASIC_INFORMATION
, /* in_file_info_class */
2507 /***************************************************************
2508 Wrapper that allows SMB2 to set file handle times.
2510 ***************************************************************/
2512 NTSTATUS
cli_smb2_setattrE(struct cli_state
*cli
,
2518 uint8_t inbuf_store
[40];
2519 DATA_BLOB inbuf
= data_blob_null
;
2522 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2524 * Can't use sync call while an async call is in flight
2526 return NT_STATUS_INVALID_PARAMETER
;
2529 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2530 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2532 inbuf
.data
= inbuf_store
;
2533 inbuf
.length
= sizeof(inbuf_store
);
2534 data_blob_clear(&inbuf
);
2536 SBVAL(inbuf
.data
, 0, 0xFFFFFFFFFFFFFFFFLL
);
2537 if (change_time
!= 0) {
2538 put_long_date((char *)inbuf
.data
+ 24, change_time
);
2540 if (access_time
!= 0) {
2541 put_long_date((char *)inbuf
.data
+ 8, access_time
);
2543 if (write_time
!= 0) {
2544 put_long_date((char *)inbuf
.data
+ 16, write_time
);
2547 status
= cli_smb2_set_info_fnum(
2550 SMB2_0_INFO_FILE
, /* in_info_type */
2551 FSCC_FILE_BASIC_INFORMATION
, /* in_file_info_class */
2552 &inbuf
, /* in_input_buffer */
2553 0); /* in_additional_info */
2557 /***************************************************************
2558 Wrapper that allows SMB2 to query disk attributes (size).
2560 ***************************************************************/
2562 NTSTATUS
cli_smb2_dskattr(struct cli_state
*cli
, const char *path
,
2563 uint64_t *bsize
, uint64_t *total
, uint64_t *avail
)
2566 uint16_t fnum
= 0xffff;
2567 DATA_BLOB outbuf
= data_blob_null
;
2568 uint32_t sectors_per_unit
= 0;
2569 uint32_t bytes_per_sector
= 0;
2570 uint64_t total_size
= 0;
2571 uint64_t size_free
= 0;
2572 TALLOC_CTX
*frame
= talloc_stackframe();
2574 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2576 * Can't use sync call while an async call is in flight
2578 status
= NT_STATUS_INVALID_PARAMETER
;
2582 /* First open the top level directory. */
2583 status
= cli_smb2_create_fnum(cli
,
2585 (struct cli_smb2_create_flags
){0},
2586 SMB2_IMPERSONATION_IMPERSONATION
,
2587 FILE_READ_ATTRIBUTES
, /* desired_access */
2588 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
2589 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
, /* share_access */
2590 FILE_OPEN
, /* create_disposition */
2591 FILE_DIRECTORY_FILE
, /* create_options */
2598 if (!NT_STATUS_IS_OK(status
)) {
2602 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2603 level 3 (SMB_FS_SIZE_INFORMATION). */
2605 status
= cli_smb2_query_info_fnum(
2608 2, /* in_info_type */
2609 3, /* in_file_info_class */
2610 0xFFFF, /* in_max_output_length */
2611 NULL
, /* in_input_buffer */
2612 0, /* in_additional_info */
2616 if (!NT_STATUS_IS_OK(status
)) {
2620 /* Parse the reply. */
2621 if (outbuf
.length
!= 24) {
2622 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2626 total_size
= BVAL(outbuf
.data
, 0);
2627 size_free
= BVAL(outbuf
.data
, 8);
2628 sectors_per_unit
= IVAL(outbuf
.data
, 16);
2629 bytes_per_sector
= IVAL(outbuf
.data
, 20);
2632 *bsize
= (uint64_t)sectors_per_unit
* (uint64_t)bytes_per_sector
;
2635 *total
= total_size
;
2641 status
= NT_STATUS_OK
;
2645 if (fnum
!= 0xffff) {
2646 cli_smb2_close_fnum(cli
, fnum
);
2653 /***************************************************************
2654 Wrapper that allows SMB2 to query file system sizes.
2656 ***************************************************************/
2658 NTSTATUS
cli_smb2_get_fs_full_size_info(struct cli_state
*cli
,
2659 uint64_t *total_allocation_units
,
2660 uint64_t *caller_allocation_units
,
2661 uint64_t *actual_allocation_units
,
2662 uint64_t *sectors_per_allocation_unit
,
2663 uint64_t *bytes_per_sector
)
2666 uint16_t fnum
= 0xffff;
2667 DATA_BLOB outbuf
= data_blob_null
;
2668 TALLOC_CTX
*frame
= talloc_stackframe();
2670 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2672 * Can't use sync call while an async call is in flight
2674 status
= NT_STATUS_INVALID_PARAMETER
;
2678 /* First open the top level directory. */
2680 cli_smb2_create_fnum(cli
, "",
2681 (struct cli_smb2_create_flags
){0},
2682 SMB2_IMPERSONATION_IMPERSONATION
,
2683 FILE_READ_ATTRIBUTES
, /* desired_access */
2684 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
2685 FILE_SHARE_READ
| FILE_SHARE_WRITE
|
2686 FILE_SHARE_DELETE
, /* share_access */
2687 FILE_OPEN
, /* create_disposition */
2688 FILE_DIRECTORY_FILE
, /* create_options */
2695 if (!NT_STATUS_IS_OK(status
)) {
2699 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2700 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2702 status
= cli_smb2_query_info_fnum(
2705 SMB2_0_INFO_FILESYSTEM
, /* in_info_type */
2706 FSCC_FS_FULL_SIZE_INFORMATION
, /* in_file_info_class */
2707 0xFFFF, /* in_max_output_length */
2708 NULL
, /* in_input_buffer */
2709 0, /* in_additional_info */
2713 if (!NT_STATUS_IS_OK(status
)) {
2717 if (outbuf
.length
< 32) {
2718 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2722 *total_allocation_units
= BIG_UINT(outbuf
.data
, 0);
2723 *caller_allocation_units
= BIG_UINT(outbuf
.data
, 8);
2724 *actual_allocation_units
= BIG_UINT(outbuf
.data
, 16);
2725 *sectors_per_allocation_unit
= (uint64_t)IVAL(outbuf
.data
, 24);
2726 *bytes_per_sector
= (uint64_t)IVAL(outbuf
.data
, 28);
2730 if (fnum
!= 0xffff) {
2731 cli_smb2_close_fnum(cli
, fnum
);
2738 /***************************************************************
2739 Wrapper that allows SMB2 to query file system attributes.
2741 ***************************************************************/
2743 NTSTATUS
cli_smb2_get_fs_attr_info(struct cli_state
*cli
, uint32_t *fs_attr
)
2746 uint16_t fnum
= 0xffff;
2747 DATA_BLOB outbuf
= data_blob_null
;
2748 TALLOC_CTX
*frame
= talloc_stackframe();
2750 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2752 * Can't use sync call while an async call is in flight
2754 status
= NT_STATUS_INVALID_PARAMETER
;
2758 /* First open the top level directory. */
2760 cli_smb2_create_fnum(cli
, "",
2761 (struct cli_smb2_create_flags
){0},
2762 SMB2_IMPERSONATION_IMPERSONATION
,
2763 FILE_READ_ATTRIBUTES
, /* desired_access */
2764 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
2765 FILE_SHARE_READ
| FILE_SHARE_WRITE
|
2766 FILE_SHARE_DELETE
, /* share_access */
2767 FILE_OPEN
, /* create_disposition */
2768 FILE_DIRECTORY_FILE
, /* create_options */
2775 if (!NT_STATUS_IS_OK(status
)) {
2779 status
= cli_smb2_query_info_fnum(
2782 2, /* in_info_type */
2783 5, /* in_file_info_class */
2784 0xFFFF, /* in_max_output_length */
2785 NULL
, /* in_input_buffer */
2786 0, /* in_additional_info */
2790 if (!NT_STATUS_IS_OK(status
)) {
2794 if (outbuf
.length
< 12) {
2795 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2799 *fs_attr
= IVAL(outbuf
.data
, 0);
2803 if (fnum
!= 0xffff) {
2804 cli_smb2_close_fnum(cli
, fnum
);
2811 /***************************************************************
2812 Wrapper that allows SMB2 to query file system volume info.
2814 ***************************************************************/
2816 NTSTATUS
cli_smb2_get_fs_volume_info(struct cli_state
*cli
,
2817 TALLOC_CTX
*mem_ctx
,
2818 char **_volume_name
,
2819 uint32_t *pserial_number
,
2823 uint16_t fnum
= 0xffff;
2824 DATA_BLOB outbuf
= data_blob_null
;
2826 char *volume_name
= NULL
;
2827 TALLOC_CTX
*frame
= talloc_stackframe();
2829 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
2831 * Can't use sync call while an async call is in flight
2833 status
= NT_STATUS_INVALID_PARAMETER
;
2837 /* First open the top level directory. */
2839 cli_smb2_create_fnum(cli
, "",
2840 (struct cli_smb2_create_flags
){0},
2841 SMB2_IMPERSONATION_IMPERSONATION
,
2842 FILE_READ_ATTRIBUTES
, /* desired_access */
2843 FILE_ATTRIBUTE_DIRECTORY
, /* file attributes */
2844 FILE_SHARE_READ
| FILE_SHARE_WRITE
|
2845 FILE_SHARE_DELETE
, /* share_access */
2846 FILE_OPEN
, /* create_disposition */
2847 FILE_DIRECTORY_FILE
, /* create_options */
2854 if (!NT_STATUS_IS_OK(status
)) {
2858 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2859 level 1 (SMB_FS_VOLUME_INFORMATION). */
2861 status
= cli_smb2_query_info_fnum(
2864 SMB2_0_INFO_FILESYSTEM
, /* in_info_type */
2865 /* in_file_info_class */
2866 FSCC_FS_VOLUME_INFORMATION
,
2867 0xFFFF, /* in_max_output_length */
2868 NULL
, /* in_input_buffer */
2869 0, /* in_additional_info */
2873 if (!NT_STATUS_IS_OK(status
)) {
2877 if (outbuf
.length
< 24) {
2878 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2884 ts
= interpret_long_date(BVAL(outbuf
.data
, 0));
2887 if (pserial_number
) {
2888 *pserial_number
= IVAL(outbuf
.data
,8);
2890 nlen
= IVAL(outbuf
.data
,12);
2891 if (nlen
+ 18 < 18) {
2893 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2897 * The next check is safe as we know outbuf.length >= 24
2900 if (nlen
> (outbuf
.length
- 18)) {
2901 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
2905 pull_string_talloc(mem_ctx
,
2906 (const char *)outbuf
.data
,
2912 if (volume_name
== NULL
) {
2913 status
= map_nt_error_from_unix(errno
);
2917 *_volume_name
= volume_name
;
2921 if (fnum
!= 0xffff) {
2922 cli_smb2_close_fnum(cli
, fnum
);
2929 struct cli_smb2_mxac_state
{
2930 struct tevent_context
*ev
;
2931 struct cli_state
*cli
;
2933 struct smb2_create_blobs in_cblobs
;
2939 static void cli_smb2_mxac_opened(struct tevent_req
*subreq
);
2940 static void cli_smb2_mxac_closed(struct tevent_req
*subreq
);
2942 struct tevent_req
*cli_smb2_query_mxac_send(TALLOC_CTX
*mem_ctx
,
2943 struct tevent_context
*ev
,
2944 struct cli_state
*cli
,
2947 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
2948 struct cli_smb2_mxac_state
*state
= NULL
;
2951 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_mxac_state
);
2955 *state
= (struct cli_smb2_mxac_state
) {
2961 status
= smb2_create_blob_add(state
,
2963 SMB2_CREATE_TAG_MXAC
,
2964 data_blob(NULL
, 0));
2965 if (tevent_req_nterror(req
, status
)) {
2966 return tevent_req_post(req
, ev
);
2969 subreq
= cli_smb2_create_fnum_send(
2974 (struct cli_smb2_create_flags
){0},
2975 SMB2_IMPERSONATION_IMPERSONATION
,
2976 FILE_READ_ATTRIBUTES
,
2977 0, /* file attributes */
2978 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
2980 0, /* create_options */
2982 if (tevent_req_nomem(subreq
, req
)) {
2983 return tevent_req_post(req
, ev
);
2985 tevent_req_set_callback(subreq
, cli_smb2_mxac_opened
, req
);
2989 static void cli_smb2_mxac_opened(struct tevent_req
*subreq
)
2991 struct tevent_req
*req
= tevent_req_callback_data(
2992 subreq
, struct tevent_req
);
2993 struct cli_smb2_mxac_state
*state
= tevent_req_data(
2994 req
, struct cli_smb2_mxac_state
);
2995 struct smb2_create_blobs out_cblobs
= {0};
2996 struct smb2_create_blob
*mxac_blob
= NULL
;
2999 status
= cli_smb2_create_fnum_recv(
3000 subreq
, &state
->fnum
, NULL
, state
, &out_cblobs
, NULL
);
3001 TALLOC_FREE(subreq
);
3003 if (tevent_req_nterror(req
, status
)) {
3007 mxac_blob
= smb2_create_blob_find(&out_cblobs
, SMB2_CREATE_TAG_MXAC
);
3008 if (mxac_blob
== NULL
) {
3009 state
->status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
3012 if (mxac_blob
->data
.length
!= 8) {
3013 state
->status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
3017 state
->status
= NT_STATUS(IVAL(mxac_blob
->data
.data
, 0));
3018 state
->mxac
= IVAL(mxac_blob
->data
.data
, 4);
3021 subreq
= cli_smb2_close_fnum_send(state
,
3026 if (tevent_req_nomem(subreq
, req
)) {
3029 tevent_req_set_callback(subreq
, cli_smb2_mxac_closed
, req
);
3034 static void cli_smb2_mxac_closed(struct tevent_req
*subreq
)
3036 struct tevent_req
*req
= tevent_req_callback_data(
3037 subreq
, struct tevent_req
);
3040 status
= cli_smb2_close_fnum_recv(subreq
);
3041 if (tevent_req_nterror(req
, status
)) {
3045 tevent_req_done(req
);
3048 NTSTATUS
cli_smb2_query_mxac_recv(struct tevent_req
*req
, uint32_t *mxac
)
3050 struct cli_smb2_mxac_state
*state
= tevent_req_data(
3051 req
, struct cli_smb2_mxac_state
);
3054 if (tevent_req_is_nterror(req
, &status
)) {
3058 if (!NT_STATUS_IS_OK(state
->status
)) {
3059 return state
->status
;
3062 *mxac
= state
->mxac
;
3063 return NT_STATUS_OK
;
3066 NTSTATUS
cli_smb2_query_mxac(struct cli_state
*cli
,
3070 TALLOC_CTX
*frame
= talloc_stackframe();
3071 struct tevent_context
*ev
= NULL
;
3072 struct tevent_req
*req
= NULL
;
3073 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
3076 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3078 * Can't use sync call while an async call is in flight
3080 status
= NT_STATUS_INVALID_PARAMETER
;
3084 ev
= samba_tevent_context_init(frame
);
3088 req
= cli_smb2_query_mxac_send(frame
, ev
, cli
, fname
);
3092 ok
= tevent_req_poll_ntstatus(req
, ev
, &status
);
3096 status
= cli_smb2_query_mxac_recv(req
, _mxac
);
3103 struct cli_smb2_rename_fnum_state
{
3107 static void cli_smb2_rename_fnum_done(struct tevent_req
*subreq
);
3109 static struct tevent_req
*cli_smb2_rename_fnum_send(
3110 TALLOC_CTX
*mem_ctx
,
3111 struct tevent_context
*ev
,
3112 struct cli_state
*cli
,
3114 const char *fname_dst
,
3117 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
3118 struct cli_smb2_rename_fnum_state
*state
= NULL
;
3119 size_t namelen
= strlen(fname_dst
);
3120 smb_ucs2_t
*converted_str
= NULL
;
3121 size_t converted_size_bytes
= 0;
3125 req
= tevent_req_create(
3126 mem_ctx
, &state
, struct cli_smb2_rename_fnum_state
);
3132 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3135 if (*fname_dst
== '\\') {
3140 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3143 if (namelen
> 0 && fname_dst
[namelen
-1] == '\\') {
3144 fname_dst
= talloc_strndup(state
, fname_dst
, namelen
-1);
3145 if (tevent_req_nomem(fname_dst
, req
)) {
3146 return tevent_req_post(req
, ev
);
3150 ok
= push_ucs2_talloc(
3151 state
, &converted_str
, fname_dst
, &converted_size_bytes
);
3153 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
3154 return tevent_req_post(req
, ev
);
3158 * W2K8 insists the dest name is not null terminated. Remove
3159 * the last 2 zero bytes and reduce the name length.
3161 if (converted_size_bytes
< 2) {
3162 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
3163 return tevent_req_post(req
, ev
);
3165 converted_size_bytes
-= 2;
3167 inbuf_size
= 20 + converted_size_bytes
;
3168 if (inbuf_size
< 20) {
3169 /* Integer wrap check. */
3170 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
3171 return tevent_req_post(req
, ev
);
3175 * The Windows 10 SMB2 server has a minimum length
3176 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3177 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3178 * if the length is less. This isn't an alignment
3179 * issue as Windows client accepts happily 2-byte align
3180 * for larger target name sizes. Also the Windows 10
3181 * SMB1 server doesn't have this restriction.
3183 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3185 inbuf_size
= MAX(inbuf_size
, 24);
3187 state
->inbuf
= data_blob_talloc_zero(state
, inbuf_size
);
3188 if (tevent_req_nomem(state
->inbuf
.data
, req
)) {
3189 return tevent_req_post(req
, ev
);
3193 SCVAL(state
->inbuf
.data
, 0, 1);
3196 SIVAL(state
->inbuf
.data
, 16, converted_size_bytes
);
3197 memcpy(state
->inbuf
.data
+ 20, converted_str
, converted_size_bytes
);
3199 TALLOC_FREE(converted_str
);
3201 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3202 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3204 subreq
= cli_smb2_set_info_fnum_send(
3205 state
, /* mem_ctx */
3209 SMB2_0_INFO_FILE
, /* in_info_type */
3210 FSCC_FILE_RENAME_INFORMATION
, /* in_file_info_class */
3211 &state
->inbuf
, /* in_input_buffer */
3212 0); /* in_additional_info */
3213 if (tevent_req_nomem(subreq
, req
)) {
3214 return tevent_req_post(req
, ev
);
3216 tevent_req_set_callback(subreq
, cli_smb2_rename_fnum_done
, req
);
3220 static void cli_smb2_rename_fnum_done(struct tevent_req
*subreq
)
3222 NTSTATUS status
= cli_smb2_set_info_fnum_recv(subreq
);
3223 tevent_req_simple_finish_ntstatus(subreq
, status
);
3226 static NTSTATUS
cli_smb2_rename_fnum_recv(struct tevent_req
*req
)
3228 return tevent_req_simple_recv_ntstatus(req
);
3231 /***************************************************************
3232 Wrapper that allows SMB2 to rename a file.
3233 ***************************************************************/
3235 struct cli_smb2_rename_state
{
3236 struct tevent_context
*ev
;
3237 struct cli_state
*cli
;
3238 const char *fname_dst
;
3242 NTSTATUS rename_status
;
3245 static void cli_smb2_rename_opened(struct tevent_req
*subreq
);
3246 static void cli_smb2_rename_renamed(struct tevent_req
*subreq
);
3247 static void cli_smb2_rename_closed(struct tevent_req
*subreq
);
3249 struct tevent_req
*cli_smb2_rename_send(
3250 TALLOC_CTX
*mem_ctx
,
3251 struct tevent_context
*ev
,
3252 struct cli_state
*cli
,
3253 const char *fname_src
,
3254 const char *fname_dst
,
3257 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
3258 struct cli_smb2_rename_state
*state
= NULL
;
3261 req
= tevent_req_create(
3262 mem_ctx
, &state
, struct cli_smb2_rename_state
);
3268 * Strip a MSDFS path from fname_dst if we were given one.
3270 status
= cli_dfs_target_check(state
,
3274 if (tevent_req_nterror(req
, status
)) {
3275 return tevent_req_post(req
, ev
);
3280 state
->fname_dst
= fname_dst
;
3281 state
->replace
= replace
;
3283 subreq
= get_fnum_from_path_send(
3284 state
, ev
, cli
, fname_src
, DELETE_ACCESS
);
3285 if (tevent_req_nomem(subreq
, req
)) {
3286 return tevent_req_post(req
, ev
);
3288 tevent_req_set_callback(subreq
, cli_smb2_rename_opened
, req
);
3292 static void cli_smb2_rename_opened(struct tevent_req
*subreq
)
3294 struct tevent_req
*req
= tevent_req_callback_data(
3295 subreq
, struct tevent_req
);
3296 struct cli_smb2_rename_state
*state
= tevent_req_data(
3297 req
, struct cli_smb2_rename_state
);
3300 status
= get_fnum_from_path_recv(subreq
, &state
->fnum
);
3301 TALLOC_FREE(subreq
);
3302 if (tevent_req_nterror(req
, status
)) {
3306 subreq
= cli_smb2_rename_fnum_send(
3313 if (tevent_req_nomem(subreq
, req
)) {
3316 tevent_req_set_callback(subreq
, cli_smb2_rename_renamed
, req
);
3319 static void cli_smb2_rename_renamed(struct tevent_req
*subreq
)
3321 struct tevent_req
*req
= tevent_req_callback_data(
3322 subreq
, struct tevent_req
);
3323 struct cli_smb2_rename_state
*state
= tevent_req_data(
3324 req
, struct cli_smb2_rename_state
);
3326 state
->rename_status
= cli_smb2_rename_fnum_recv(subreq
);
3327 TALLOC_FREE(subreq
);
3329 subreq
= cli_smb2_close_fnum_send(state
,
3334 if (tevent_req_nomem(subreq
, req
)) {
3337 tevent_req_set_callback(subreq
, cli_smb2_rename_closed
, req
);
3340 static void cli_smb2_rename_closed(struct tevent_req
*subreq
)
3342 NTSTATUS status
= cli_smb2_close_fnum_recv(subreq
);
3343 tevent_req_simple_finish_ntstatus(subreq
, status
);
3346 NTSTATUS
cli_smb2_rename_recv(struct tevent_req
*req
)
3348 struct cli_smb2_rename_state
*state
= tevent_req_data(
3349 req
, struct cli_smb2_rename_state
);
3350 NTSTATUS status
= NT_STATUS_OK
;
3352 if (!tevent_req_is_nterror(req
, &status
)) {
3353 status
= state
->rename_status
;
3355 tevent_req_received(req
);
3359 /***************************************************************
3360 Wrapper that allows SMB2 to set an EA on a fnum.
3362 ***************************************************************/
3364 NTSTATUS
cli_smb2_set_ea_fnum(struct cli_state
*cli
,
3366 const char *ea_name
,
3371 DATA_BLOB inbuf
= data_blob_null
;
3373 char *ea_name_ascii
= NULL
;
3375 TALLOC_CTX
*frame
= talloc_stackframe();
3377 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3379 * Can't use sync call while an async call is in flight
3381 status
= NT_STATUS_INVALID_PARAMETER
;
3385 /* Marshall the SMB2 EA data. */
3386 if (ea_len
> 0xFFFF) {
3387 status
= NT_STATUS_INVALID_PARAMETER
;
3391 if (!push_ascii_talloc(frame
,
3395 status
= NT_STATUS_INVALID_PARAMETER
;
3399 if (namelen
< 2 || namelen
> 0xFF) {
3400 status
= NT_STATUS_INVALID_PARAMETER
;
3404 bloblen
= 8 + ea_len
+ namelen
;
3405 /* Round up to a 4 byte boundary. */
3406 bloblen
= ((bloblen
+ 3)&~3);
3408 inbuf
= data_blob_talloc_zero(frame
, bloblen
);
3409 if (inbuf
.data
== NULL
) {
3410 status
= NT_STATUS_NO_MEMORY
;
3413 /* namelen doesn't include the NULL byte. */
3414 SCVAL(inbuf
.data
, 5, namelen
- 1);
3415 SSVAL(inbuf
.data
, 6, ea_len
);
3416 memcpy(inbuf
.data
+ 8, ea_name_ascii
, namelen
);
3417 memcpy(inbuf
.data
+ 8 + namelen
, ea_val
, ea_len
);
3419 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3420 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3422 status
= cli_smb2_set_info_fnum(
3425 SMB2_0_INFO_FILE
, /* in_info_type */
3426 FSCC_FILE_FULL_EA_INFORMATION
, /* in_file_info_class */
3427 &inbuf
, /* in_input_buffer */
3428 0); /* in_additional_info */
3435 /***************************************************************
3436 Wrapper that allows SMB2 to set an EA on a pathname.
3438 ***************************************************************/
3440 NTSTATUS
cli_smb2_set_ea_path(struct cli_state
*cli
,
3442 const char *ea_name
,
3447 uint16_t fnum
= 0xffff;
3449 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3451 * Can't use sync call while an async call is in flight
3453 status
= NT_STATUS_INVALID_PARAMETER
;
3457 status
= get_fnum_from_path(cli
,
3462 if (!NT_STATUS_IS_OK(status
)) {
3466 status
= cli_set_ea_fnum(cli
,
3471 if (!NT_STATUS_IS_OK(status
)) {
3477 if (fnum
!= 0xffff) {
3478 cli_smb2_close_fnum(cli
, fnum
);
3483 /***************************************************************
3484 Wrapper that allows SMB2 to get an EA list on a pathname.
3486 ***************************************************************/
3488 NTSTATUS
cli_smb2_get_ea_list_path(struct cli_state
*cli
,
3492 struct ea_struct
**pea_array
)
3495 uint16_t fnum
= 0xffff;
3496 DATA_BLOB outbuf
= data_blob_null
;
3497 struct ea_list
*ea_list
= NULL
;
3498 struct ea_list
*eal
= NULL
;
3499 size_t ea_count
= 0;
3500 TALLOC_CTX
*frame
= talloc_stackframe();
3505 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3507 * Can't use sync call while an async call is in flight
3509 status
= NT_STATUS_INVALID_PARAMETER
;
3513 status
= get_fnum_from_path(cli
,
3518 if (!NT_STATUS_IS_OK(status
)) {
3522 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3523 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3525 status
= cli_smb2_query_info_fnum(
3528 SMB2_0_INFO_FILE
, /* in_info_type */
3529 FSCC_FILE_FULL_EA_INFORMATION
, /* in_file_info_class */
3530 0xFFFF, /* in_max_output_length */
3531 NULL
, /* in_input_buffer */
3532 0, /* in_additional_info */
3537 if (!NT_STATUS_IS_OK(status
)) {
3541 /* Parse the reply. */
3542 ea_list
= read_nttrans_ea_list(ctx
,
3543 (const char *)outbuf
.data
,
3545 if (ea_list
== NULL
) {
3546 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
3550 /* Convert to an array. */
3551 for (eal
= ea_list
; eal
; eal
= eal
->next
) {
3556 *pea_array
= talloc_array(ctx
, struct ea_struct
, ea_count
);
3557 if (*pea_array
== NULL
) {
3558 status
= NT_STATUS_NO_MEMORY
;
3562 for (eal
= ea_list
; eal
; eal
= eal
->next
) {
3563 (*pea_array
)[ea_count
++] = eal
->ea
;
3565 *pnum_eas
= ea_count
;
3570 if (fnum
!= 0xffff) {
3571 cli_smb2_close_fnum(cli
, fnum
);
3578 /***************************************************************
3579 Wrapper that allows SMB2 to get user quota.
3581 ***************************************************************/
3583 NTSTATUS
cli_smb2_get_user_quota(struct cli_state
*cli
,
3585 SMB_NTQUOTA_STRUCT
*pqt
)
3588 DATA_BLOB inbuf
= data_blob_null
;
3589 DATA_BLOB info_blob
= data_blob_null
;
3590 DATA_BLOB outbuf
= data_blob_null
;
3591 TALLOC_CTX
*frame
= talloc_stackframe();
3593 unsigned int offset
;
3594 struct smb2_query_quota_info query
= {0};
3595 struct file_get_quota_info info
= {0};
3596 enum ndr_err_code err
;
3597 struct ndr_push
*ndr_push
= NULL
;
3599 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3601 * Can't use sync call while an async call is in flight
3603 status
= NT_STATUS_INVALID_PARAMETER
;
3607 sid_len
= ndr_size_dom_sid(&pqt
->sid
, 0);
3609 query
.return_single
= 1;
3611 info
.next_entry_offset
= 0;
3612 info
.sid_length
= sid_len
;
3613 info
.sid
= pqt
->sid
;
3615 err
= ndr_push_struct_blob(
3619 (ndr_push_flags_fn_t
)ndr_push_file_get_quota_info
);
3621 if (!NDR_ERR_CODE_IS_SUCCESS(err
)) {
3622 status
= NT_STATUS_INTERNAL_ERROR
;
3626 query
.sid_list_length
= info_blob
.length
;
3627 ndr_push
= ndr_push_init_ctx(frame
);
3629 status
= NT_STATUS_NO_MEMORY
;
3633 err
= ndr_push_smb2_query_quota_info(ndr_push
,
3634 NDR_SCALARS
| NDR_BUFFERS
,
3637 if (!NDR_ERR_CODE_IS_SUCCESS(err
)) {
3638 status
= NT_STATUS_INTERNAL_ERROR
;
3642 err
= ndr_push_array_uint8(ndr_push
, NDR_SCALARS
, info_blob
.data
,
3645 if (!NDR_ERR_CODE_IS_SUCCESS(err
)) {
3646 status
= NT_STATUS_INTERNAL_ERROR
;
3649 inbuf
.data
= ndr_push
->data
;
3650 inbuf
.length
= ndr_push
->offset
;
3652 status
= cli_smb2_query_info_fnum(
3655 4, /* in_info_type */
3656 0, /* in_file_info_class */
3657 0xFFFF, /* in_max_output_length */
3658 &inbuf
, /* in_input_buffer */
3659 0, /* in_additional_info */
3664 if (!NT_STATUS_IS_OK(status
)) {
3668 if (!parse_user_quota_record(outbuf
.data
, outbuf
.length
, &offset
,
3670 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
3671 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3679 /***************************************************************
3680 Wrapper that allows SMB2 to list user quota.
3682 ***************************************************************/
3684 NTSTATUS
cli_smb2_list_user_quota_step(struct cli_state
*cli
,
3685 TALLOC_CTX
*mem_ctx
,
3687 SMB_NTQUOTA_LIST
**pqt_list
,
3691 DATA_BLOB inbuf
= data_blob_null
;
3692 DATA_BLOB outbuf
= data_blob_null
;
3693 TALLOC_CTX
*frame
= talloc_stackframe();
3694 struct smb2_query_quota_info info
= {0};
3695 enum ndr_err_code err
;
3697 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3699 * Can't use sync call while an async call is in flight
3701 status
= NT_STATUS_INVALID_PARAMETER
;
3705 info
.restart_scan
= first
? 1 : 0;
3707 err
= ndr_push_struct_blob(
3711 (ndr_push_flags_fn_t
)ndr_push_smb2_query_quota_info
);
3713 if (!NDR_ERR_CODE_IS_SUCCESS(err
)) {
3714 status
= NT_STATUS_INTERNAL_ERROR
;
3718 status
= cli_smb2_query_info_fnum(
3721 4, /* in_info_type */
3722 0, /* in_file_info_class */
3723 0xFFFF, /* in_max_output_length */
3724 &inbuf
, /* in_input_buffer */
3725 0, /* in_additional_info */
3731 * safeguard against panic from calling parse_user_quota_list with
3734 if (NT_STATUS_IS_OK(status
) && outbuf
.length
== 0) {
3735 status
= NT_STATUS_NO_MORE_ENTRIES
;
3738 if (!NT_STATUS_IS_OK(status
)) {
3742 status
= parse_user_quota_list(outbuf
.data
, outbuf
.length
, mem_ctx
,
3750 /***************************************************************
3751 Wrapper that allows SMB2 to get file system quota.
3753 ***************************************************************/
3755 NTSTATUS
cli_smb2_get_fs_quota_info(struct cli_state
*cli
,
3757 SMB_NTQUOTA_STRUCT
*pqt
)
3760 DATA_BLOB outbuf
= data_blob_null
;
3761 TALLOC_CTX
*frame
= talloc_stackframe();
3763 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3765 * Can't use sync call while an async call is in flight
3767 status
= NT_STATUS_INVALID_PARAMETER
;
3771 status
= cli_smb2_query_info_fnum(
3774 SMB2_0_INFO_FILESYSTEM
, /* in_info_type */
3775 FSCC_FS_QUOTA_INFORMATION
, /* in_file_info_class */
3776 0xFFFF, /* in_max_output_length */
3777 NULL
, /* in_input_buffer */
3778 0, /* in_additional_info */
3783 if (!NT_STATUS_IS_OK(status
)) {
3787 status
= parse_fs_quota_buffer(outbuf
.data
, outbuf
.length
, pqt
);
3794 /***************************************************************
3795 Wrapper that allows SMB2 to set user quota.
3797 ***************************************************************/
3799 NTSTATUS
cli_smb2_set_user_quota(struct cli_state
*cli
,
3801 SMB_NTQUOTA_LIST
*qtl
)
3804 DATA_BLOB inbuf
= data_blob_null
;
3805 TALLOC_CTX
*frame
= talloc_stackframe();
3807 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3809 * Can't use sync call while an async call is in flight
3811 status
= NT_STATUS_INVALID_PARAMETER
;
3815 status
= build_user_quota_buffer(qtl
, 0, talloc_tos(), &inbuf
, NULL
);
3816 if (!NT_STATUS_IS_OK(status
)) {
3820 status
= cli_smb2_set_info_fnum(
3823 4, /* in_info_type */
3824 0, /* in_file_info_class */
3825 &inbuf
, /* in_input_buffer */
3826 0); /* in_additional_info */
3833 NTSTATUS
cli_smb2_set_fs_quota_info(struct cli_state
*cli
,
3835 SMB_NTQUOTA_STRUCT
*pqt
)
3838 DATA_BLOB inbuf
= data_blob_null
;
3839 TALLOC_CTX
*frame
= talloc_stackframe();
3841 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
3843 * Can't use sync call while an async call is in flight
3845 status
= NT_STATUS_INVALID_PARAMETER
;
3849 status
= build_fs_quota_buffer(talloc_tos(), pqt
, &inbuf
, 0);
3850 if (!NT_STATUS_IS_OK(status
)) {
3854 status
= cli_smb2_set_info_fnum(
3857 SMB2_0_INFO_FILESYSTEM
, /* in_info_type */
3858 FSCC_FS_QUOTA_INFORMATION
, /* in_file_info_class */
3859 &inbuf
, /* in_input_buffer */
3860 0); /* in_additional_info */
3866 struct cli_smb2_read_state
{
3867 struct tevent_context
*ev
;
3868 struct cli_state
*cli
;
3869 struct smb2_hnd
*ph
;
3870 uint64_t start_offset
;
3876 static void cli_smb2_read_done(struct tevent_req
*subreq
);
3878 struct tevent_req
*cli_smb2_read_send(TALLOC_CTX
*mem_ctx
,
3879 struct tevent_context
*ev
,
3880 struct cli_state
*cli
,
3886 struct tevent_req
*req
, *subreq
;
3887 struct cli_smb2_read_state
*state
;
3889 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_read_state
);
3895 state
->start_offset
= (uint64_t)offset
;
3896 state
->size
= (uint32_t)size
;
3897 state
->received
= 0;
3900 status
= map_fnum_to_smb2_handle(cli
,
3903 if (tevent_req_nterror(req
, status
)) {
3904 return tevent_req_post(req
, ev
);
3907 subreq
= smb2cli_read_send(state
,
3910 state
->cli
->timeout
,
3911 state
->cli
->smb2
.session
,
3912 state
->cli
->smb2
.tcon
,
3914 state
->start_offset
,
3915 state
->ph
->fid_persistent
,
3916 state
->ph
->fid_volatile
,
3917 0, /* minimum_count */
3918 0); /* remaining_bytes */
3920 if (tevent_req_nomem(subreq
, req
)) {
3921 return tevent_req_post(req
, ev
);
3923 tevent_req_set_callback(subreq
, cli_smb2_read_done
, req
);
3927 static void cli_smb2_read_done(struct tevent_req
*subreq
)
3929 struct tevent_req
*req
= tevent_req_callback_data(
3930 subreq
, struct tevent_req
);
3931 struct cli_smb2_read_state
*state
= tevent_req_data(
3932 req
, struct cli_smb2_read_state
);
3935 status
= smb2cli_read_recv(subreq
, state
,
3936 &state
->buf
, &state
->received
);
3937 if (tevent_req_nterror(req
, status
)) {
3941 if (state
->received
> state
->size
) {
3942 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
3946 tevent_req_done(req
);
3949 NTSTATUS
cli_smb2_read_recv(struct tevent_req
*req
,
3954 struct cli_smb2_read_state
*state
= tevent_req_data(
3955 req
, struct cli_smb2_read_state
);
3957 if (tevent_req_is_nterror(req
, &status
)) {
3961 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3962 * better make sure that you copy it away before you talloc_free(req).
3963 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3965 *received
= (ssize_t
)state
->received
;
3966 *rcvbuf
= state
->buf
;
3967 return NT_STATUS_OK
;
3970 struct cli_smb2_write_state
{
3971 struct tevent_context
*ev
;
3972 struct cli_state
*cli
;
3973 struct smb2_hnd
*ph
;
3981 static void cli_smb2_write_written(struct tevent_req
*req
);
3983 struct tevent_req
*cli_smb2_write_send(TALLOC_CTX
*mem_ctx
,
3984 struct tevent_context
*ev
,
3985 struct cli_state
*cli
,
3993 struct tevent_req
*req
, *subreq
= NULL
;
3994 struct cli_smb2_write_state
*state
= NULL
;
3996 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_write_state
);
4002 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4003 state
->flags
= (uint32_t)mode
;
4005 state
->offset
= (uint64_t)offset
;
4006 state
->size
= (uint32_t)size
;
4009 status
= map_fnum_to_smb2_handle(cli
,
4012 if (tevent_req_nterror(req
, status
)) {
4013 return tevent_req_post(req
, ev
);
4016 subreq
= smb2cli_write_send(state
,
4019 state
->cli
->timeout
,
4020 state
->cli
->smb2
.session
,
4021 state
->cli
->smb2
.tcon
,
4024 state
->ph
->fid_persistent
,
4025 state
->ph
->fid_volatile
,
4026 0, /* remaining_bytes */
4027 state
->flags
, /* flags */
4030 if (tevent_req_nomem(subreq
, req
)) {
4031 return tevent_req_post(req
, ev
);
4033 tevent_req_set_callback(subreq
, cli_smb2_write_written
, req
);
4037 static void cli_smb2_write_written(struct tevent_req
*subreq
)
4039 struct tevent_req
*req
= tevent_req_callback_data(
4040 subreq
, struct tevent_req
);
4041 struct cli_smb2_write_state
*state
= tevent_req_data(
4042 req
, struct cli_smb2_write_state
);
4046 status
= smb2cli_write_recv(subreq
, &written
);
4047 TALLOC_FREE(subreq
);
4048 if (tevent_req_nterror(req
, status
)) {
4052 state
->written
= written
;
4054 tevent_req_done(req
);
4057 NTSTATUS
cli_smb2_write_recv(struct tevent_req
*req
,
4060 struct cli_smb2_write_state
*state
= tevent_req_data(
4061 req
, struct cli_smb2_write_state
);
4064 if (tevent_req_is_nterror(req
, &status
)) {
4065 tevent_req_received(req
);
4069 if (pwritten
!= NULL
) {
4070 *pwritten
= (size_t)state
->written
;
4072 tevent_req_received(req
);
4073 return NT_STATUS_OK
;
4076 /***************************************************************
4077 Wrapper that allows SMB2 async write using an fnum.
4078 This is mostly cut-and-paste from Volker's code inside
4079 source3/libsmb/clireadwrite.c, adapted for SMB2.
4081 Done this way so I can reuse all the logic inside cli_push()
4083 ***************************************************************/
4085 struct cli_smb2_writeall_state
{
4086 struct tevent_context
*ev
;
4087 struct cli_state
*cli
;
4088 struct smb2_hnd
*ph
;
4096 static void cli_smb2_writeall_written(struct tevent_req
*req
);
4098 struct tevent_req
*cli_smb2_writeall_send(TALLOC_CTX
*mem_ctx
,
4099 struct tevent_context
*ev
,
4100 struct cli_state
*cli
,
4108 struct tevent_req
*req
, *subreq
= NULL
;
4109 struct cli_smb2_writeall_state
*state
= NULL
;
4114 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_writeall_state
);
4120 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4121 state
->flags
= (uint32_t)mode
;
4123 state
->offset
= (uint64_t)offset
;
4124 state
->size
= (uint32_t)size
;
4127 status
= map_fnum_to_smb2_handle(cli
,
4130 if (tevent_req_nterror(req
, status
)) {
4131 return tevent_req_post(req
, ev
);
4134 to_write
= state
->size
;
4135 max_size
= smb2cli_conn_max_write_size(state
->cli
->conn
);
4136 to_write
= MIN(max_size
, to_write
);
4137 ok
= smb2cli_conn_req_possible(state
->cli
->conn
, &max_size
);
4139 to_write
= MIN(max_size
, to_write
);
4142 subreq
= smb2cli_write_send(state
,
4145 state
->cli
->timeout
,
4146 state
->cli
->smb2
.session
,
4147 state
->cli
->smb2
.tcon
,
4150 state
->ph
->fid_persistent
,
4151 state
->ph
->fid_volatile
,
4152 0, /* remaining_bytes */
4153 state
->flags
, /* flags */
4154 state
->buf
+ state
->written
);
4156 if (tevent_req_nomem(subreq
, req
)) {
4157 return tevent_req_post(req
, ev
);
4159 tevent_req_set_callback(subreq
, cli_smb2_writeall_written
, req
);
4163 static void cli_smb2_writeall_written(struct tevent_req
*subreq
)
4165 struct tevent_req
*req
= tevent_req_callback_data(
4166 subreq
, struct tevent_req
);
4167 struct cli_smb2_writeall_state
*state
= tevent_req_data(
4168 req
, struct cli_smb2_writeall_state
);
4170 uint32_t written
, to_write
;
4174 status
= smb2cli_write_recv(subreq
, &written
);
4175 TALLOC_FREE(subreq
);
4176 if (tevent_req_nterror(req
, status
)) {
4180 state
->written
+= written
;
4182 if (state
->written
> state
->size
) {
4183 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
4187 to_write
= state
->size
- state
->written
;
4189 if (to_write
== 0) {
4190 tevent_req_done(req
);
4194 max_size
= smb2cli_conn_max_write_size(state
->cli
->conn
);
4195 to_write
= MIN(max_size
, to_write
);
4196 ok
= smb2cli_conn_req_possible(state
->cli
->conn
, &max_size
);
4198 to_write
= MIN(max_size
, to_write
);
4201 subreq
= smb2cli_write_send(state
,
4204 state
->cli
->timeout
,
4205 state
->cli
->smb2
.session
,
4206 state
->cli
->smb2
.tcon
,
4208 state
->offset
+ state
->written
,
4209 state
->ph
->fid_persistent
,
4210 state
->ph
->fid_volatile
,
4211 0, /* remaining_bytes */
4212 state
->flags
, /* flags */
4213 state
->buf
+ state
->written
);
4215 if (tevent_req_nomem(subreq
, req
)) {
4218 tevent_req_set_callback(subreq
, cli_smb2_writeall_written
, req
);
4221 NTSTATUS
cli_smb2_writeall_recv(struct tevent_req
*req
,
4224 struct cli_smb2_writeall_state
*state
= tevent_req_data(
4225 req
, struct cli_smb2_writeall_state
);
4228 if (tevent_req_is_nterror(req
, &status
)) {
4231 if (pwritten
!= NULL
) {
4232 *pwritten
= (size_t)state
->written
;
4234 return NT_STATUS_OK
;
4237 struct cli_smb2_splice_state
{
4238 struct tevent_context
*ev
;
4239 struct cli_state
*cli
;
4240 struct smb2_hnd
*src_ph
;
4241 struct smb2_hnd
*dst_ph
;
4242 int (*splice_cb
)(off_t n
, void *priv
);
4249 struct req_resume_key_rsp resume_rsp
;
4250 struct srv_copychunk_copy cc_copy
;
4253 static void cli_splice_copychunk_send(struct cli_smb2_splice_state
*state
,
4254 struct tevent_req
*req
);
4256 static void cli_splice_copychunk_done(struct tevent_req
*subreq
)
4258 struct tevent_req
*req
= tevent_req_callback_data(
4259 subreq
, struct tevent_req
);
4260 struct cli_smb2_splice_state
*state
=
4261 tevent_req_data(req
,
4262 struct cli_smb2_splice_state
);
4263 struct smbXcli_conn
*conn
= state
->cli
->conn
;
4264 DATA_BLOB out_input_buffer
= data_blob_null
;
4265 DATA_BLOB out_output_buffer
= data_blob_null
;
4266 struct srv_copychunk_rsp cc_copy_rsp
;
4267 enum ndr_err_code ndr_ret
;
4270 status
= smb2cli_ioctl_recv(subreq
, state
,
4272 &out_output_buffer
);
4273 TALLOC_FREE(subreq
);
4274 if ((!NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
) ||
4275 state
->resized
) && tevent_req_nterror(req
, status
)) {
4279 ndr_ret
= ndr_pull_struct_blob(&out_output_buffer
, state
, &cc_copy_rsp
,
4280 (ndr_pull_flags_fn_t
)ndr_pull_srv_copychunk_rsp
);
4281 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
4282 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4283 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
4287 if (NT_STATUS_EQUAL(status
, NT_STATUS_INVALID_PARAMETER
)) {
4288 uint32_t max_chunks
= MIN(cc_copy_rsp
.chunks_written
,
4289 cc_copy_rsp
.total_bytes_written
/ cc_copy_rsp
.chunk_bytes_written
);
4290 if ((cc_copy_rsp
.chunk_bytes_written
> smb2cli_conn_cc_chunk_len(conn
) ||
4291 max_chunks
> smb2cli_conn_cc_max_chunks(conn
)) &&
4292 tevent_req_nterror(req
, status
)) {
4296 state
->resized
= true;
4297 smb2cli_conn_set_cc_chunk_len(conn
, cc_copy_rsp
.chunk_bytes_written
);
4298 smb2cli_conn_set_cc_max_chunks(conn
, max_chunks
);
4300 if ((state
->src_offset
> INT64_MAX
- cc_copy_rsp
.total_bytes_written
) ||
4301 (state
->dst_offset
> INT64_MAX
- cc_copy_rsp
.total_bytes_written
) ||
4302 (state
->written
> INT64_MAX
- cc_copy_rsp
.total_bytes_written
)) {
4303 tevent_req_nterror(req
, NT_STATUS_FILE_TOO_LARGE
);
4306 state
->src_offset
+= cc_copy_rsp
.total_bytes_written
;
4307 state
->dst_offset
+= cc_copy_rsp
.total_bytes_written
;
4308 state
->written
+= cc_copy_rsp
.total_bytes_written
;
4309 if (!state
->splice_cb(state
->written
, state
->priv
)) {
4310 tevent_req_nterror(req
, NT_STATUS_CANCELLED
);
4315 cli_splice_copychunk_send(state
, req
);
4318 static void cli_splice_copychunk_send(struct cli_smb2_splice_state
*state
,
4319 struct tevent_req
*req
)
4321 struct tevent_req
*subreq
;
4322 enum ndr_err_code ndr_ret
;
4323 struct smbXcli_conn
*conn
= state
->cli
->conn
;
4324 struct srv_copychunk_copy
*cc_copy
= &state
->cc_copy
;
4325 off_t src_offset
= state
->src_offset
;
4326 off_t dst_offset
= state
->dst_offset
;
4327 uint32_t req_len
= MIN(smb2cli_conn_cc_chunk_len(conn
) * smb2cli_conn_cc_max_chunks(conn
),
4328 state
->size
- state
->written
);
4329 DATA_BLOB in_input_buffer
= data_blob_null
;
4330 DATA_BLOB in_output_buffer
= data_blob_null
;
4332 if (state
->size
- state
->written
== 0) {
4333 tevent_req_done(req
);
4337 cc_copy
->chunk_count
= 0;
4339 cc_copy
->chunks
[cc_copy
->chunk_count
].source_off
= src_offset
;
4340 cc_copy
->chunks
[cc_copy
->chunk_count
].target_off
= dst_offset
;
4341 cc_copy
->chunks
[cc_copy
->chunk_count
].length
= MIN(req_len
,
4342 smb2cli_conn_cc_chunk_len(conn
));
4343 if (req_len
< cc_copy
->chunks
[cc_copy
->chunk_count
].length
) {
4344 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
4347 req_len
-= cc_copy
->chunks
[cc_copy
->chunk_count
].length
;
4348 if ((src_offset
> INT64_MAX
- cc_copy
->chunks
[cc_copy
->chunk_count
].length
) ||
4349 (dst_offset
> INT64_MAX
- cc_copy
->chunks
[cc_copy
->chunk_count
].length
)) {
4350 tevent_req_nterror(req
, NT_STATUS_FILE_TOO_LARGE
);
4353 src_offset
+= cc_copy
->chunks
[cc_copy
->chunk_count
].length
;
4354 dst_offset
+= cc_copy
->chunks
[cc_copy
->chunk_count
].length
;
4355 cc_copy
->chunk_count
++;
4358 ndr_ret
= ndr_push_struct_blob(&in_input_buffer
, state
, cc_copy
,
4359 (ndr_push_flags_fn_t
)ndr_push_srv_copychunk_copy
);
4360 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
4361 DEBUG(0, ("failed to marshall copy chunk req\n"));
4362 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
4366 subreq
= smb2cli_ioctl_send(state
, state
->ev
, state
->cli
->conn
,
4367 state
->cli
->timeout
,
4368 state
->cli
->smb2
.session
,
4369 state
->cli
->smb2
.tcon
,
4370 state
->dst_ph
->fid_persistent
, /* in_fid_persistent */
4371 state
->dst_ph
->fid_volatile
, /* in_fid_volatile */
4372 FSCTL_SRV_COPYCHUNK_WRITE
,
4373 0, /* in_max_input_length */
4375 12, /* in_max_output_length */
4377 SMB2_IOCTL_FLAG_IS_FSCTL
);
4378 if (tevent_req_nomem(subreq
, req
)) {
4381 tevent_req_set_callback(subreq
,
4382 cli_splice_copychunk_done
,
4386 static void cli_splice_key_done(struct tevent_req
*subreq
)
4388 struct tevent_req
*req
= tevent_req_callback_data(
4389 subreq
, struct tevent_req
);
4390 struct cli_smb2_splice_state
*state
=
4391 tevent_req_data(req
,
4392 struct cli_smb2_splice_state
);
4393 enum ndr_err_code ndr_ret
;
4396 DATA_BLOB out_input_buffer
= data_blob_null
;
4397 DATA_BLOB out_output_buffer
= data_blob_null
;
4399 status
= smb2cli_ioctl_recv(subreq
, state
,
4401 &out_output_buffer
);
4402 TALLOC_FREE(subreq
);
4403 if (tevent_req_nterror(req
, status
)) {
4407 ndr_ret
= ndr_pull_struct_blob(&out_output_buffer
,
4408 state
, &state
->resume_rsp
,
4409 (ndr_pull_flags_fn_t
)ndr_pull_req_resume_key_rsp
);
4410 if (ndr_ret
!= NDR_ERR_SUCCESS
) {
4411 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4412 tevent_req_nterror(req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
4416 memcpy(&state
->cc_copy
.source_key
,
4417 &state
->resume_rsp
.resume_key
,
4418 sizeof state
->resume_rsp
.resume_key
);
4420 cli_splice_copychunk_send(state
, req
);
4423 struct tevent_req
*cli_smb2_splice_send(TALLOC_CTX
*mem_ctx
,
4424 struct tevent_context
*ev
,
4425 struct cli_state
*cli
,
4426 uint16_t src_fnum
, uint16_t dst_fnum
,
4427 off_t size
, off_t src_offset
, off_t dst_offset
,
4428 int (*splice_cb
)(off_t n
, void *priv
),
4431 struct tevent_req
*req
;
4432 struct tevent_req
*subreq
;
4433 struct cli_smb2_splice_state
*state
;
4435 DATA_BLOB in_input_buffer
= data_blob_null
;
4436 DATA_BLOB in_output_buffer
= data_blob_null
;
4438 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_splice_state
);
4444 state
->splice_cb
= splice_cb
;
4448 state
->src_offset
= src_offset
;
4449 state
->dst_offset
= dst_offset
;
4450 state
->cc_copy
.chunks
= talloc_array(state
,
4451 struct srv_copychunk
,
4452 smb2cli_conn_cc_max_chunks(cli
->conn
));
4453 if (state
->cc_copy
.chunks
== NULL
) {
4457 status
= map_fnum_to_smb2_handle(cli
, src_fnum
, &state
->src_ph
);
4458 if (tevent_req_nterror(req
, status
))
4459 return tevent_req_post(req
, ev
);
4461 status
= map_fnum_to_smb2_handle(cli
, dst_fnum
, &state
->dst_ph
);
4462 if (tevent_req_nterror(req
, status
))
4463 return tevent_req_post(req
, ev
);
4465 subreq
= smb2cli_ioctl_send(state
, ev
, cli
->conn
,
4469 state
->src_ph
->fid_persistent
, /* in_fid_persistent */
4470 state
->src_ph
->fid_volatile
, /* in_fid_volatile */
4471 FSCTL_SRV_REQUEST_RESUME_KEY
,
4472 0, /* in_max_input_length */
4474 32, /* in_max_output_length */
4476 SMB2_IOCTL_FLAG_IS_FSCTL
);
4477 if (tevent_req_nomem(subreq
, req
)) {
4480 tevent_req_set_callback(subreq
,
4481 cli_splice_key_done
,
4487 NTSTATUS
cli_smb2_splice_recv(struct tevent_req
*req
, off_t
*written
)
4489 struct cli_smb2_splice_state
*state
= tevent_req_data(
4490 req
, struct cli_smb2_splice_state
);
4493 if (tevent_req_is_nterror(req
, &status
)) {
4494 tevent_req_received(req
);
4497 if (written
!= NULL
) {
4498 *written
= state
->written
;
4500 tevent_req_received(req
);
4501 return NT_STATUS_OK
;
4504 /***************************************************************
4505 SMB2 enum shadow copy data.
4506 ***************************************************************/
4508 struct cli_smb2_shadow_copy_data_fnum_state
{
4509 struct cli_state
*cli
;
4511 struct smb2_hnd
*ph
;
4512 DATA_BLOB out_input_buffer
;
4513 DATA_BLOB out_output_buffer
;
4516 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req
*subreq
);
4518 static struct tevent_req
*cli_smb2_shadow_copy_data_fnum_send(
4519 TALLOC_CTX
*mem_ctx
,
4520 struct tevent_context
*ev
,
4521 struct cli_state
*cli
,
4525 struct tevent_req
*req
, *subreq
;
4526 struct cli_smb2_shadow_copy_data_fnum_state
*state
;
4529 req
= tevent_req_create(mem_ctx
, &state
,
4530 struct cli_smb2_shadow_copy_data_fnum_state
);
4538 status
= map_fnum_to_smb2_handle(cli
, fnum
, &state
->ph
);
4539 if (tevent_req_nterror(req
, status
)) {
4540 return tevent_req_post(req
, ev
);
4544 * TODO. Under SMB2 we should send a zero max_output_length
4545 * ioctl to get the required size, then send another ioctl
4546 * to get the data, but the current SMB1 implementation just
4547 * does one roundtrip with a 64K buffer size. Do the same
4551 subreq
= smb2cli_ioctl_send(state
, ev
, state
->cli
->conn
,
4552 state
->cli
->timeout
,
4553 state
->cli
->smb2
.session
,
4554 state
->cli
->smb2
.tcon
,
4555 state
->ph
->fid_persistent
, /* in_fid_persistent */
4556 state
->ph
->fid_volatile
, /* in_fid_volatile */
4557 FSCTL_GET_SHADOW_COPY_DATA
,
4558 0, /* in_max_input_length */
4559 NULL
, /* in_input_buffer */
4561 CLI_BUFFER_SIZE
: 16, /* in_max_output_length */
4562 NULL
, /* in_output_buffer */
4563 SMB2_IOCTL_FLAG_IS_FSCTL
);
4565 if (tevent_req_nomem(subreq
, req
)) {
4566 return tevent_req_post(req
, ev
);
4568 tevent_req_set_callback(subreq
,
4569 cli_smb2_shadow_copy_data_fnum_done
,
4575 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req
*subreq
)
4577 struct tevent_req
*req
= tevent_req_callback_data(
4578 subreq
, struct tevent_req
);
4579 struct cli_smb2_shadow_copy_data_fnum_state
*state
= tevent_req_data(
4580 req
, struct cli_smb2_shadow_copy_data_fnum_state
);
4583 status
= smb2cli_ioctl_recv(subreq
, state
,
4584 &state
->out_input_buffer
,
4585 &state
->out_output_buffer
);
4586 tevent_req_simple_finish_ntstatus(subreq
, status
);
4589 static NTSTATUS
cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req
*req
,
4590 TALLOC_CTX
*mem_ctx
,
4595 struct cli_smb2_shadow_copy_data_fnum_state
*state
= tevent_req_data(
4596 req
, struct cli_smb2_shadow_copy_data_fnum_state
);
4597 char **names
= NULL
;
4598 uint32_t num_names
= 0;
4599 uint32_t num_names_returned
= 0;
4600 uint32_t dlength
= 0;
4602 uint8_t *endp
= NULL
;
4605 if (tevent_req_is_nterror(req
, &status
)) {
4609 if (state
->out_output_buffer
.length
< 16) {
4610 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4613 num_names
= IVAL(state
->out_output_buffer
.data
, 0);
4614 num_names_returned
= IVAL(state
->out_output_buffer
.data
, 4);
4615 dlength
= IVAL(state
->out_output_buffer
.data
, 8);
4617 if (num_names
> 0x7FFFFFFF) {
4618 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4621 if (get_names
== false) {
4622 *pnum_names
= (int)num_names
;
4623 return NT_STATUS_OK
;
4625 if (num_names
!= num_names_returned
) {
4626 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4628 if (dlength
+ 12 < 12) {
4629 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4632 * NB. The below is an allowable return if there are
4633 * more snapshots than the buffer size we told the
4634 * server we can receive. We currently don't support
4637 if (dlength
+ 12 > state
->out_output_buffer
.length
) {
4638 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4640 if (state
->out_output_buffer
.length
+
4641 (2 * sizeof(SHADOW_COPY_LABEL
)) <
4642 state
->out_output_buffer
.length
) {
4643 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4646 names
= talloc_array(mem_ctx
, char *, num_names_returned
);
4647 if (names
== NULL
) {
4648 return NT_STATUS_NO_MEMORY
;
4651 endp
= state
->out_output_buffer
.data
+
4652 state
->out_output_buffer
.length
;
4654 for (i
=0; i
<num_names_returned
; i
++) {
4657 size_t converted_size
;
4659 src
= state
->out_output_buffer
.data
+ 12 +
4660 (i
* 2 * sizeof(SHADOW_COPY_LABEL
));
4662 if (src
+ (2 * sizeof(SHADOW_COPY_LABEL
)) > endp
) {
4663 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4665 ret
= convert_string_talloc(
4666 names
, CH_UTF16LE
, CH_UNIX
,
4667 src
, 2 * sizeof(SHADOW_COPY_LABEL
),
4668 &names
[i
], &converted_size
);
4671 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
4674 *pnum_names
= num_names
;
4676 return NT_STATUS_OK
;
4679 NTSTATUS
cli_smb2_shadow_copy_data(TALLOC_CTX
*mem_ctx
,
4680 struct cli_state
*cli
,
4686 TALLOC_CTX
*frame
= talloc_stackframe();
4687 struct tevent_context
*ev
;
4688 struct tevent_req
*req
;
4689 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
4691 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
4693 * Can't use sync call while an async call is in flight
4695 status
= NT_STATUS_INVALID_PARAMETER
;
4698 ev
= samba_tevent_context_init(frame
);
4702 req
= cli_smb2_shadow_copy_data_fnum_send(frame
,
4710 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
4713 status
= cli_smb2_shadow_copy_data_fnum_recv(req
,
4723 /***************************************************************
4724 Wrapper that allows SMB2 to truncate a file.
4726 ***************************************************************/
4728 NTSTATUS
cli_smb2_ftruncate(struct cli_state
*cli
,
4733 uint8_t buf
[8] = {0};
4734 DATA_BLOB inbuf
= { .data
= buf
, .length
= sizeof(buf
) };
4735 TALLOC_CTX
*frame
= talloc_stackframe();
4737 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
4739 * Can't use sync call while an async call is in flight
4741 status
= NT_STATUS_INVALID_PARAMETER
;
4745 SBVAL(buf
, 0, newsize
);
4747 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4748 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4750 status
= cli_smb2_set_info_fnum(
4753 SMB2_0_INFO_FILE
, /* in_info_type */
4754 FSCC_FILE_END_OF_FILE_INFORMATION
, /* in_file_info_class */
4755 &inbuf
, /* in_input_buffer */
4763 struct cli_smb2_notify_state
{
4764 struct tevent_req
*subreq
;
4765 struct notify_change
*changes
;
4769 static void cli_smb2_notify_done(struct tevent_req
*subreq
);
4770 static bool cli_smb2_notify_cancel(struct tevent_req
*req
);
4772 struct tevent_req
*cli_smb2_notify_send(
4773 TALLOC_CTX
*mem_ctx
,
4774 struct tevent_context
*ev
,
4775 struct cli_state
*cli
,
4777 uint32_t buffer_size
,
4778 uint32_t completion_filter
,
4781 struct tevent_req
*req
= NULL
;
4782 struct cli_smb2_notify_state
*state
= NULL
;
4783 struct smb2_hnd
*ph
= NULL
;
4786 req
= tevent_req_create(mem_ctx
, &state
,
4787 struct cli_smb2_notify_state
);
4792 status
= map_fnum_to_smb2_handle(cli
, fnum
, &ph
);
4793 if (tevent_req_nterror(req
, status
)) {
4794 return tevent_req_post(req
, ev
);
4797 state
->subreq
= smb2cli_notify_send(
4809 if (tevent_req_nomem(state
->subreq
, req
)) {
4810 return tevent_req_post(req
, ev
);
4812 tevent_req_set_callback(state
->subreq
, cli_smb2_notify_done
, req
);
4813 tevent_req_set_cancel_fn(req
, cli_smb2_notify_cancel
);
4817 static bool cli_smb2_notify_cancel(struct tevent_req
*req
)
4819 struct cli_smb2_notify_state
*state
= tevent_req_data(
4820 req
, struct cli_smb2_notify_state
);
4823 ok
= tevent_req_cancel(state
->subreq
);
4827 static void cli_smb2_notify_done(struct tevent_req
*subreq
)
4829 struct tevent_req
*req
= tevent_req_callback_data(
4830 subreq
, struct tevent_req
);
4831 struct cli_smb2_notify_state
*state
= tevent_req_data(
4832 req
, struct cli_smb2_notify_state
);
4838 status
= smb2cli_notify_recv(subreq
, state
, &base
, &len
);
4839 TALLOC_FREE(subreq
);
4841 if (NT_STATUS_EQUAL(status
, NT_STATUS_IO_TIMEOUT
)) {
4842 tevent_req_done(req
);
4845 if (tevent_req_nterror(req
, status
)) {
4851 while (len
- ofs
>= 12) {
4852 struct notify_change
*tmp
;
4853 struct notify_change
*c
;
4854 uint32_t next_ofs
= IVAL(base
, ofs
);
4855 uint32_t file_name_length
= IVAL(base
, ofs
+8);
4859 tmp
= talloc_realloc(
4862 struct notify_change
,
4863 state
->num_changes
+ 1);
4864 if (tevent_req_nomem(tmp
, req
)) {
4867 state
->changes
= tmp
;
4868 c
= &state
->changes
[state
->num_changes
];
4869 state
->num_changes
+= 1;
4871 if (smb_buffer_oob(len
, ofs
, next_ofs
) ||
4872 smb_buffer_oob(len
, ofs
+12, file_name_length
)) {
4874 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
4878 c
->action
= IVAL(base
, ofs
+4);
4880 ok
= convert_string_talloc(
4890 req
, NT_STATUS_INVALID_NETWORK_RESPONSE
);
4894 if (next_ofs
== 0) {
4900 tevent_req_done(req
);
4903 NTSTATUS
cli_smb2_notify_recv(struct tevent_req
*req
,
4904 TALLOC_CTX
*mem_ctx
,
4905 struct notify_change
**pchanges
,
4906 uint32_t *pnum_changes
)
4908 struct cli_smb2_notify_state
*state
= tevent_req_data(
4909 req
, struct cli_smb2_notify_state
);
4912 if (tevent_req_is_nterror(req
, &status
)) {
4915 *pchanges
= talloc_move(mem_ctx
, &state
->changes
);
4916 *pnum_changes
= state
->num_changes
;
4917 return NT_STATUS_OK
;
4920 NTSTATUS
cli_smb2_notify(struct cli_state
*cli
, uint16_t fnum
,
4921 uint32_t buffer_size
, uint32_t completion_filter
,
4922 bool recursive
, TALLOC_CTX
*mem_ctx
,
4923 struct notify_change
**pchanges
,
4924 uint32_t *pnum_changes
)
4926 TALLOC_CTX
*frame
= talloc_stackframe();
4927 struct tevent_context
*ev
;
4928 struct tevent_req
*req
;
4929 NTSTATUS status
= NT_STATUS_NO_MEMORY
;
4931 if (smbXcli_conn_has_async_calls(cli
->conn
)) {
4933 * Can't use sync call while an async call is in flight
4935 status
= NT_STATUS_INVALID_PARAMETER
;
4938 ev
= samba_tevent_context_init(frame
);
4942 req
= cli_smb2_notify_send(
4953 if (!tevent_req_poll_ntstatus(req
, ev
, &status
)) {
4956 status
= cli_smb2_notify_recv(req
, mem_ctx
, pchanges
, pnum_changes
);
4962 struct cli_smb2_fsctl_state
{
4966 static void cli_smb2_fsctl_done(struct tevent_req
*subreq
);
4968 struct tevent_req
*cli_smb2_fsctl_send(
4969 TALLOC_CTX
*mem_ctx
,
4970 struct tevent_context
*ev
,
4971 struct cli_state
*cli
,
4974 const DATA_BLOB
*in
,
4977 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
4978 struct cli_smb2_fsctl_state
*state
= NULL
;
4979 struct smb2_hnd
*ph
= NULL
;
4982 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_fsctl_state
);
4987 status
= map_fnum_to_smb2_handle(cli
, fnum
, &ph
);
4988 if (tevent_req_nterror(req
, status
)) {
4989 return tevent_req_post(req
, ev
);
4992 subreq
= smb2cli_ioctl_send(
5002 0, /* in_max_input_length */
5006 SMB2_IOCTL_FLAG_IS_FSCTL
);
5008 if (tevent_req_nomem(subreq
, req
)) {
5009 return tevent_req_post(req
, ev
);
5011 tevent_req_set_callback(subreq
, cli_smb2_fsctl_done
, req
);
5015 static void cli_smb2_fsctl_done(struct tevent_req
*subreq
)
5017 struct tevent_req
*req
= tevent_req_callback_data(
5018 subreq
, struct tevent_req
);
5019 struct cli_smb2_fsctl_state
*state
= tevent_req_data(
5020 req
, struct cli_smb2_fsctl_state
);
5023 status
= smb2cli_ioctl_recv(subreq
, state
, NULL
, &state
->out
);
5024 tevent_req_simple_finish_ntstatus(subreq
, status
);
5027 NTSTATUS
cli_smb2_fsctl_recv(
5028 struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
, DATA_BLOB
*out
)
5030 struct cli_smb2_fsctl_state
*state
= tevent_req_data(
5031 req
, struct cli_smb2_fsctl_state
);
5032 NTSTATUS status
= NT_STATUS_OK
;
5034 if (tevent_req_is_nterror(req
, &status
)) {
5035 tevent_req_received(req
);
5039 if (state
->out
.length
== 0) {
5040 *out
= (DATA_BLOB
) { .data
= NULL
, };
5043 * Can't use talloc_move() here, the outblobs from
5044 * smb2cli_ioctl_recv() are not standalone talloc
5045 * objects but just peek into the larger buffers
5046 * received, hanging off "state".
5048 *out
= data_blob_talloc(
5049 mem_ctx
, state
->out
.data
, state
->out
.length
);
5050 if (out
->data
== NULL
) {
5051 status
= NT_STATUS_NO_MEMORY
;
5055 tevent_req_received(req
);
5056 return NT_STATUS_OK
;
5059 struct cli_smb2_get_posix_fs_info_state
{
5060 struct tevent_context
*ev
;
5061 struct cli_state
*cli
;
5063 uint32_t optimal_transfer_size
;
5064 uint32_t block_size
;
5065 uint64_t total_blocks
;
5066 uint64_t blocks_available
;
5067 uint64_t user_blocks_available
;
5068 uint64_t total_file_nodes
;
5069 uint64_t free_file_nodes
;
5070 uint64_t fs_identifier
;
5073 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req
*subreq
);
5074 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req
*subreq
);
5075 static void cli_smb2_get_posix_fs_info_done(struct tevent_req
*subreq
);
5077 struct tevent_req
*cli_smb2_get_posix_fs_info_send(TALLOC_CTX
*mem_ctx
,
5078 struct tevent_context
*ev
,
5079 struct cli_state
*cli
)
5081 struct smb2_create_blobs
*cblob
= NULL
;
5082 struct tevent_req
*req
= NULL
, *subreq
= NULL
;
5083 struct cli_smb2_get_posix_fs_info_state
*state
= NULL
;
5086 req
= tevent_req_create(mem_ctx
, &state
, struct cli_smb2_get_posix_fs_info_state
);
5090 *state
= (struct cli_smb2_get_posix_fs_info_state
) {
5094 status
= make_smb2_posix_create_ctx(state
, &cblob
, 0);
5095 if (!NT_STATUS_IS_OK(status
)) {
5099 /* First open the top level directory. */
5100 subreq
= cli_smb2_create_fnum_send(state
,
5104 (struct cli_smb2_create_flags
){0},
5105 SMB2_IMPERSONATION_IMPERSONATION
,
5106 FILE_READ_ATTRIBUTES
,
5107 FILE_ATTRIBUTE_DIRECTORY
,
5108 FILE_SHARE_READ
| FILE_SHARE_WRITE
|
5111 FILE_DIRECTORY_FILE
,
5113 if (tevent_req_nomem(subreq
, req
)) {
5114 return tevent_req_post(req
, ev
);
5117 tevent_req_set_callback(subreq
, cli_smb2_get_posix_fs_info_opened
, req
);
5121 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req
*subreq
)
5123 struct tevent_req
*req
= tevent_req_callback_data(
5124 subreq
, struct tevent_req
);
5125 struct cli_smb2_get_posix_fs_info_state
*state
= tevent_req_data(
5126 req
, struct cli_smb2_get_posix_fs_info_state
);
5127 struct smb2_create_blobs
*cblob
= {0};
5130 status
= cli_smb2_create_fnum_recv(subreq
,
5136 TALLOC_FREE(subreq
);
5138 if (tevent_req_nterror(req
, status
)) {
5142 subreq
= cli_smb2_query_info_fnum_send(
5147 SMB2_0_INFO_FILESYSTEM
, /* in_info_type */
5148 FSCC_FS_POSIX_INFORMATION
, /* in_file_info_class */
5149 0xFFFF, /* in_max_output_length */
5150 NULL
, /* in_input_buffer */
5151 0, /* in_additional_info */
5153 if (tevent_req_nomem(subreq
, req
)) {
5157 tevent_req_set_callback(subreq
, cli_smb2_get_posix_fs_info_queried
, req
);
5160 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req
*subreq
)
5162 struct tevent_req
*req
= tevent_req_callback_data(
5163 subreq
, struct tevent_req
);
5164 struct cli_smb2_get_posix_fs_info_state
*state
= tevent_req_data(
5165 req
, struct cli_smb2_get_posix_fs_info_state
);
5166 DATA_BLOB outbuf
= data_blob_null
;
5169 status
= cli_smb2_query_info_fnum_recv(subreq
, state
, &outbuf
);
5170 TALLOC_FREE(subreq
);
5172 if (tevent_req_nterror(req
, status
)) {
5176 if (outbuf
.length
!= 56) {
5180 state
->optimal_transfer_size
= PULL_LE_U32(outbuf
.data
, 0);
5181 state
->block_size
= PULL_LE_U32(outbuf
.data
, 4);
5182 state
->total_blocks
= PULL_LE_U64(outbuf
.data
, 8);
5183 state
->blocks_available
= PULL_LE_U64(outbuf
.data
, 16);
5184 state
->user_blocks_available
= PULL_LE_U64(outbuf
.data
, 24);
5185 state
->total_file_nodes
= PULL_LE_U64(outbuf
.data
, 32);
5186 state
->free_file_nodes
= PULL_LE_U64(outbuf
.data
, 40);
5187 state
->fs_identifier
= PULL_LE_U64(outbuf
.data
, 48);
5190 subreq
= cli_smb2_close_fnum_send(state
,
5195 if (tevent_req_nomem(subreq
, req
)) {
5199 tevent_req_set_callback(subreq
, cli_smb2_get_posix_fs_info_done
, req
);
5202 static void cli_smb2_get_posix_fs_info_done(struct tevent_req
*subreq
)
5204 struct tevent_req
*req
= tevent_req_callback_data(
5205 subreq
, struct tevent_req
);
5208 status
= cli_smb2_close_fnum_recv(subreq
);
5209 TALLOC_FREE(subreq
);
5210 if (tevent_req_nterror(req
, status
)) {
5214 tevent_req_done(req
);
5217 NTSTATUS
cli_smb2_get_posix_fs_info_recv(struct tevent_req
*req
,
5218 uint32_t *optimal_transfer_size
,
5219 uint32_t *block_size
,
5220 uint64_t *total_blocks
,
5221 uint64_t *blocks_available
,
5222 uint64_t *user_blocks_available
,
5223 uint64_t *total_file_nodes
,
5224 uint64_t *free_file_nodes
,
5225 uint64_t *fs_identifier
)
5227 struct cli_smb2_get_posix_fs_info_state
*state
= tevent_req_data(
5228 req
, struct cli_smb2_get_posix_fs_info_state
);
5231 if (tevent_req_is_nterror(req
, &status
)) {
5232 tevent_req_received(req
);
5235 *optimal_transfer_size
= state
->optimal_transfer_size
;
5236 *block_size
= state
->block_size
;
5237 *total_blocks
= state
->total_blocks
;
5238 *blocks_available
= state
->blocks_available
;
5239 *user_blocks_available
= state
->user_blocks_available
;
5240 *total_file_nodes
= state
->total_file_nodes
;
5241 *free_file_nodes
= state
->free_file_nodes
;
5242 *fs_identifier
= state
->fs_identifier
;
5243 tevent_req_received(req
);
5244 return NT_STATUS_OK
;