ctdb-scripts: Move connection tracking to 10.interface
[samba4-gss.git] / source3 / libsmb / cli_smb2_fnum.c
blob91933c6efe983edcaf360043b65639d295b86784
1 /*
2 Unix SMB/CIFS implementation.
3 smb2 lib
4 Copyright (C) Jeremy Allison 2013
5 Copyright (C) Volker Lendecke 2013
6 Copyright (C) Stefan Metzmacher 2013
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 This code is a thin wrapper around the existing
24 cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c,
25 but allows the handles to be mapped to uint16_t fnums,
26 which are easier for smbclient to use.
29 #include "includes.h"
30 #include "client.h"
31 #include "async_smb.h"
32 #include "../libcli/smb/smbXcli_base.h"
33 #include "cli_smb2_fnum.h"
34 #include "trans2.h"
35 #include "clirap.h"
36 #include "../libcli/smb/smb2_create_blob.h"
37 #include "libsmb/proto.h"
38 #include "lib/util/tevent_ntstatus.h"
39 #include "../libcli/security/security.h"
40 #include "../librpc/gen_ndr/ndr_security.h"
41 #include "lib/util_ea.h"
42 #include "librpc/gen_ndr/ndr_ioctl.h"
43 #include "ntioctl.h"
44 #include "librpc/gen_ndr/ndr_quota.h"
45 #include "librpc/gen_ndr/ndr_smb3posix.h"
46 #include "lib/util/string_wrappers.h"
47 #include "lib/util/idtree.h"
48 #include "libcli/smb/smb2_posix.h"
50 struct smb2_hnd {
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,
68 bool posix,
69 uint16_t *pfnum)
71 int ret;
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,
82 .posix = posix,
85 if (idp == NULL) {
86 /* Lazy init */
87 cli->smb2.open_handles = idr_init(cli);
88 if (cli->smb2.open_handles == NULL) {
89 TALLOC_FREE(owned_h);
90 return NT_STATUS_NO_MEMORY;
92 idp = cli->smb2.open_handles;
95 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
96 if (ret == -1) {
97 TALLOC_FREE(owned_h);
98 return NT_STATUS_NO_MEMORY;
101 *pfnum = (uint16_t)ret;
102 return NT_STATUS_OK;
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;
115 if (idp == NULL) {
116 return NT_STATUS_INVALID_PARAMETER;
118 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
119 if (*pph == NULL) {
120 return NT_STATUS_INVALID_HANDLE;
122 return NT_STATUS_OK;
125 /***************************************************************
126 Delete the fnum to smb2_hnd mapping. Zeros out handle on
127 successful return.
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;
135 struct smb2_hnd *ph;
137 if (idp == NULL) {
138 return NT_STATUS_INVALID_PARAMETER;
141 ph = (struct smb2_hnd *)idr_find(idp, fnum);
142 if (ph != *pph) {
143 return NT_STATUS_INVALID_PARAMETER;
145 idr_remove(idp, fnum);
146 TALLOC_FREE(*pph);
147 return NT_STATUS_OK;
150 /***************************************************************
151 Oplock mapping code.
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,
173 char *path)
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;
179 if (!is_dfs) {
180 return path;
182 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
183 if (is_already_dfs_path) {
184 return path;
186 if (path[0] == '\0') {
187 return talloc_asprintf(ctx,
188 "%s\\%s",
189 smbXcli_conn_remote_name(cli->conn),
190 cli->share);
192 while (*path == '\\') {
193 path++;
195 return talloc_asprintf(ctx,
196 "%s\\%s\\%s",
197 smbXcli_conn_remote_name(cli->conn),
198 cli->share,
199 path);
202 /***************************************************************
203 Small wrapper that allows SMB2 create to return a uint16_t fnum.
204 ***************************************************************/
206 struct cli_smb2_create_fnum_state {
207 struct cli_state *cli;
208 struct smb2_create_blobs in_cblobs;
209 struct smb2_create_blobs out_cblobs;
210 struct smb_create_returns cr;
211 struct symlink_reparse_struct *symlink;
212 uint16_t fnum;
213 struct tevent_req *subreq;
216 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
217 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
219 struct tevent_req *cli_smb2_create_fnum_send(
220 TALLOC_CTX *mem_ctx,
221 struct tevent_context *ev,
222 struct cli_state *cli,
223 const char *fname_in,
224 struct cli_smb2_create_flags create_flags,
225 uint32_t impersonation_level,
226 uint32_t desired_access,
227 uint32_t file_attributes,
228 uint32_t share_access,
229 uint32_t create_disposition,
230 uint32_t create_options,
231 const struct smb2_create_blobs *in_cblobs)
233 struct tevent_req *req, *subreq;
234 struct cli_smb2_create_fnum_state *state;
235 char *fname = NULL;
236 size_t fname_len = 0;
237 bool have_twrp;
238 NTTIME ntt;
239 NTSTATUS status;
241 req = tevent_req_create(mem_ctx, &state,
242 struct cli_smb2_create_fnum_state);
243 if (req == NULL) {
244 return NULL;
246 state->cli = cli;
248 fname = talloc_strdup(state, fname_in);
249 if (tevent_req_nomem(fname, req)) {
250 return tevent_req_post(req, ev);
253 if (cli->backup_intent) {
254 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
257 if (cli->smb2.client_smb311_posix) {
258 uint8_t modebuf[4] = {
262 status =
263 smb2_create_blob_add(state,
264 &state->in_cblobs,
265 SMB2_CREATE_TAG_POSIX,
266 (DATA_BLOB){
267 .data = modebuf,
268 .length = sizeof(modebuf),
270 if (tevent_req_nterror(req, status)) {
271 return tevent_req_post(req, ev);
275 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
276 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
277 if (have_twrp) {
278 status = smb2_create_blob_add(
279 state,
280 &state->in_cblobs,
281 SMB2_CREATE_TAG_TWRP,
282 (DATA_BLOB) {
283 .data = (uint8_t *)&ntt,
284 .length = sizeof(ntt),
286 if (tevent_req_nterror(req, status)) {
287 return tevent_req_post(req, ev);
291 if (in_cblobs != NULL) {
292 uint32_t i;
293 for (i=0; i<in_cblobs->num_blobs; i++) {
294 struct smb2_create_blob *b = &in_cblobs->blobs[i];
295 status = smb2_create_blob_add(
296 state, &state->in_cblobs, b->tag, b->data);
297 if (!NT_STATUS_IS_OK(status)) {
298 tevent_req_nterror(req, status);
299 return tevent_req_post(req, ev);
304 fname = smb2_dfs_share_path(state, cli, fname);
305 if (tevent_req_nomem(fname, req)) {
306 return tevent_req_post(req, ev);
308 fname_len = strlen(fname);
310 /* SMB2 is pickier about pathnames. Ensure it doesn't
311 start in a '\' */
312 if (*fname == '\\') {
313 fname++;
314 fname_len--;
317 /* Or end in a '\' */
318 if (fname_len > 0 && fname[fname_len-1] == '\\') {
319 fname[fname_len-1] = '\0';
322 subreq = smb2cli_create_send(state, ev,
323 cli->conn,
324 cli->timeout,
325 cli->smb2.session,
326 cli->smb2.tcon,
327 fname,
328 flags_to_smb2_oplock(create_flags),
329 impersonation_level,
330 desired_access,
331 file_attributes,
332 share_access,
333 create_disposition,
334 create_options,
335 &state->in_cblobs);
336 if (tevent_req_nomem(subreq, req)) {
337 return tevent_req_post(req, ev);
339 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
341 state->subreq = subreq;
342 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
344 return req;
347 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
349 struct tevent_req *req = tevent_req_callback_data(
350 subreq, struct tevent_req);
351 struct cli_smb2_create_fnum_state *state = tevent_req_data(
352 req, struct cli_smb2_create_fnum_state);
353 uint64_t fid_persistent, fid_volatile;
354 struct smb2_create_blob *posix = NULL;
355 NTSTATUS status;
357 status = smb2cli_create_recv(subreq,
358 &fid_persistent,
359 &fid_volatile,
360 &state->cr,
361 state,
362 &state->out_cblobs,
363 &state->symlink);
364 TALLOC_FREE(subreq);
365 if (tevent_req_nterror(req, status)) {
366 return;
369 posix = smb2_create_blob_find(&state->in_cblobs,
370 SMB2_CREATE_TAG_POSIX);
372 status = map_smb2_handle_to_fnum(state->cli,
373 fid_persistent,
374 fid_volatile,
375 (posix != NULL),
376 &state->fnum);
377 if (tevent_req_nterror(req, status)) {
378 return;
380 tevent_req_done(req);
383 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
385 struct cli_smb2_create_fnum_state *state = tevent_req_data(
386 req, struct cli_smb2_create_fnum_state);
387 return tevent_req_cancel(state->subreq);
390 NTSTATUS cli_smb2_create_fnum_recv(
391 struct tevent_req *req,
392 uint16_t *pfnum,
393 struct smb_create_returns *cr,
394 TALLOC_CTX *mem_ctx,
395 struct smb2_create_blobs *out_cblobs,
396 struct symlink_reparse_struct **symlink)
398 struct cli_smb2_create_fnum_state *state = tevent_req_data(
399 req, struct cli_smb2_create_fnum_state);
400 NTSTATUS status;
402 if (tevent_req_is_nterror(req, &status)) {
403 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
404 (symlink != NULL)) {
405 *symlink = talloc_move(mem_ctx, &state->symlink);
407 return status;
409 if (pfnum != NULL) {
410 *pfnum = state->fnum;
412 if (cr != NULL) {
413 *cr = state->cr;
415 if (out_cblobs != NULL) {
416 *out_cblobs = (struct smb2_create_blobs) {
417 .num_blobs = state->out_cblobs.num_blobs,
418 .blobs = talloc_move(
419 mem_ctx, &state->out_cblobs.blobs),
422 return NT_STATUS_OK;
425 bool cli_smb2_fnum_is_posix(struct cli_state *cli, uint16_t fnum)
427 struct smb2_hnd *ph = NULL;
428 NTSTATUS status;
430 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
431 if (!NT_STATUS_IS_OK(status)) {
432 return false;
434 return ph->posix;
437 NTSTATUS cli_smb2_create_fnum(
438 struct cli_state *cli,
439 const char *fname,
440 struct cli_smb2_create_flags create_flags,
441 uint32_t impersonation_level,
442 uint32_t desired_access,
443 uint32_t file_attributes,
444 uint32_t share_access,
445 uint32_t create_disposition,
446 uint32_t create_options,
447 const struct smb2_create_blobs *in_cblobs,
448 uint16_t *pfid,
449 struct smb_create_returns *cr,
450 TALLOC_CTX *mem_ctx,
451 struct smb2_create_blobs *out_cblobs)
453 TALLOC_CTX *frame = talloc_stackframe();
454 struct tevent_context *ev;
455 struct tevent_req *req;
456 NTSTATUS status = NT_STATUS_NO_MEMORY;
458 if (smbXcli_conn_has_async_calls(cli->conn)) {
460 * Can't use sync call while an async call is in flight
462 status = NT_STATUS_INVALID_PARAMETER;
463 goto fail;
465 ev = samba_tevent_context_init(frame);
466 if (ev == NULL) {
467 goto fail;
469 req = cli_smb2_create_fnum_send(
470 frame,
472 cli,
473 fname,
474 create_flags,
475 impersonation_level,
476 desired_access,
477 file_attributes,
478 share_access,
479 create_disposition,
480 create_options,
481 in_cblobs);
482 if (req == NULL) {
483 goto fail;
485 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
486 goto fail;
488 status = cli_smb2_create_fnum_recv(
489 req, pfid, cr, mem_ctx, out_cblobs, NULL);
490 fail:
491 TALLOC_FREE(frame);
492 return status;
495 /***************************************************************
496 Small wrapper that allows SMB2 close to use a uint16_t fnum.
497 ***************************************************************/
499 struct cli_smb2_close_fnum_state {
500 struct cli_state *cli;
501 uint16_t fnum;
502 struct smb2_hnd *ph;
505 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
507 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
508 struct tevent_context *ev,
509 struct cli_state *cli,
510 uint16_t fnum,
511 uint16_t flags)
513 struct tevent_req *req, *subreq;
514 struct cli_smb2_close_fnum_state *state;
515 NTSTATUS status;
517 req = tevent_req_create(mem_ctx, &state,
518 struct cli_smb2_close_fnum_state);
519 if (req == NULL) {
520 return NULL;
522 state->cli = cli;
523 state->fnum = fnum;
525 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
526 if (tevent_req_nterror(req, status)) {
527 return tevent_req_post(req, ev);
530 subreq = smb2cli_close_send(state,
532 cli->conn,
533 cli->timeout,
534 cli->smb2.session,
535 cli->smb2.tcon,
536 flags,
537 state->ph->fid_persistent,
538 state->ph->fid_volatile);
539 if (tevent_req_nomem(subreq, req)) {
540 return tevent_req_post(req, ev);
542 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
543 return req;
546 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
548 struct tevent_req *req = tevent_req_callback_data(
549 subreq, struct tevent_req);
550 struct cli_smb2_close_fnum_state *state = tevent_req_data(
551 req, struct cli_smb2_close_fnum_state);
552 NTSTATUS status;
554 status = smb2cli_close_recv(subreq);
555 if (tevent_req_nterror(req, status)) {
556 return;
559 /* Delete the fnum -> handle mapping. */
560 status = delete_smb2_handle_mapping(state->cli, &state->ph,
561 state->fnum);
562 if (tevent_req_nterror(req, status)) {
563 return;
565 tevent_req_done(req);
568 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
570 return tevent_req_simple_recv_ntstatus(req);
573 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
575 TALLOC_CTX *frame = talloc_stackframe();
576 struct tevent_context *ev;
577 struct tevent_req *req;
578 NTSTATUS status = NT_STATUS_NO_MEMORY;
580 if (smbXcli_conn_has_async_calls(cli->conn)) {
582 * Can't use sync call while an async call is in flight
584 status = NT_STATUS_INVALID_PARAMETER;
585 goto fail;
587 ev = samba_tevent_context_init(frame);
588 if (ev == NULL) {
589 goto fail;
591 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
592 if (req == NULL) {
593 goto fail;
595 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
596 goto fail;
598 status = cli_smb2_close_fnum_recv(req);
599 fail:
600 TALLOC_FREE(frame);
601 return status;
604 struct cli_smb2_set_info_fnum_state {
605 uint8_t dummy;
608 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
610 struct tevent_req *cli_smb2_set_info_fnum_send(
611 TALLOC_CTX *mem_ctx,
612 struct tevent_context *ev,
613 struct cli_state *cli,
614 uint16_t fnum,
615 uint8_t in_info_type,
616 uint8_t in_info_class,
617 const DATA_BLOB *in_input_buffer,
618 uint32_t in_additional_info)
620 struct tevent_req *req = NULL, *subreq = NULL;
621 struct cli_smb2_set_info_fnum_state *state = NULL;
622 struct smb2_hnd *ph = NULL;
623 NTSTATUS status;
625 req = tevent_req_create(
626 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
627 if (req == NULL) {
628 return NULL;
631 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
632 if (tevent_req_nterror(req, status)) {
633 return tevent_req_post(req, ev);
636 subreq = smb2cli_set_info_send(
637 state,
639 cli->conn,
640 cli->timeout,
641 cli->smb2.session,
642 cli->smb2.tcon,
643 in_info_type,
644 in_info_class,
645 in_input_buffer,
646 in_additional_info,
647 ph->fid_persistent,
648 ph->fid_volatile);
649 if (tevent_req_nomem(subreq, req)) {
650 return tevent_req_post(req, ev);
652 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
653 return req;
656 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
658 NTSTATUS status = smb2cli_set_info_recv(subreq);
659 tevent_req_simple_finish_ntstatus(subreq, status);
662 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
664 return tevent_req_simple_recv_ntstatus(req);
667 NTSTATUS cli_smb2_set_info_fnum(
668 struct cli_state *cli,
669 uint16_t fnum,
670 uint8_t in_info_type,
671 uint8_t in_info_class,
672 const DATA_BLOB *in_input_buffer,
673 uint32_t in_additional_info)
675 TALLOC_CTX *frame = talloc_stackframe();
676 struct tevent_context *ev = NULL;
677 struct tevent_req *req = NULL;
678 NTSTATUS status = NT_STATUS_NO_MEMORY;
679 bool ok;
681 if (smbXcli_conn_has_async_calls(cli->conn)) {
683 * Can't use sync call while an async call is in flight
685 status = NT_STATUS_INVALID_PARAMETER;
686 goto fail;
688 ev = samba_tevent_context_init(frame);
689 if (ev == NULL) {
690 goto fail;
692 req = cli_smb2_set_info_fnum_send(
693 frame,
695 cli,
696 fnum,
697 in_info_type,
698 in_info_class,
699 in_input_buffer,
700 in_additional_info);
701 if (req == NULL) {
702 goto fail;
704 ok = tevent_req_poll_ntstatus(req, ev, &status);
705 if (!ok) {
706 goto fail;
708 status = cli_smb2_set_info_fnum_recv(req);
709 fail:
710 TALLOC_FREE(frame);
711 return status;
714 struct cli_smb2_delete_on_close_state {
715 struct cli_state *cli;
716 uint8_t data[1];
717 DATA_BLOB inbuf;
720 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
722 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
723 struct tevent_context *ev,
724 struct cli_state *cli,
725 uint16_t fnum,
726 bool flag)
728 struct tevent_req *req = NULL;
729 struct cli_smb2_delete_on_close_state *state = NULL;
730 struct tevent_req *subreq = NULL;
732 req = tevent_req_create(mem_ctx, &state,
733 struct cli_smb2_delete_on_close_state);
734 if (req == NULL) {
735 return NULL;
737 state->cli = cli;
739 /* Setup data array. */
740 SCVAL(&state->data[0], 0, flag ? 1 : 0);
741 state->inbuf.data = &state->data[0];
742 state->inbuf.length = 1;
744 subreq = cli_smb2_set_info_fnum_send(state,
746 cli,
747 fnum,
748 SMB2_0_INFO_FILE,
749 FSCC_FILE_DISPOSITION_INFORMATION,
750 &state->inbuf,
752 if (tevent_req_nomem(subreq, req)) {
753 return tevent_req_post(req, ev);
755 tevent_req_set_callback(subreq,
756 cli_smb2_delete_on_close_done,
757 req);
758 return req;
761 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
763 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
764 tevent_req_simple_finish_ntstatus(subreq, status);
767 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
769 return tevent_req_simple_recv_ntstatus(req);
772 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
774 TALLOC_CTX *frame = talloc_stackframe();
775 struct tevent_context *ev;
776 struct tevent_req *req;
777 NTSTATUS status = NT_STATUS_NO_MEMORY;
779 if (smbXcli_conn_has_async_calls(cli->conn)) {
781 * Can't use sync call while an async call is in flight
783 status = NT_STATUS_INVALID_PARAMETER;
784 goto fail;
786 ev = samba_tevent_context_init(frame);
787 if (ev == NULL) {
788 goto fail;
790 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
791 if (req == NULL) {
792 goto fail;
794 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
795 goto fail;
797 status = cli_smb2_delete_on_close_recv(req);
798 fail:
799 TALLOC_FREE(frame);
800 return status;
803 struct cli_smb2_mkdir_state {
804 struct tevent_context *ev;
805 struct cli_state *cli;
808 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
809 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
811 struct tevent_req *cli_smb2_mkdir_send(
812 TALLOC_CTX *mem_ctx,
813 struct tevent_context *ev,
814 struct cli_state *cli,
815 const char *dname)
817 struct tevent_req *req = NULL, *subreq = NULL;
818 struct cli_smb2_mkdir_state *state = NULL;
820 req = tevent_req_create(
821 mem_ctx, &state, struct cli_smb2_mkdir_state);
822 if (req == NULL) {
823 return NULL;
825 state->ev = ev;
826 state->cli = cli;
828 /* Ensure this is a directory. */
829 subreq = cli_smb2_create_fnum_send(
830 state, /* mem_ctx */
831 ev, /* ev */
832 cli, /* cli */
833 dname, /* fname */
834 (struct cli_smb2_create_flags){0}, /* create_flags */
835 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
836 FILE_READ_ATTRIBUTES, /* desired_access */
837 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
838 FILE_SHARE_READ|
839 FILE_SHARE_WRITE, /* share_access */
840 FILE_CREATE, /* create_disposition */
841 FILE_DIRECTORY_FILE, /* create_options */
842 NULL); /* in_cblobs */
843 if (tevent_req_nomem(subreq, req)) {
844 return tevent_req_post(req, ev);
846 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
847 return req;
850 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
852 struct tevent_req *req = tevent_req_callback_data(
853 subreq, struct tevent_req);
854 struct cli_smb2_mkdir_state *state = tevent_req_data(
855 req, struct cli_smb2_mkdir_state);
856 NTSTATUS status;
857 uint16_t fnum = 0xffff;
859 status = cli_smb2_create_fnum_recv(
860 subreq, &fnum, NULL, NULL, NULL, NULL);
861 TALLOC_FREE(subreq);
862 if (tevent_req_nterror(req, status)) {
863 return;
866 subreq =
867 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
868 if (tevent_req_nomem(subreq, req)) {
869 return;
871 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
874 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
876 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
877 tevent_req_simple_finish_ntstatus(subreq, status);
880 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
882 return tevent_req_simple_recv_ntstatus(req);
885 struct cli_smb2_rmdir_state {
886 struct tevent_context *ev;
887 struct cli_state *cli;
888 const char *dname;
889 const struct smb2_create_blobs *in_cblobs;
890 uint16_t fnum;
891 NTSTATUS status;
894 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
895 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
896 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
897 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
899 struct tevent_req *cli_smb2_rmdir_send(
900 TALLOC_CTX *mem_ctx,
901 struct tevent_context *ev,
902 struct cli_state *cli,
903 const char *dname,
904 const struct smb2_create_blobs *in_cblobs)
906 struct tevent_req *req = NULL, *subreq = NULL;
907 struct cli_smb2_rmdir_state *state = NULL;
909 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
910 if (req == NULL) {
911 return NULL;
913 state->ev = ev;
914 state->cli = cli;
915 state->dname = dname;
916 state->in_cblobs = in_cblobs;
918 subreq = cli_smb2_create_fnum_send(
919 state,
920 state->ev,
921 state->cli,
922 state->dname,
923 (struct cli_smb2_create_flags){0},
924 SMB2_IMPERSONATION_IMPERSONATION,
925 DELETE_ACCESS, /* desired_access */
926 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
927 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
928 FILE_OPEN, /* create_disposition */
929 FILE_DIRECTORY_FILE, /* create_options */
930 state->in_cblobs); /* in_cblobs */
931 if (tevent_req_nomem(subreq, req)) {
932 return tevent_req_post(req, ev);
934 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
935 return req;
938 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
940 struct tevent_req *req = tevent_req_callback_data(
941 subreq, struct tevent_req);
942 struct cli_smb2_rmdir_state *state = tevent_req_data(
943 req, struct cli_smb2_rmdir_state);
944 NTSTATUS status;
946 status = cli_smb2_create_fnum_recv(
947 subreq, &state->fnum, NULL, NULL, NULL, NULL);
948 TALLOC_FREE(subreq);
950 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
952 * Naive option to match our SMB1 code. Assume the
953 * symlink path that tripped us up was the last
954 * component and try again. Eventually we will have to
955 * deal with the returned path unprocessed component. JRA.
957 subreq = cli_smb2_create_fnum_send(
958 state,
959 state->ev,
960 state->cli,
961 state->dname,
962 (struct cli_smb2_create_flags){0},
963 SMB2_IMPERSONATION_IMPERSONATION,
964 DELETE_ACCESS, /* desired_access */
965 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
966 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
967 FILE_OPEN, /* create_disposition */
968 FILE_DIRECTORY_FILE|
969 FILE_DELETE_ON_CLOSE|
970 FILE_OPEN_REPARSE_POINT, /* create_options */
971 state->in_cblobs); /* in_cblobs */
972 if (tevent_req_nomem(subreq, req)) {
973 return;
975 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
976 return;
979 if (tevent_req_nterror(req, status)) {
980 return;
983 subreq = cli_smb2_delete_on_close_send(
984 state, state->ev, state->cli, state->fnum, true);
985 if (tevent_req_nomem(subreq, req)) {
986 return;
988 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
991 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
993 struct tevent_req *req = tevent_req_callback_data(
994 subreq, struct tevent_req);
995 struct cli_smb2_rmdir_state *state = tevent_req_data(
996 req, struct cli_smb2_rmdir_state);
997 NTSTATUS status;
999 status = cli_smb2_create_fnum_recv(
1000 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1001 TALLOC_FREE(subreq);
1002 if (tevent_req_nterror(req, status)) {
1003 return;
1006 subreq = cli_smb2_delete_on_close_send(
1007 state, state->ev, state->cli, state->fnum, true);
1008 if (tevent_req_nomem(subreq, req)) {
1009 return;
1011 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1014 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1016 struct tevent_req *req = tevent_req_callback_data(
1017 subreq, struct tevent_req);
1018 struct cli_smb2_rmdir_state *state = tevent_req_data(
1019 req, struct cli_smb2_rmdir_state);
1021 state->status = cli_smb2_delete_on_close_recv(subreq);
1022 TALLOC_FREE(subreq);
1025 * Close the fd even if the set_disp failed
1028 subreq = cli_smb2_close_fnum_send(state,
1029 state->ev,
1030 state->cli,
1031 state->fnum,
1033 if (tevent_req_nomem(subreq, req)) {
1034 return;
1036 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1039 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1041 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1042 tevent_req_simple_finish_ntstatus(subreq, status);
1045 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1047 struct cli_smb2_rmdir_state *state = tevent_req_data(
1048 req, struct cli_smb2_rmdir_state);
1049 NTSTATUS status;
1051 if (tevent_req_is_nterror(req, &status)) {
1052 return status;
1054 return state->status;
1057 /***************************************************************
1058 Small wrapper that allows SMB2 to unlink a pathname.
1059 ***************************************************************/
1061 struct cli_smb2_unlink_state {
1062 struct tevent_context *ev;
1063 struct cli_state *cli;
1064 const char *fname;
1065 const struct smb2_create_blobs *in_cblobs;
1068 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1069 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1070 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1072 struct tevent_req *cli_smb2_unlink_send(
1073 TALLOC_CTX *mem_ctx,
1074 struct tevent_context *ev,
1075 struct cli_state *cli,
1076 const char *fname,
1077 const struct smb2_create_blobs *in_cblobs)
1079 struct tevent_req *req = NULL, *subreq = NULL;
1080 struct cli_smb2_unlink_state *state = NULL;
1082 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1083 if (req == NULL) {
1084 return NULL;
1086 state->ev = ev;
1087 state->cli = cli;
1088 state->fname = fname;
1089 state->in_cblobs = in_cblobs;
1091 subreq = cli_smb2_create_fnum_send(
1092 state, /* mem_ctx */
1093 state->ev, /* tevent_context */
1094 state->cli, /* cli_struct */
1095 state->fname, /* filename */
1096 (struct cli_smb2_create_flags){0},
1097 SMB2_IMPERSONATION_IMPERSONATION,
1098 DELETE_ACCESS, /* desired_access */
1099 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1100 FILE_SHARE_READ|
1101 FILE_SHARE_WRITE|
1102 FILE_SHARE_DELETE, /* share_access */
1103 FILE_OPEN, /* create_disposition */
1104 FILE_DELETE_ON_CLOSE, /* create_options */
1105 state->in_cblobs); /* in_cblobs */
1106 if (tevent_req_nomem(subreq, req)) {
1107 return tevent_req_post(req, ev);
1109 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1110 return req;
1113 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1115 struct tevent_req *req = tevent_req_callback_data(
1116 subreq, struct tevent_req);
1117 struct cli_smb2_unlink_state *state = tevent_req_data(
1118 req, struct cli_smb2_unlink_state);
1119 uint16_t fnum = 0xffff;
1120 NTSTATUS status;
1122 status = cli_smb2_create_fnum_recv(
1123 subreq, &fnum, NULL, NULL, NULL, NULL);
1124 TALLOC_FREE(subreq);
1126 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1127 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1129 * Naive option to match our SMB1 code. Assume the
1130 * symlink path that tripped us up was the last
1131 * component and try again. Eventually we will have to
1132 * deal with the returned path unprocessed component. JRA.
1134 subreq = cli_smb2_create_fnum_send(
1135 state, /* mem_ctx */
1136 state->ev, /* tevent_context */
1137 state->cli, /* cli_struct */
1138 state->fname, /* filename */
1139 (struct cli_smb2_create_flags){0},
1140 SMB2_IMPERSONATION_IMPERSONATION,
1141 DELETE_ACCESS, /* desired_access */
1142 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1143 FILE_SHARE_READ|
1144 FILE_SHARE_WRITE|
1145 FILE_SHARE_DELETE, /* share_access */
1146 FILE_OPEN, /* create_disposition */
1147 FILE_DELETE_ON_CLOSE|
1148 FILE_OPEN_REPARSE_POINT, /* create_options */
1149 state->in_cblobs); /* in_cblobs */
1150 if (tevent_req_nomem(subreq, req)) {
1151 return;
1153 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1154 return;
1157 if (tevent_req_nterror(req, status)) {
1158 return;
1161 subreq =
1162 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1163 if (tevent_req_nomem(subreq, req)) {
1164 return;
1166 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1169 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1171 struct tevent_req *req = tevent_req_callback_data(
1172 subreq, struct tevent_req);
1173 struct cli_smb2_unlink_state *state = tevent_req_data(
1174 req, struct cli_smb2_unlink_state);
1175 uint16_t fnum = 0xffff;
1176 NTSTATUS status;
1178 status = cli_smb2_create_fnum_recv(
1179 subreq, &fnum, NULL, NULL, NULL, NULL);
1180 TALLOC_FREE(subreq);
1181 if (tevent_req_nterror(req, status)) {
1182 return;
1185 subreq =
1186 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1187 if (tevent_req_nomem(subreq, req)) {
1188 return;
1190 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1193 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1195 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1196 tevent_req_simple_finish_ntstatus(subreq, status);
1199 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1201 return tevent_req_simple_recv_ntstatus(req);
1204 /***************************************************************
1205 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1206 ***************************************************************/
1208 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1209 uint32_t dir_data_length,
1210 struct file_info *finfo,
1211 uint32_t *next_offset)
1213 struct smb3_file_posix_information info = {};
1214 size_t consumed;
1215 enum ndr_err_code ndr_err;
1216 size_t namelen = 0;
1217 size_t ret = 0;
1218 uint32_t _next_offset = 0;
1220 if (dir_data_length < 4) {
1221 return NT_STATUS_INFO_LENGTH_MISMATCH;
1224 _next_offset = IVAL(dir_data, 0);
1226 if (_next_offset > dir_data_length) {
1227 return NT_STATUS_INFO_LENGTH_MISMATCH;
1230 if (_next_offset != 0) {
1231 /* Ensure we only read what in this record. */
1232 dir_data_length = _next_offset;
1236 * Skip NextEntryOffset and FileIndex
1238 if (dir_data_length < 8) {
1239 return NT_STATUS_INFO_LENGTH_MISMATCH;
1241 dir_data += 8;
1242 dir_data_length -= 8;
1244 ndr_err = ndr_pull_struct_blob_noalloc(
1245 dir_data,
1246 dir_data_length,
1247 &info,
1248 (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1249 &consumed);
1250 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1251 return ndr_map_error2ntstatus(ndr_err);
1253 if (consumed > dir_data_length) {
1254 return NT_STATUS_INFO_LENGTH_MISMATCH;
1256 dir_data += consumed;
1257 dir_data_length -= consumed;
1259 finfo->btime_ts = interpret_long_date(info.creation_time);
1260 finfo->atime_ts = interpret_long_date(info.last_access_time);
1261 finfo->mtime_ts = interpret_long_date(info.last_write_time);
1262 finfo->ctime_ts = interpret_long_date(info.change_time);
1263 finfo->allocated_size = info.allocation_size;
1264 finfo->size = info.end_of_file;
1265 finfo->attr = info.file_attributes;
1266 finfo->ino = info.inode;
1267 finfo->st_ex_dev = info.device;
1268 finfo->st_ex_nlink = info.cc.nlinks;
1269 finfo->reparse_tag = info.cc.reparse_tag;
1270 finfo->st_ex_mode = wire_mode_to_unix(info.cc.posix_mode);
1271 sid_copy(&finfo->owner_sid, &info.cc.owner);
1272 sid_copy(&finfo->group_sid, &info.cc.group);
1274 if (dir_data_length < 4) {
1275 return NT_STATUS_INFO_LENGTH_MISMATCH;
1277 namelen = PULL_LE_U32(dir_data, 0);
1279 dir_data += 4;
1280 dir_data_length -= 4;
1282 if (namelen > dir_data_length) {
1283 return NT_STATUS_INFO_LENGTH_MISMATCH;
1286 ret = pull_string_talloc(finfo,
1287 dir_data,
1288 FLAGS2_UNICODE_STRINGS,
1289 &finfo->name,
1290 dir_data,
1291 namelen,
1292 STR_UNICODE);
1293 if (ret == (size_t)-1) {
1294 /* Bad conversion. */
1295 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1298 if (finfo->name == NULL) {
1299 /* Bad conversion. */
1300 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1303 *next_offset = _next_offset;
1304 return NT_STATUS_OK;
1307 /***************************************************************
1308 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1309 ***************************************************************/
1311 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1312 uint32_t dir_data_length,
1313 struct file_info *finfo,
1314 uint32_t *next_offset)
1316 size_t namelen = 0;
1317 size_t slen = 0;
1318 size_t ret = 0;
1320 if (dir_data_length < 4) {
1321 return NT_STATUS_INFO_LENGTH_MISMATCH;
1324 *next_offset = IVAL(dir_data, 0);
1326 if (*next_offset > dir_data_length) {
1327 return NT_STATUS_INFO_LENGTH_MISMATCH;
1330 if (*next_offset != 0) {
1331 /* Ensure we only read what in this record. */
1332 dir_data_length = *next_offset;
1335 if (dir_data_length < 105) {
1336 return NT_STATUS_INFO_LENGTH_MISMATCH;
1339 finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1340 finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1341 finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1342 finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1343 finfo->size = BVAL(dir_data + 40, 0);
1344 finfo->allocated_size = BVAL(dir_data + 48, 0);
1345 finfo->attr = IVAL(dir_data + 56, 0);
1346 finfo->ino = BVAL(dir_data + 96, 0);
1347 namelen = IVAL(dir_data + 60,0);
1348 if (namelen > (dir_data_length - 104)) {
1349 return NT_STATUS_INFO_LENGTH_MISMATCH;
1351 finfo->reparse_tag = IVAL(dir_data + 64, 0);
1352 slen = CVAL(dir_data + 68, 0);
1353 if (slen > 24) {
1354 return NT_STATUS_INFO_LENGTH_MISMATCH;
1356 ret = pull_string_talloc(finfo,
1357 dir_data,
1358 FLAGS2_UNICODE_STRINGS,
1359 &finfo->short_name,
1360 dir_data + 70,
1361 slen,
1362 STR_UNICODE);
1363 if (ret == (size_t)-1) {
1364 /* Bad conversion. */
1365 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1368 ret = pull_string_talloc(finfo,
1369 dir_data,
1370 FLAGS2_UNICODE_STRINGS,
1371 &finfo->name,
1372 dir_data + 104,
1373 namelen,
1374 STR_UNICODE);
1375 if (ret == (size_t)-1) {
1376 /* Bad conversion. */
1377 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1380 if (finfo->name == NULL) {
1381 /* Bad conversion. */
1382 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1385 return NT_STATUS_OK;
1388 /*******************************************************************
1389 Given a filename - get its directory name
1390 ********************************************************************/
1392 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1393 const char *dir,
1394 char **parent,
1395 const char **name)
1397 char *p;
1398 ptrdiff_t len;
1400 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1402 if (p == NULL) {
1403 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1404 return false;
1406 if (name) {
1407 *name = dir;
1409 return true;
1412 len = p-dir;
1414 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1415 return false;
1417 (*parent)[len] = '\0';
1419 if (name) {
1420 *name = p+1;
1422 return true;
1425 struct cli_smb2_list_dir_data {
1426 uint8_t *data;
1427 uint32_t length;
1430 struct cli_smb2_list_state {
1431 struct tevent_context *ev;
1432 struct cli_state *cli;
1433 const char *mask;
1435 uint16_t fnum;
1437 NTSTATUS status;
1438 struct cli_smb2_list_dir_data *response;
1439 uint32_t offset;
1440 unsigned int info_level;
1443 static void cli_smb2_list_opened(struct tevent_req *subreq);
1444 static void cli_smb2_list_done(struct tevent_req *subreq);
1445 static void cli_smb2_list_closed(struct tevent_req *subreq);
1447 struct tevent_req *cli_smb2_list_send(
1448 TALLOC_CTX *mem_ctx,
1449 struct tevent_context *ev,
1450 struct cli_state *cli,
1451 const char *pathname,
1452 unsigned int info_level)
1454 struct tevent_req *req = NULL, *subreq = NULL;
1455 struct cli_smb2_list_state *state = NULL;
1456 char *parent = NULL;
1457 bool ok;
1458 struct smb2_create_blobs *in_cblobs = NULL;
1460 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1461 if (req == NULL) {
1462 return NULL;
1464 state->ev = ev;
1465 state->cli = cli;
1466 state->status = NT_STATUS_OK;
1467 state->info_level = info_level;
1469 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1470 if (!ok) {
1471 tevent_req_oom(req);
1472 return tevent_req_post(req, ev);
1475 if (smbXcli_conn_have_posix(cli->conn) &&
1476 info_level == SMB2_FIND_POSIX_INFORMATION)
1478 NTSTATUS status;
1480 /* The mode MUST be 0 when opening an existing file/dir, and
1481 * will be ignored by the server.
1483 uint8_t linear_mode[4] = { 0 };
1484 DATA_BLOB blob = { .data=linear_mode,
1485 .length=sizeof(linear_mode) };
1487 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1488 if (in_cblobs == NULL) {
1489 return NULL;
1492 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1493 SMB2_CREATE_TAG_POSIX, blob);
1494 if (tevent_req_nterror(req, status)) {
1495 tevent_req_nterror(req, status);
1496 return tevent_req_post(req, ev);
1500 subreq = cli_smb2_create_fnum_send(
1501 state, /* mem_ctx */
1502 ev, /* ev */
1503 cli, /* cli */
1504 parent, /* fname */
1505 (struct cli_smb2_create_flags){0}, /* create_flags */
1506 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1507 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1508 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1509 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1510 FILE_OPEN, /* create_disposition */
1511 FILE_DIRECTORY_FILE, /* create_options */
1512 in_cblobs); /* in_cblobs */
1513 TALLOC_FREE(in_cblobs);
1514 if (tevent_req_nomem(subreq, req)) {
1515 return tevent_req_post(req, ev);
1517 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1518 return req;
1521 static void cli_smb2_list_opened(struct tevent_req *subreq)
1523 struct tevent_req *req = tevent_req_callback_data(
1524 subreq, struct tevent_req);
1525 struct cli_smb2_list_state *state = tevent_req_data(
1526 req, struct cli_smb2_list_state);
1527 NTSTATUS status;
1529 status = cli_smb2_create_fnum_recv(
1530 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1531 TALLOC_FREE(subreq);
1532 if (tevent_req_nterror(req, status)) {
1533 return;
1537 * Make our caller get back to us via cli_smb2_list_recv(),
1538 * triggering the smb2_query_directory_send()
1540 tevent_req_defer_callback(req, state->ev);
1541 tevent_req_notify_callback(req);
1544 static void cli_smb2_list_done(struct tevent_req *subreq)
1546 struct tevent_req *req = tevent_req_callback_data(
1547 subreq, struct tevent_req);
1548 struct cli_smb2_list_state *state = tevent_req_data(
1549 req, struct cli_smb2_list_state);
1550 struct cli_smb2_list_dir_data *response = NULL;
1552 response = talloc(state, struct cli_smb2_list_dir_data);
1553 if (tevent_req_nomem(response, req)) {
1554 return;
1557 state->status = smb2cli_query_directory_recv(
1558 subreq, response, &response->data, &response->length);
1559 TALLOC_FREE(subreq);
1561 if (NT_STATUS_IS_OK(state->status)) {
1562 state->response = response;
1563 state->offset = 0;
1565 tevent_req_defer_callback(req, state->ev);
1566 tevent_req_notify_callback(req);
1567 return;
1570 TALLOC_FREE(response);
1572 subreq = cli_smb2_close_fnum_send(state,
1573 state->ev,
1574 state->cli,
1575 state->fnum,
1577 if (tevent_req_nomem(subreq, req)) {
1578 return;
1580 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1583 static void cli_smb2_list_closed(struct tevent_req *subreq)
1585 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1586 tevent_req_simple_finish_ntstatus(subreq, status);
1590 * Return the next finfo directory.
1592 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1593 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1594 * NT_STATUS_RETRY, which will then trigger the caller again when the
1595 * QUERY_DIRECTORY has returned with another buffer. This way we
1596 * guarantee that no asynchronous request is open after this call
1597 * returns an entry, so that other synchronous requests can be issued
1598 * on the same connection while the directory listing proceeds.
1600 NTSTATUS cli_smb2_list_recv(
1601 struct tevent_req *req,
1602 TALLOC_CTX *mem_ctx,
1603 struct file_info **pfinfo)
1605 struct cli_smb2_list_state *state = tevent_req_data(
1606 req, struct cli_smb2_list_state);
1607 struct cli_smb2_list_dir_data *response = NULL;
1608 struct file_info *finfo = NULL;
1609 NTSTATUS status;
1610 uint32_t next_offset = 0;
1611 bool in_progress;
1613 in_progress = tevent_req_is_in_progress(req);
1615 if (!in_progress) {
1616 if (!tevent_req_is_nterror(req, &status)) {
1617 status = NT_STATUS_NO_MORE_FILES;
1619 goto fail;
1622 response = state->response;
1623 if (response == NULL) {
1624 struct tevent_req *subreq = NULL;
1625 struct cli_state *cli = state->cli;
1626 struct smb2_hnd *ph = NULL;
1627 uint32_t max_trans, max_avail_len;
1628 bool ok;
1630 if (!NT_STATUS_IS_OK(state->status)) {
1631 status = state->status;
1632 goto fail;
1635 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1636 if (!NT_STATUS_IS_OK(status)) {
1637 goto fail;
1640 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1641 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1642 if (ok) {
1643 max_trans = MIN(max_trans, max_avail_len);
1646 subreq = smb2cli_query_directory_send(
1647 state, /* mem_ctx */
1648 state->ev, /* ev */
1649 cli->conn, /* conn */
1650 cli->timeout, /* timeout_msec */
1651 cli->smb2.session, /* session */
1652 cli->smb2.tcon, /* tcon */
1653 state->info_level, /* level */
1654 0, /* flags */
1655 0, /* file_index */
1656 ph->fid_persistent, /* fid_persistent */
1657 ph->fid_volatile, /* fid_volatile */
1658 state->mask, /* mask */
1659 max_trans); /* outbuf_len */
1660 if (subreq == NULL) {
1661 status = NT_STATUS_NO_MEMORY;
1662 goto fail;
1664 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1665 return NT_STATUS_RETRY;
1668 SMB_ASSERT(response->length > state->offset);
1670 finfo = talloc_zero(mem_ctx, struct file_info);
1671 if (finfo == NULL) {
1672 status = NT_STATUS_NO_MEMORY;
1673 goto fail;
1676 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1677 status = parse_finfo_posix_info(
1678 response->data + state->offset,
1679 response->length - state->offset,
1680 finfo,
1681 &next_offset);
1682 } else {
1683 status = parse_finfo_id_both_directory_info(
1684 response->data + state->offset,
1685 response->length - state->offset,
1686 finfo,
1687 &next_offset);
1689 if (!NT_STATUS_IS_OK(status)) {
1690 goto fail;
1693 status = is_bad_finfo_name(state->cli, finfo);
1694 if (!NT_STATUS_IS_OK(status)) {
1695 goto fail;
1699 * parse_finfo_id_both_directory_info() checks for overflow,
1700 * no need to check again here.
1702 state->offset += next_offset;
1704 if (next_offset == 0) {
1705 TALLOC_FREE(state->response);
1708 tevent_req_defer_callback(req, state->ev);
1709 tevent_req_notify_callback(req);
1711 *pfinfo = finfo;
1712 return NT_STATUS_OK;
1714 fail:
1715 TALLOC_FREE(finfo);
1716 tevent_req_received(req);
1717 return status;
1720 /***************************************************************
1721 Wrapper that allows SMB2 to query a path info (basic level).
1722 Synchronous only.
1723 ***************************************************************/
1725 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1726 const char *name,
1727 SMB_STRUCT_STAT *sbuf,
1728 uint32_t *attributes)
1730 NTSTATUS status;
1731 struct smb_create_returns cr;
1732 uint16_t fnum = 0xffff;
1733 size_t namelen = strlen(name);
1735 if (smbXcli_conn_has_async_calls(cli->conn)) {
1737 * Can't use sync call while an async call is in flight
1739 return NT_STATUS_INVALID_PARAMETER;
1742 /* SMB2 is pickier about pathnames. Ensure it doesn't
1743 end in a '\' */
1744 if (namelen > 0 && name[namelen-1] == '\\') {
1745 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1746 if (modname == NULL) {
1747 return NT_STATUS_NO_MEMORY;
1749 name = modname;
1752 /* This is commonly used as a 'cd'. Try qpathinfo on
1753 a directory handle first. */
1755 status = cli_smb2_create_fnum(cli,
1756 name,
1757 (struct cli_smb2_create_flags){0},
1758 SMB2_IMPERSONATION_IMPERSONATION,
1759 FILE_READ_ATTRIBUTES, /* desired_access */
1760 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1761 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1762 FILE_OPEN, /* create_disposition */
1763 FILE_DIRECTORY_FILE, /* create_options */
1764 NULL,
1765 &fnum,
1766 &cr,
1767 NULL,
1768 NULL);
1770 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1771 /* Maybe a file ? */
1772 status = cli_smb2_create_fnum(cli,
1773 name,
1774 (struct cli_smb2_create_flags){0},
1775 SMB2_IMPERSONATION_IMPERSONATION,
1776 FILE_READ_ATTRIBUTES, /* desired_access */
1777 0, /* file attributes */
1778 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1779 FILE_OPEN, /* create_disposition */
1780 0, /* create_options */
1781 NULL,
1782 &fnum,
1783 &cr,
1784 NULL,
1785 NULL);
1788 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1789 /* Maybe a reparse point ? */
1790 status = cli_smb2_create_fnum(cli,
1791 name,
1792 (struct cli_smb2_create_flags){0},
1793 SMB2_IMPERSONATION_IMPERSONATION,
1794 FILE_READ_ATTRIBUTES, /* desired_access */
1795 0, /* file attributes */
1796 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1797 FILE_OPEN, /* create_disposition */
1798 FILE_OPEN_REPARSE_POINT, /* create_options */
1799 NULL,
1800 &fnum,
1801 &cr,
1802 NULL,
1803 NULL);
1806 if (!NT_STATUS_IS_OK(status)) {
1807 return status;
1810 status = cli_smb2_close_fnum(cli, fnum);
1812 ZERO_STRUCTP(sbuf);
1814 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1815 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1816 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1817 sbuf->st_ex_size = cr.end_of_file;
1818 *attributes = cr.file_attributes;
1820 return status;
1823 struct cli_smb2_query_info_fnum_state {
1824 DATA_BLOB outbuf;
1827 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1829 struct tevent_req *cli_smb2_query_info_fnum_send(
1830 TALLOC_CTX *mem_ctx,
1831 struct tevent_context *ev,
1832 struct cli_state *cli,
1833 uint16_t fnum,
1834 uint8_t in_info_type,
1835 uint8_t in_info_class,
1836 uint32_t in_max_output_length,
1837 const DATA_BLOB *in_input_buffer,
1838 uint32_t in_additional_info,
1839 uint32_t in_flags)
1841 struct tevent_req *req = NULL, *subreq = NULL;
1842 struct cli_smb2_query_info_fnum_state *state = NULL;
1843 struct smb2_hnd *ph = NULL;
1844 NTSTATUS status;
1846 req = tevent_req_create(
1847 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1848 if (req == NULL) {
1849 return req;
1852 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1853 if (tevent_req_nterror(req, status)) {
1854 return tevent_req_post(req, ev);
1857 subreq = smb2cli_query_info_send(
1858 state,
1860 cli->conn,
1861 cli->timeout,
1862 cli->smb2.session,
1863 cli->smb2.tcon,
1864 in_info_type,
1865 in_info_class,
1866 in_max_output_length,
1867 in_input_buffer,
1868 in_additional_info,
1869 in_flags,
1870 ph->fid_persistent,
1871 ph->fid_volatile);
1872 if (tevent_req_nomem(subreq, req)) {
1873 return tevent_req_post(req, ev);
1875 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1876 return req;
1879 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1881 struct tevent_req *req = tevent_req_callback_data(
1882 subreq, struct tevent_req);
1883 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1884 req, struct cli_smb2_query_info_fnum_state);
1885 DATA_BLOB outbuf;
1886 NTSTATUS status;
1888 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1889 TALLOC_FREE(subreq);
1890 if (tevent_req_nterror(req, status)) {
1891 return;
1895 * We have to dup the memory here because outbuf.data is not
1896 * returned as a talloc object by smb2cli_query_info_recv.
1897 * It's a pointer into the received buffer.
1899 state->outbuf = data_blob_dup_talloc(state, outbuf);
1901 if ((outbuf.length != 0) &&
1902 tevent_req_nomem(state->outbuf.data, req)) {
1903 return;
1905 tevent_req_done(req);
1908 NTSTATUS cli_smb2_query_info_fnum_recv(
1909 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1911 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1912 req, struct cli_smb2_query_info_fnum_state);
1913 NTSTATUS status;
1915 if (tevent_req_is_nterror(req, &status)) {
1916 return status;
1918 *outbuf = (DATA_BLOB) {
1919 .data = talloc_move(mem_ctx, &state->outbuf.data),
1920 .length = state->outbuf.length,
1922 tevent_req_received(req);
1923 return NT_STATUS_OK;
1926 NTSTATUS cli_smb2_query_info_fnum(
1927 struct cli_state *cli,
1928 uint16_t fnum,
1929 uint8_t in_info_type,
1930 uint8_t in_info_class,
1931 uint32_t in_max_output_length,
1932 const DATA_BLOB *in_input_buffer,
1933 uint32_t in_additional_info,
1934 uint32_t in_flags,
1935 TALLOC_CTX *mem_ctx,
1936 DATA_BLOB *outbuf)
1938 TALLOC_CTX *frame = talloc_stackframe();
1939 struct tevent_context *ev = NULL;
1940 struct tevent_req *req = NULL;
1941 NTSTATUS status = NT_STATUS_NO_MEMORY;
1942 bool ok;
1944 if (smbXcli_conn_has_async_calls(cli->conn)) {
1946 * Can't use sync call while an async call is in flight
1948 status = NT_STATUS_INVALID_PARAMETER;
1949 goto fail;
1951 ev = samba_tevent_context_init(frame);
1952 if (ev == NULL) {
1953 goto fail;
1955 req = cli_smb2_query_info_fnum_send(
1956 frame,
1958 cli,
1959 fnum,
1960 in_info_type,
1961 in_info_class,
1962 in_max_output_length,
1963 in_input_buffer,
1964 in_additional_info,
1965 in_flags);
1966 if (req == NULL) {
1967 goto fail;
1969 ok = tevent_req_poll_ntstatus(req, ev, &status);
1970 if (!ok) {
1971 goto fail;
1973 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1974 fail:
1975 TALLOC_FREE(frame);
1976 return status;
1979 /***************************************************************
1980 Helper function for pathname operations.
1981 ***************************************************************/
1983 struct get_fnum_from_path_state {
1984 struct tevent_context *ev;
1985 struct cli_state *cli;
1986 const char *name;
1987 uint32_t desired_access;
1988 uint16_t fnum;
1991 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1992 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1993 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1995 static struct tevent_req *get_fnum_from_path_send(
1996 TALLOC_CTX *mem_ctx,
1997 struct tevent_context *ev,
1998 struct cli_state *cli,
1999 const char *name,
2000 uint32_t desired_access)
2002 struct tevent_req *req = NULL, *subreq = NULL;
2003 struct get_fnum_from_path_state *state = NULL;
2004 size_t namelen = strlen(name);
2006 req = tevent_req_create(
2007 mem_ctx, &state, struct get_fnum_from_path_state);
2008 if (req == NULL) {
2009 return NULL;
2011 state->ev = ev;
2012 state->cli = cli;
2013 state->name = name;
2014 state->desired_access = desired_access;
2017 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2018 * '\'
2020 if (namelen > 0 && name[namelen-1] == '\\') {
2021 state->name = talloc_strndup(state, name, namelen-1);
2022 if (tevent_req_nomem(state->name, req)) {
2023 return tevent_req_post(req, ev);
2027 subreq = cli_smb2_create_fnum_send(
2028 state, /* mem_ctx, */
2029 ev, /* ev */
2030 cli, /* cli */
2031 state->name, /* fname */
2032 (struct cli_smb2_create_flags){0}, /* create_flags */
2033 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2034 desired_access, /* desired_access */
2035 0, /* file_attributes */
2036 FILE_SHARE_READ|
2037 FILE_SHARE_WRITE|
2038 FILE_SHARE_DELETE, /* share_access */
2039 FILE_OPEN, /* create_disposition */
2040 0, /* create_options */
2041 NULL); /* in_cblobs */
2042 if (tevent_req_nomem(subreq, req)) {
2043 return tevent_req_post(req, ev);
2045 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2046 return req;
2049 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2051 struct tevent_req *req = tevent_req_callback_data(
2052 subreq, struct tevent_req);
2053 struct get_fnum_from_path_state *state = tevent_req_data(
2054 req, struct get_fnum_from_path_state);
2055 NTSTATUS status;
2057 status = cli_smb2_create_fnum_recv(
2058 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2059 TALLOC_FREE(subreq);
2061 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2062 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2064 * Naive option to match our SMB1 code. Assume the
2065 * symlink path that tripped us up was the last
2066 * component and try again. Eventually we will have to
2067 * deal with the returned path unprocessed component. JRA.
2069 subreq = cli_smb2_create_fnum_send(
2070 state, /* mem_ctx, */
2071 state->ev, /* ev */
2072 state->cli, /* cli */
2073 state->name, /* fname */
2074 (struct cli_smb2_create_flags){0}, /* create_flags */
2075 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2076 state->desired_access, /* desired_access */
2077 0, /* file_attributes */
2078 FILE_SHARE_READ|
2079 FILE_SHARE_WRITE|
2080 FILE_SHARE_DELETE, /* share_access */
2081 FILE_OPEN, /* create_disposition */
2082 FILE_OPEN_REPARSE_POINT, /* create_options */
2083 NULL); /* in_cblobs */
2084 if (tevent_req_nomem(subreq, req)) {
2085 return;
2087 tevent_req_set_callback(
2088 subreq, get_fnum_from_path_opened_reparse, req);
2089 return;
2092 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2093 subreq = cli_smb2_create_fnum_send(
2094 state, /* mem_ctx, */
2095 state->ev, /* ev */
2096 state->cli, /* cli */
2097 state->name, /* fname */
2098 (struct cli_smb2_create_flags){0}, /* create_flags */
2099 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2100 state->desired_access, /* desired_access */
2101 0, /* file_attributes */
2102 FILE_SHARE_READ|
2103 FILE_SHARE_WRITE|
2104 FILE_SHARE_DELETE, /* share_access */
2105 FILE_OPEN, /* create_disposition */
2106 FILE_DIRECTORY_FILE, /* create_options */
2107 NULL); /* in_cblobs */
2108 if (tevent_req_nomem(subreq, req)) {
2109 return;
2111 tevent_req_set_callback(
2112 subreq, get_fnum_from_path_opened_dir, req);
2113 return;
2116 if (tevent_req_nterror(req, status)) {
2117 return;
2119 tevent_req_done(req);
2122 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2124 struct tevent_req *req = tevent_req_callback_data(
2125 subreq, struct tevent_req);
2126 struct get_fnum_from_path_state *state = tevent_req_data(
2127 req, struct get_fnum_from_path_state);
2128 NTSTATUS status = cli_smb2_create_fnum_recv(
2129 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2130 tevent_req_simple_finish_ntstatus(subreq, status);
2133 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2135 /* Abstraction violation, but these two are just the same... */
2136 get_fnum_from_path_opened_reparse(subreq);
2139 static NTSTATUS get_fnum_from_path_recv(
2140 struct tevent_req *req, uint16_t *pfnum)
2142 struct get_fnum_from_path_state *state = tevent_req_data(
2143 req, struct get_fnum_from_path_state);
2144 NTSTATUS status = NT_STATUS_OK;
2146 if (!tevent_req_is_nterror(req, &status)) {
2147 *pfnum = state->fnum;
2149 tevent_req_received(req);
2150 return status;
2153 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2154 const char *name,
2155 uint32_t desired_access,
2156 uint16_t *pfnum)
2158 TALLOC_CTX *frame = talloc_stackframe();
2159 struct tevent_context *ev = NULL;
2160 struct tevent_req *req = NULL;
2161 NTSTATUS status = NT_STATUS_NO_MEMORY;
2163 if (smbXcli_conn_has_async_calls(cli->conn)) {
2164 status = NT_STATUS_INVALID_PARAMETER;
2165 goto fail;
2167 ev = samba_tevent_context_init(frame);
2168 if (ev == NULL) {
2169 goto fail;
2171 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2172 if (req == NULL) {
2173 goto fail;
2175 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2176 goto fail;
2178 status = get_fnum_from_path_recv(req, pfnum);
2179 fail:
2180 TALLOC_FREE(frame);
2181 return status;
2184 struct cli_smb2_qpathinfo_state {
2185 struct tevent_context *ev;
2186 struct cli_state *cli;
2187 const char *fname;
2188 uint16_t fnum;
2189 uint16_t level;
2190 uint32_t min_rdata;
2191 uint32_t max_rdata;
2193 NTSTATUS status;
2194 DATA_BLOB out;
2197 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2198 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2199 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2201 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2202 struct tevent_context *ev,
2203 struct cli_state *cli,
2204 const char *fname,
2205 uint16_t level,
2206 uint32_t min_rdata,
2207 uint32_t max_rdata)
2209 struct tevent_req *req = NULL, *subreq = NULL;
2210 struct cli_smb2_qpathinfo_state *state = NULL;
2212 req = tevent_req_create(mem_ctx,
2213 &state,
2214 struct cli_smb2_qpathinfo_state);
2215 if (req == NULL) {
2216 return NULL;
2218 state->ev = ev;
2219 state->cli = cli;
2220 state->level = level;
2221 state->min_rdata = min_rdata;
2222 state->max_rdata = max_rdata;
2224 subreq = get_fnum_from_path_send(state,
2226 cli,
2227 fname,
2228 FILE_READ_ATTRIBUTES);
2229 if (tevent_req_nomem(subreq, req)) {
2230 return tevent_req_post(req, ev);
2232 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2233 return req;
2236 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2238 struct tevent_req *req =
2239 tevent_req_callback_data(subreq, struct tevent_req);
2240 struct cli_smb2_qpathinfo_state *state =
2241 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2242 NTSTATUS status;
2244 status = get_fnum_from_path_recv(subreq, &state->fnum);
2245 TALLOC_FREE(subreq);
2246 if (tevent_req_nterror(req, status)) {
2247 return;
2250 subreq = cli_smb2_query_info_fnum_send(state,
2251 state->ev,
2252 state->cli,
2253 state->fnum,
2254 1, /* in_info_type */
2255 state->level,
2256 state->max_rdata,
2257 NULL, /* in_input_buffer */
2258 0, /* in_additional_info */
2259 0); /* in_flags */
2260 if (tevent_req_nomem(subreq, req)) {
2261 return;
2263 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2266 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2268 struct tevent_req *req =
2269 tevent_req_callback_data(subreq, struct tevent_req);
2270 struct cli_smb2_qpathinfo_state *state =
2271 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2273 state->status =
2274 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2275 TALLOC_FREE(subreq);
2277 if (NT_STATUS_IS_OK(state->status) &&
2278 (state->out.length < state->min_rdata)) {
2279 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2282 subreq = cli_smb2_close_fnum_send(state,
2283 state->ev,
2284 state->cli,
2285 state->fnum,
2287 if (tevent_req_nomem(subreq, req)) {
2288 return;
2290 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2293 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2295 struct tevent_req *req =
2296 tevent_req_callback_data(subreq, struct tevent_req);
2297 struct cli_smb2_qpathinfo_state *state =
2298 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2299 NTSTATUS status;
2301 status = cli_smb2_close_fnum_recv(subreq);
2302 TALLOC_FREE(subreq);
2303 if (tevent_req_nterror(req, status)) {
2304 return;
2306 if (tevent_req_nterror(req, state->status)) {
2307 return;
2309 tevent_req_done(req);
2312 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2313 TALLOC_CTX *mem_ctx,
2314 uint8_t **rdata,
2315 uint32_t *num_rdata)
2317 struct cli_smb2_qpathinfo_state *state =
2318 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2319 NTSTATUS status;
2321 if (tevent_req_is_nterror(req, &status)) {
2322 return status;
2325 *rdata = talloc_move(mem_ctx, &state->out.data);
2326 *num_rdata = state->out.length;
2327 tevent_req_received(req);
2328 return NT_STATUS_OK;
2331 /***************************************************************
2332 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2333 a pathname.
2334 Synchronous only.
2335 ***************************************************************/
2337 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2338 const char *name,
2339 uint8_t in_info_type,
2340 uint8_t in_file_info_class,
2341 const DATA_BLOB *p_in_data)
2343 NTSTATUS status;
2344 uint16_t fnum = 0xffff;
2345 TALLOC_CTX *frame = talloc_stackframe();
2347 if (smbXcli_conn_has_async_calls(cli->conn)) {
2349 * Can't use sync call while an async call is in flight
2351 status = NT_STATUS_INVALID_PARAMETER;
2352 goto fail;
2355 status = get_fnum_from_path(cli,
2356 name,
2357 FILE_WRITE_ATTRIBUTES,
2358 &fnum);
2360 if (!NT_STATUS_IS_OK(status)) {
2361 goto fail;
2364 status = cli_smb2_set_info_fnum(
2365 cli,
2366 fnum,
2367 in_info_type,
2368 in_file_info_class,
2369 p_in_data, /* in_input_buffer */
2370 0); /* in_additional_info */
2371 fail:
2373 if (fnum != 0xffff) {
2374 cli_smb2_close_fnum(cli, fnum);
2377 TALLOC_FREE(frame);
2378 return status;
2382 /***************************************************************
2383 Wrapper that allows SMB2 to set pathname attributes.
2384 Synchronous only.
2385 ***************************************************************/
2387 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2388 const char *name,
2389 uint32_t attr,
2390 time_t mtime)
2392 uint8_t inbuf_store[40];
2393 DATA_BLOB inbuf = data_blob_null;
2395 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2396 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2398 inbuf.data = inbuf_store;
2399 inbuf.length = sizeof(inbuf_store);
2400 data_blob_clear(&inbuf);
2403 * SMB1 uses attr == 0 to clear all attributes
2404 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2405 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2406 * request attribute change.
2408 * SMB2 uses exactly the reverse. Unfortunately as the
2409 * cli_setatr() ABI is exposed inside libsmbclient,
2410 * we must make the SMB2 cli_smb2_setatr() call
2411 * export the same ABI as the SMB1 cli_setatr()
2412 * which calls it. This means reversing the sense
2413 * of the requested attr argument if it's zero
2414 * or FILE_ATTRIBUTE_NORMAL.
2416 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2419 if (attr == 0) {
2420 attr = FILE_ATTRIBUTE_NORMAL;
2421 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2422 attr = 0;
2425 SIVAL(inbuf.data, 32, attr);
2426 if (mtime != 0) {
2427 put_long_date((char *)inbuf.data + 16,mtime);
2429 /* Set all the other times to -1. */
2430 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2431 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2432 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2434 return cli_smb2_setpathinfo(
2435 cli,
2436 name,
2437 SMB2_0_INFO_FILE, /* in_info_type */
2438 FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2439 &inbuf);
2443 /***************************************************************
2444 Wrapper that allows SMB2 to set file handle times.
2445 Synchronous only.
2446 ***************************************************************/
2448 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2449 uint16_t fnum,
2450 time_t change_time,
2451 time_t access_time,
2452 time_t write_time)
2454 uint8_t inbuf_store[40];
2455 DATA_BLOB inbuf = data_blob_null;
2456 NTSTATUS status;
2458 if (smbXcli_conn_has_async_calls(cli->conn)) {
2460 * Can't use sync call while an async call is in flight
2462 return NT_STATUS_INVALID_PARAMETER;
2465 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2466 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2468 inbuf.data = inbuf_store;
2469 inbuf.length = sizeof(inbuf_store);
2470 data_blob_clear(&inbuf);
2472 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2473 if (change_time != 0) {
2474 put_long_date((char *)inbuf.data + 24, change_time);
2476 if (access_time != 0) {
2477 put_long_date((char *)inbuf.data + 8, access_time);
2479 if (write_time != 0) {
2480 put_long_date((char *)inbuf.data + 16, write_time);
2483 status = cli_smb2_set_info_fnum(
2484 cli,
2485 fnum,
2486 SMB2_0_INFO_FILE, /* in_info_type */
2487 FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2488 &inbuf, /* in_input_buffer */
2489 0); /* in_additional_info */
2490 return status;
2493 /***************************************************************
2494 Wrapper that allows SMB2 to query disk attributes (size).
2495 Synchronous only.
2496 ***************************************************************/
2498 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2499 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2501 NTSTATUS status;
2502 uint16_t fnum = 0xffff;
2503 DATA_BLOB outbuf = data_blob_null;
2504 uint32_t sectors_per_unit = 0;
2505 uint32_t bytes_per_sector = 0;
2506 uint64_t total_size = 0;
2507 uint64_t size_free = 0;
2508 TALLOC_CTX *frame = talloc_stackframe();
2510 if (smbXcli_conn_has_async_calls(cli->conn)) {
2512 * Can't use sync call while an async call is in flight
2514 status = NT_STATUS_INVALID_PARAMETER;
2515 goto fail;
2518 /* First open the top level directory. */
2519 status = cli_smb2_create_fnum(cli,
2520 path,
2521 (struct cli_smb2_create_flags){0},
2522 SMB2_IMPERSONATION_IMPERSONATION,
2523 FILE_READ_ATTRIBUTES, /* desired_access */
2524 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2525 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2526 FILE_OPEN, /* create_disposition */
2527 FILE_DIRECTORY_FILE, /* create_options */
2528 NULL,
2529 &fnum,
2530 NULL,
2531 NULL,
2532 NULL);
2534 if (!NT_STATUS_IS_OK(status)) {
2535 goto fail;
2538 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2539 level 3 (SMB_FS_SIZE_INFORMATION). */
2541 status = cli_smb2_query_info_fnum(
2542 cli,
2543 fnum,
2544 2, /* in_info_type */
2545 3, /* in_file_info_class */
2546 0xFFFF, /* in_max_output_length */
2547 NULL, /* in_input_buffer */
2548 0, /* in_additional_info */
2549 0, /* in_flags */
2550 frame,
2551 &outbuf);
2552 if (!NT_STATUS_IS_OK(status)) {
2553 goto fail;
2556 /* Parse the reply. */
2557 if (outbuf.length != 24) {
2558 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2559 goto fail;
2562 total_size = BVAL(outbuf.data, 0);
2563 size_free = BVAL(outbuf.data, 8);
2564 sectors_per_unit = IVAL(outbuf.data, 16);
2565 bytes_per_sector = IVAL(outbuf.data, 20);
2567 if (bsize) {
2568 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2570 if (total) {
2571 *total = total_size;
2573 if (avail) {
2574 *avail = size_free;
2577 status = NT_STATUS_OK;
2579 fail:
2581 if (fnum != 0xffff) {
2582 cli_smb2_close_fnum(cli, fnum);
2585 TALLOC_FREE(frame);
2586 return status;
2589 /***************************************************************
2590 Wrapper that allows SMB2 to query file system sizes.
2591 Synchronous only.
2592 ***************************************************************/
2594 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2595 uint64_t *total_allocation_units,
2596 uint64_t *caller_allocation_units,
2597 uint64_t *actual_allocation_units,
2598 uint64_t *sectors_per_allocation_unit,
2599 uint64_t *bytes_per_sector)
2601 NTSTATUS status;
2602 uint16_t fnum = 0xffff;
2603 DATA_BLOB outbuf = data_blob_null;
2604 TALLOC_CTX *frame = talloc_stackframe();
2606 if (smbXcli_conn_has_async_calls(cli->conn)) {
2608 * Can't use sync call while an async call is in flight
2610 status = NT_STATUS_INVALID_PARAMETER;
2611 goto fail;
2614 /* First open the top level directory. */
2615 status =
2616 cli_smb2_create_fnum(cli, "",
2617 (struct cli_smb2_create_flags){0},
2618 SMB2_IMPERSONATION_IMPERSONATION,
2619 FILE_READ_ATTRIBUTES, /* desired_access */
2620 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2621 FILE_SHARE_READ | FILE_SHARE_WRITE |
2622 FILE_SHARE_DELETE, /* share_access */
2623 FILE_OPEN, /* create_disposition */
2624 FILE_DIRECTORY_FILE, /* create_options */
2625 NULL,
2626 &fnum,
2627 NULL,
2628 NULL,
2629 NULL);
2631 if (!NT_STATUS_IS_OK(status)) {
2632 goto fail;
2635 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2636 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2638 status = cli_smb2_query_info_fnum(
2639 cli,
2640 fnum,
2641 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2642 FSCC_FS_FULL_SIZE_INFORMATION, /* in_file_info_class */
2643 0xFFFF, /* in_max_output_length */
2644 NULL, /* in_input_buffer */
2645 0, /* in_additional_info */
2646 0, /* in_flags */
2647 frame,
2648 &outbuf);
2649 if (!NT_STATUS_IS_OK(status)) {
2650 goto fail;
2653 if (outbuf.length < 32) {
2654 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2655 goto fail;
2658 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2659 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2660 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2661 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2662 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2664 fail:
2666 if (fnum != 0xffff) {
2667 cli_smb2_close_fnum(cli, fnum);
2670 TALLOC_FREE(frame);
2671 return status;
2674 /***************************************************************
2675 Wrapper that allows SMB2 to query file system attributes.
2676 Synchronous only.
2677 ***************************************************************/
2679 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2681 NTSTATUS status;
2682 uint16_t fnum = 0xffff;
2683 DATA_BLOB outbuf = data_blob_null;
2684 TALLOC_CTX *frame = talloc_stackframe();
2686 if (smbXcli_conn_has_async_calls(cli->conn)) {
2688 * Can't use sync call while an async call is in flight
2690 status = NT_STATUS_INVALID_PARAMETER;
2691 goto fail;
2694 /* First open the top level directory. */
2695 status =
2696 cli_smb2_create_fnum(cli, "",
2697 (struct cli_smb2_create_flags){0},
2698 SMB2_IMPERSONATION_IMPERSONATION,
2699 FILE_READ_ATTRIBUTES, /* desired_access */
2700 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2701 FILE_SHARE_READ | FILE_SHARE_WRITE |
2702 FILE_SHARE_DELETE, /* share_access */
2703 FILE_OPEN, /* create_disposition */
2704 FILE_DIRECTORY_FILE, /* create_options */
2705 NULL,
2706 &fnum,
2707 NULL,
2708 NULL,
2709 NULL);
2711 if (!NT_STATUS_IS_OK(status)) {
2712 goto fail;
2715 status = cli_smb2_query_info_fnum(
2716 cli,
2717 fnum,
2718 2, /* in_info_type */
2719 5, /* in_file_info_class */
2720 0xFFFF, /* in_max_output_length */
2721 NULL, /* in_input_buffer */
2722 0, /* in_additional_info */
2723 0, /* in_flags */
2724 frame,
2725 &outbuf);
2726 if (!NT_STATUS_IS_OK(status)) {
2727 goto fail;
2730 if (outbuf.length < 12) {
2731 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2732 goto fail;
2735 *fs_attr = IVAL(outbuf.data, 0);
2737 fail:
2739 if (fnum != 0xffff) {
2740 cli_smb2_close_fnum(cli, fnum);
2743 TALLOC_FREE(frame);
2744 return status;
2747 /***************************************************************
2748 Wrapper that allows SMB2 to query file system volume info.
2749 Synchronous only.
2750 ***************************************************************/
2752 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2753 TALLOC_CTX *mem_ctx,
2754 char **_volume_name,
2755 uint32_t *pserial_number,
2756 time_t *pdate)
2758 NTSTATUS status;
2759 uint16_t fnum = 0xffff;
2760 DATA_BLOB outbuf = data_blob_null;
2761 uint32_t nlen;
2762 char *volume_name = NULL;
2763 TALLOC_CTX *frame = talloc_stackframe();
2765 if (smbXcli_conn_has_async_calls(cli->conn)) {
2767 * Can't use sync call while an async call is in flight
2769 status = NT_STATUS_INVALID_PARAMETER;
2770 goto fail;
2773 /* First open the top level directory. */
2774 status =
2775 cli_smb2_create_fnum(cli, "",
2776 (struct cli_smb2_create_flags){0},
2777 SMB2_IMPERSONATION_IMPERSONATION,
2778 FILE_READ_ATTRIBUTES, /* desired_access */
2779 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2780 FILE_SHARE_READ | FILE_SHARE_WRITE |
2781 FILE_SHARE_DELETE, /* share_access */
2782 FILE_OPEN, /* create_disposition */
2783 FILE_DIRECTORY_FILE, /* create_options */
2784 NULL,
2785 &fnum,
2786 NULL,
2787 NULL,
2788 NULL);
2790 if (!NT_STATUS_IS_OK(status)) {
2791 goto fail;
2794 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2795 level 1 (SMB_FS_VOLUME_INFORMATION). */
2797 status = cli_smb2_query_info_fnum(
2798 cli,
2799 fnum,
2800 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2801 /* in_file_info_class */
2802 FSCC_FS_VOLUME_INFORMATION,
2803 0xFFFF, /* in_max_output_length */
2804 NULL, /* in_input_buffer */
2805 0, /* in_additional_info */
2806 0, /* in_flags */
2807 frame,
2808 &outbuf);
2809 if (!NT_STATUS_IS_OK(status)) {
2810 goto fail;
2813 if (outbuf.length < 24) {
2814 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2815 goto fail;
2818 if (pdate) {
2819 struct timespec ts;
2820 ts = interpret_long_date(BVAL(outbuf.data, 0));
2821 *pdate = ts.tv_sec;
2823 if (pserial_number) {
2824 *pserial_number = IVAL(outbuf.data,8);
2826 nlen = IVAL(outbuf.data,12);
2827 if (nlen + 18 < 18) {
2828 /* Integer wrap. */
2829 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2830 goto fail;
2833 * The next check is safe as we know outbuf.length >= 24
2834 * from above.
2836 if (nlen > (outbuf.length - 18)) {
2837 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2838 goto fail;
2841 pull_string_talloc(mem_ctx,
2842 (const char *)outbuf.data,
2844 &volume_name,
2845 outbuf.data + 18,
2846 nlen,
2847 STR_UNICODE);
2848 if (volume_name == NULL) {
2849 status = map_nt_error_from_unix(errno);
2850 goto fail;
2853 *_volume_name = volume_name;
2855 fail:
2857 if (fnum != 0xffff) {
2858 cli_smb2_close_fnum(cli, fnum);
2861 TALLOC_FREE(frame);
2862 return status;
2865 struct cli_smb2_mxac_state {
2866 struct tevent_context *ev;
2867 struct cli_state *cli;
2868 const char *fname;
2869 struct smb2_create_blobs in_cblobs;
2870 uint16_t fnum;
2871 NTSTATUS status;
2872 uint32_t mxac;
2875 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2876 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2878 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2879 struct tevent_context *ev,
2880 struct cli_state *cli,
2881 const char *fname)
2883 struct tevent_req *req = NULL, *subreq = NULL;
2884 struct cli_smb2_mxac_state *state = NULL;
2885 NTSTATUS status;
2887 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2888 if (req == NULL) {
2889 return NULL;
2891 *state = (struct cli_smb2_mxac_state) {
2892 .ev = ev,
2893 .cli = cli,
2894 .fname = fname,
2897 status = smb2_create_blob_add(state,
2898 &state->in_cblobs,
2899 SMB2_CREATE_TAG_MXAC,
2900 data_blob(NULL, 0));
2901 if (tevent_req_nterror(req, status)) {
2902 return tevent_req_post(req, ev);
2905 subreq = cli_smb2_create_fnum_send(
2906 state,
2907 state->ev,
2908 state->cli,
2909 state->fname,
2910 (struct cli_smb2_create_flags){0},
2911 SMB2_IMPERSONATION_IMPERSONATION,
2912 FILE_READ_ATTRIBUTES,
2913 0, /* file attributes */
2914 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2915 FILE_OPEN,
2916 0, /* create_options */
2917 &state->in_cblobs);
2918 if (tevent_req_nomem(subreq, req)) {
2919 return tevent_req_post(req, ev);
2921 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2922 return req;
2925 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2927 struct tevent_req *req = tevent_req_callback_data(
2928 subreq, struct tevent_req);
2929 struct cli_smb2_mxac_state *state = tevent_req_data(
2930 req, struct cli_smb2_mxac_state);
2931 struct smb2_create_blobs out_cblobs = {0};
2932 struct smb2_create_blob *mxac_blob = NULL;
2933 NTSTATUS status;
2935 status = cli_smb2_create_fnum_recv(
2936 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
2937 TALLOC_FREE(subreq);
2939 if (tevent_req_nterror(req, status)) {
2940 return;
2943 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
2944 if (mxac_blob == NULL) {
2945 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2946 goto close;
2948 if (mxac_blob->data.length != 8) {
2949 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2950 goto close;
2953 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
2954 state->mxac = IVAL(mxac_blob->data.data, 4);
2956 close:
2957 subreq = cli_smb2_close_fnum_send(state,
2958 state->ev,
2959 state->cli,
2960 state->fnum,
2962 if (tevent_req_nomem(subreq, req)) {
2963 return;
2965 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
2967 return;
2970 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
2972 struct tevent_req *req = tevent_req_callback_data(
2973 subreq, struct tevent_req);
2974 NTSTATUS status;
2976 status = cli_smb2_close_fnum_recv(subreq);
2977 if (tevent_req_nterror(req, status)) {
2978 return;
2981 tevent_req_done(req);
2984 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
2986 struct cli_smb2_mxac_state *state = tevent_req_data(
2987 req, struct cli_smb2_mxac_state);
2988 NTSTATUS status;
2990 if (tevent_req_is_nterror(req, &status)) {
2991 return status;
2994 if (!NT_STATUS_IS_OK(state->status)) {
2995 return state->status;
2998 *mxac = state->mxac;
2999 return NT_STATUS_OK;
3002 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3003 const char *fname,
3004 uint32_t *_mxac)
3006 TALLOC_CTX *frame = talloc_stackframe();
3007 struct tevent_context *ev = NULL;
3008 struct tevent_req *req = NULL;
3009 NTSTATUS status = NT_STATUS_NO_MEMORY;
3010 bool ok;
3012 if (smbXcli_conn_has_async_calls(cli->conn)) {
3014 * Can't use sync call while an async call is in flight
3016 status = NT_STATUS_INVALID_PARAMETER;
3017 goto fail;
3020 ev = samba_tevent_context_init(frame);
3021 if (ev == NULL) {
3022 goto fail;
3024 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3025 if (req == NULL) {
3026 goto fail;
3028 ok = tevent_req_poll_ntstatus(req, ev, &status);
3029 if (!ok) {
3030 goto fail;
3032 status = cli_smb2_query_mxac_recv(req, _mxac);
3034 fail:
3035 TALLOC_FREE(frame);
3036 return status;
3039 struct cli_smb2_rename_fnum_state {
3040 DATA_BLOB inbuf;
3043 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3045 static struct tevent_req *cli_smb2_rename_fnum_send(
3046 TALLOC_CTX *mem_ctx,
3047 struct tevent_context *ev,
3048 struct cli_state *cli,
3049 uint16_t fnum,
3050 const char *fname_dst,
3051 bool replace)
3053 struct tevent_req *req = NULL, *subreq = NULL;
3054 struct cli_smb2_rename_fnum_state *state = NULL;
3055 size_t namelen = strlen(fname_dst);
3056 smb_ucs2_t *converted_str = NULL;
3057 size_t converted_size_bytes = 0;
3058 size_t inbuf_size;
3059 bool ok;
3061 req = tevent_req_create(
3062 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3063 if (req == NULL) {
3064 return NULL;
3068 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3069 * a '\'
3071 if (*fname_dst == '\\') {
3072 fname_dst++;
3076 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3077 * '\'
3079 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3080 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3081 if (tevent_req_nomem(fname_dst, req)) {
3082 return tevent_req_post(req, ev);
3086 ok = push_ucs2_talloc(
3087 state, &converted_str, fname_dst, &converted_size_bytes);
3088 if (!ok) {
3089 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3090 return tevent_req_post(req, ev);
3094 * W2K8 insists the dest name is not null terminated. Remove
3095 * the last 2 zero bytes and reduce the name length.
3097 if (converted_size_bytes < 2) {
3098 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3099 return tevent_req_post(req, ev);
3101 converted_size_bytes -= 2;
3103 inbuf_size = 20 + converted_size_bytes;
3104 if (inbuf_size < 20) {
3105 /* Integer wrap check. */
3106 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3107 return tevent_req_post(req, ev);
3111 * The Windows 10 SMB2 server has a minimum length
3112 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3113 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3114 * if the length is less. This isn't an alignment
3115 * issue as Windows client accepts happily 2-byte align
3116 * for larger target name sizes. Also the Windows 10
3117 * SMB1 server doesn't have this restriction.
3119 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3121 inbuf_size = MAX(inbuf_size, 24);
3123 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3124 if (tevent_req_nomem(state->inbuf.data, req)) {
3125 return tevent_req_post(req, ev);
3128 if (replace) {
3129 SCVAL(state->inbuf.data, 0, 1);
3132 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3133 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3135 TALLOC_FREE(converted_str);
3137 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3138 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3140 subreq = cli_smb2_set_info_fnum_send(
3141 state, /* mem_ctx */
3142 ev, /* ev */
3143 cli, /* cli */
3144 fnum, /* fnum */
3145 SMB2_0_INFO_FILE, /* in_info_type */
3146 FSCC_FILE_RENAME_INFORMATION, /* in_file_info_class */
3147 &state->inbuf, /* in_input_buffer */
3148 0); /* in_additional_info */
3149 if (tevent_req_nomem(subreq, req)) {
3150 return tevent_req_post(req, ev);
3152 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3153 return req;
3156 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3158 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3159 tevent_req_simple_finish_ntstatus(subreq, status);
3162 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3164 return tevent_req_simple_recv_ntstatus(req);
3167 /***************************************************************
3168 Wrapper that allows SMB2 to rename a file.
3169 ***************************************************************/
3171 struct cli_smb2_rename_state {
3172 struct tevent_context *ev;
3173 struct cli_state *cli;
3174 const char *fname_dst;
3175 bool replace;
3176 uint16_t fnum;
3178 NTSTATUS rename_status;
3181 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3182 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3183 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3185 struct tevent_req *cli_smb2_rename_send(
3186 TALLOC_CTX *mem_ctx,
3187 struct tevent_context *ev,
3188 struct cli_state *cli,
3189 const char *fname_src,
3190 const char *fname_dst,
3191 bool replace)
3193 struct tevent_req *req = NULL, *subreq = NULL;
3194 struct cli_smb2_rename_state *state = NULL;
3195 NTSTATUS status;
3197 req = tevent_req_create(
3198 mem_ctx, &state, struct cli_smb2_rename_state);
3199 if (req == NULL) {
3200 return NULL;
3204 * Strip a MSDFS path from fname_dst if we were given one.
3206 status = cli_dfs_target_check(state,
3207 cli,
3208 fname_dst,
3209 &fname_dst);
3210 if (tevent_req_nterror(req, status)) {
3211 return tevent_req_post(req, ev);
3214 state->ev = ev;
3215 state->cli = cli;
3216 state->fname_dst = fname_dst;
3217 state->replace = replace;
3219 subreq = get_fnum_from_path_send(
3220 state, ev, cli, fname_src, DELETE_ACCESS);
3221 if (tevent_req_nomem(subreq, req)) {
3222 return tevent_req_post(req, ev);
3224 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3225 return req;
3228 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3230 struct tevent_req *req = tevent_req_callback_data(
3231 subreq, struct tevent_req);
3232 struct cli_smb2_rename_state *state = tevent_req_data(
3233 req, struct cli_smb2_rename_state);
3234 NTSTATUS status;
3236 status = get_fnum_from_path_recv(subreq, &state->fnum);
3237 TALLOC_FREE(subreq);
3238 if (tevent_req_nterror(req, status)) {
3239 return;
3242 subreq = cli_smb2_rename_fnum_send(
3243 state,
3244 state->ev,
3245 state->cli,
3246 state->fnum,
3247 state->fname_dst,
3248 state->replace);
3249 if (tevent_req_nomem(subreq, req)) {
3250 return;
3252 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3255 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3257 struct tevent_req *req = tevent_req_callback_data(
3258 subreq, struct tevent_req);
3259 struct cli_smb2_rename_state *state = tevent_req_data(
3260 req, struct cli_smb2_rename_state);
3262 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3263 TALLOC_FREE(subreq);
3265 subreq = cli_smb2_close_fnum_send(state,
3266 state->ev,
3267 state->cli,
3268 state->fnum,
3270 if (tevent_req_nomem(subreq, req)) {
3271 return;
3273 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3276 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3278 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3279 tevent_req_simple_finish_ntstatus(subreq, status);
3282 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3284 struct cli_smb2_rename_state *state = tevent_req_data(
3285 req, struct cli_smb2_rename_state);
3286 NTSTATUS status = NT_STATUS_OK;
3288 if (!tevent_req_is_nterror(req, &status)) {
3289 status = state->rename_status;
3291 tevent_req_received(req);
3292 return status;
3295 /***************************************************************
3296 Wrapper that allows SMB2 to set an EA on a fnum.
3297 Synchronous only.
3298 ***************************************************************/
3300 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3301 uint16_t fnum,
3302 const char *ea_name,
3303 const char *ea_val,
3304 size_t ea_len)
3306 NTSTATUS status;
3307 DATA_BLOB inbuf = data_blob_null;
3308 size_t bloblen = 0;
3309 char *ea_name_ascii = NULL;
3310 size_t namelen = 0;
3311 TALLOC_CTX *frame = talloc_stackframe();
3313 if (smbXcli_conn_has_async_calls(cli->conn)) {
3315 * Can't use sync call while an async call is in flight
3317 status = NT_STATUS_INVALID_PARAMETER;
3318 goto fail;
3321 /* Marshall the SMB2 EA data. */
3322 if (ea_len > 0xFFFF) {
3323 status = NT_STATUS_INVALID_PARAMETER;
3324 goto fail;
3327 if (!push_ascii_talloc(frame,
3328 &ea_name_ascii,
3329 ea_name,
3330 &namelen)) {
3331 status = NT_STATUS_INVALID_PARAMETER;
3332 goto fail;
3335 if (namelen < 2 || namelen > 0xFF) {
3336 status = NT_STATUS_INVALID_PARAMETER;
3337 goto fail;
3340 bloblen = 8 + ea_len + namelen;
3341 /* Round up to a 4 byte boundary. */
3342 bloblen = ((bloblen + 3)&~3);
3344 inbuf = data_blob_talloc_zero(frame, bloblen);
3345 if (inbuf.data == NULL) {
3346 status = NT_STATUS_NO_MEMORY;
3347 goto fail;
3349 /* namelen doesn't include the NULL byte. */
3350 SCVAL(inbuf.data, 5, namelen - 1);
3351 SSVAL(inbuf.data, 6, ea_len);
3352 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3353 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3355 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3356 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3358 status = cli_smb2_set_info_fnum(
3359 cli,
3360 fnum,
3361 SMB2_0_INFO_FILE, /* in_info_type */
3362 FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3363 &inbuf, /* in_input_buffer */
3364 0); /* in_additional_info */
3366 fail:
3367 TALLOC_FREE(frame);
3368 return status;
3371 /***************************************************************
3372 Wrapper that allows SMB2 to set an EA on a pathname.
3373 Synchronous only.
3374 ***************************************************************/
3376 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3377 const char *name,
3378 const char *ea_name,
3379 const char *ea_val,
3380 size_t ea_len)
3382 NTSTATUS status;
3383 uint16_t fnum = 0xffff;
3385 if (smbXcli_conn_has_async_calls(cli->conn)) {
3387 * Can't use sync call while an async call is in flight
3389 status = NT_STATUS_INVALID_PARAMETER;
3390 goto fail;
3393 status = get_fnum_from_path(cli,
3394 name,
3395 FILE_WRITE_EA,
3396 &fnum);
3398 if (!NT_STATUS_IS_OK(status)) {
3399 goto fail;
3402 status = cli_set_ea_fnum(cli,
3403 fnum,
3404 ea_name,
3405 ea_val,
3406 ea_len);
3407 if (!NT_STATUS_IS_OK(status)) {
3408 goto fail;
3411 fail:
3413 if (fnum != 0xffff) {
3414 cli_smb2_close_fnum(cli, fnum);
3416 return status;
3419 /***************************************************************
3420 Wrapper that allows SMB2 to get an EA list on a pathname.
3421 Synchronous only.
3422 ***************************************************************/
3424 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3425 const char *name,
3426 TALLOC_CTX *ctx,
3427 size_t *pnum_eas,
3428 struct ea_struct **pea_array)
3430 NTSTATUS status;
3431 uint16_t fnum = 0xffff;
3432 DATA_BLOB outbuf = data_blob_null;
3433 struct ea_list *ea_list = NULL;
3434 struct ea_list *eal = NULL;
3435 size_t ea_count = 0;
3436 TALLOC_CTX *frame = talloc_stackframe();
3438 *pnum_eas = 0;
3439 *pea_array = NULL;
3441 if (smbXcli_conn_has_async_calls(cli->conn)) {
3443 * Can't use sync call while an async call is in flight
3445 status = NT_STATUS_INVALID_PARAMETER;
3446 goto fail;
3449 status = get_fnum_from_path(cli,
3450 name,
3451 FILE_READ_EA,
3452 &fnum);
3454 if (!NT_STATUS_IS_OK(status)) {
3455 goto fail;
3458 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3459 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3461 status = cli_smb2_query_info_fnum(
3462 cli,
3463 fnum,
3464 SMB2_0_INFO_FILE, /* in_info_type */
3465 FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3466 0xFFFF, /* in_max_output_length */
3467 NULL, /* in_input_buffer */
3468 0, /* in_additional_info */
3469 0, /* in_flags */
3470 frame,
3471 &outbuf);
3473 if (!NT_STATUS_IS_OK(status)) {
3474 goto fail;
3477 /* Parse the reply. */
3478 ea_list = read_nttrans_ea_list(ctx,
3479 (const char *)outbuf.data,
3480 outbuf.length);
3481 if (ea_list == NULL) {
3482 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3483 goto fail;
3486 /* Convert to an array. */
3487 for (eal = ea_list; eal; eal = eal->next) {
3488 ea_count++;
3491 if (ea_count) {
3492 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3493 if (*pea_array == NULL) {
3494 status = NT_STATUS_NO_MEMORY;
3495 goto fail;
3497 ea_count = 0;
3498 for (eal = ea_list; eal; eal = eal->next) {
3499 (*pea_array)[ea_count++] = eal->ea;
3501 *pnum_eas = ea_count;
3504 fail:
3506 if (fnum != 0xffff) {
3507 cli_smb2_close_fnum(cli, fnum);
3510 TALLOC_FREE(frame);
3511 return status;
3514 /***************************************************************
3515 Wrapper that allows SMB2 to get user quota.
3516 Synchronous only.
3517 ***************************************************************/
3519 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3520 int quota_fnum,
3521 SMB_NTQUOTA_STRUCT *pqt)
3523 NTSTATUS status;
3524 DATA_BLOB inbuf = data_blob_null;
3525 DATA_BLOB info_blob = data_blob_null;
3526 DATA_BLOB outbuf = data_blob_null;
3527 TALLOC_CTX *frame = talloc_stackframe();
3528 unsigned sid_len;
3529 unsigned int offset;
3530 struct smb2_query_quota_info query = {0};
3531 struct file_get_quota_info info = {0};
3532 enum ndr_err_code err;
3533 struct ndr_push *ndr_push = NULL;
3535 if (smbXcli_conn_has_async_calls(cli->conn)) {
3537 * Can't use sync call while an async call is in flight
3539 status = NT_STATUS_INVALID_PARAMETER;
3540 goto fail;
3543 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3545 query.return_single = 1;
3547 info.next_entry_offset = 0;
3548 info.sid_length = sid_len;
3549 info.sid = pqt->sid;
3551 err = ndr_push_struct_blob(
3552 &info_blob,
3553 frame,
3554 &info,
3555 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3557 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3558 status = NT_STATUS_INTERNAL_ERROR;
3559 goto fail;
3562 query.sid_list_length = info_blob.length;
3563 ndr_push = ndr_push_init_ctx(frame);
3564 if (!ndr_push) {
3565 status = NT_STATUS_NO_MEMORY;
3566 goto fail;
3569 err = ndr_push_smb2_query_quota_info(ndr_push,
3570 NDR_SCALARS | NDR_BUFFERS,
3571 &query);
3573 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3574 status = NT_STATUS_INTERNAL_ERROR;
3575 goto fail;
3578 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3579 info_blob.length);
3581 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3582 status = NT_STATUS_INTERNAL_ERROR;
3583 goto fail;
3585 inbuf.data = ndr_push->data;
3586 inbuf.length = ndr_push->offset;
3588 status = cli_smb2_query_info_fnum(
3589 cli,
3590 quota_fnum,
3591 4, /* in_info_type */
3592 0, /* in_file_info_class */
3593 0xFFFF, /* in_max_output_length */
3594 &inbuf, /* in_input_buffer */
3595 0, /* in_additional_info */
3596 0, /* in_flags */
3597 frame,
3598 &outbuf);
3600 if (!NT_STATUS_IS_OK(status)) {
3601 goto fail;
3604 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3605 pqt)) {
3606 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3607 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3610 fail:
3611 TALLOC_FREE(frame);
3612 return status;
3615 /***************************************************************
3616 Wrapper that allows SMB2 to list user quota.
3617 Synchronous only.
3618 ***************************************************************/
3620 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3621 TALLOC_CTX *mem_ctx,
3622 int quota_fnum,
3623 SMB_NTQUOTA_LIST **pqt_list,
3624 bool first)
3626 NTSTATUS status;
3627 DATA_BLOB inbuf = data_blob_null;
3628 DATA_BLOB outbuf = data_blob_null;
3629 TALLOC_CTX *frame = talloc_stackframe();
3630 struct smb2_query_quota_info info = {0};
3631 enum ndr_err_code err;
3633 if (smbXcli_conn_has_async_calls(cli->conn)) {
3635 * Can't use sync call while an async call is in flight
3637 status = NT_STATUS_INVALID_PARAMETER;
3638 goto cleanup;
3641 info.restart_scan = first ? 1 : 0;
3643 err = ndr_push_struct_blob(
3644 &inbuf,
3645 frame,
3646 &info,
3647 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3649 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3650 status = NT_STATUS_INTERNAL_ERROR;
3651 goto cleanup;
3654 status = cli_smb2_query_info_fnum(
3655 cli,
3656 quota_fnum,
3657 4, /* in_info_type */
3658 0, /* in_file_info_class */
3659 0xFFFF, /* in_max_output_length */
3660 &inbuf, /* in_input_buffer */
3661 0, /* in_additional_info */
3662 0, /* in_flags */
3663 frame,
3664 &outbuf);
3667 * safeguard against panic from calling parse_user_quota_list with
3668 * NULL buffer
3670 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3671 status = NT_STATUS_NO_MORE_ENTRIES;
3674 if (!NT_STATUS_IS_OK(status)) {
3675 goto cleanup;
3678 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3679 pqt_list);
3681 cleanup:
3682 TALLOC_FREE(frame);
3683 return status;
3686 /***************************************************************
3687 Wrapper that allows SMB2 to get file system quota.
3688 Synchronous only.
3689 ***************************************************************/
3691 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3692 int quota_fnum,
3693 SMB_NTQUOTA_STRUCT *pqt)
3695 NTSTATUS status;
3696 DATA_BLOB outbuf = data_blob_null;
3697 TALLOC_CTX *frame = talloc_stackframe();
3699 if (smbXcli_conn_has_async_calls(cli->conn)) {
3701 * Can't use sync call while an async call is in flight
3703 status = NT_STATUS_INVALID_PARAMETER;
3704 goto cleanup;
3707 status = cli_smb2_query_info_fnum(
3708 cli,
3709 quota_fnum,
3710 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3711 FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3712 0xFFFF, /* in_max_output_length */
3713 NULL, /* in_input_buffer */
3714 0, /* in_additional_info */
3715 0, /* in_flags */
3716 frame,
3717 &outbuf);
3719 if (!NT_STATUS_IS_OK(status)) {
3720 goto cleanup;
3723 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3725 cleanup:
3726 TALLOC_FREE(frame);
3727 return status;
3730 /***************************************************************
3731 Wrapper that allows SMB2 to set user quota.
3732 Synchronous only.
3733 ***************************************************************/
3735 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3736 int quota_fnum,
3737 SMB_NTQUOTA_LIST *qtl)
3739 NTSTATUS status;
3740 DATA_BLOB inbuf = data_blob_null;
3741 TALLOC_CTX *frame = talloc_stackframe();
3743 if (smbXcli_conn_has_async_calls(cli->conn)) {
3745 * Can't use sync call while an async call is in flight
3747 status = NT_STATUS_INVALID_PARAMETER;
3748 goto cleanup;
3751 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3752 if (!NT_STATUS_IS_OK(status)) {
3753 goto cleanup;
3756 status = cli_smb2_set_info_fnum(
3757 cli,
3758 quota_fnum,
3759 4, /* in_info_type */
3760 0, /* in_file_info_class */
3761 &inbuf, /* in_input_buffer */
3762 0); /* in_additional_info */
3763 cleanup:
3764 TALLOC_FREE(frame);
3766 return status;
3769 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3770 int quota_fnum,
3771 SMB_NTQUOTA_STRUCT *pqt)
3773 NTSTATUS status;
3774 DATA_BLOB inbuf = data_blob_null;
3775 TALLOC_CTX *frame = talloc_stackframe();
3777 if (smbXcli_conn_has_async_calls(cli->conn)) {
3779 * Can't use sync call while an async call is in flight
3781 status = NT_STATUS_INVALID_PARAMETER;
3782 goto cleanup;
3785 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3786 if (!NT_STATUS_IS_OK(status)) {
3787 goto cleanup;
3790 status = cli_smb2_set_info_fnum(
3791 cli,
3792 quota_fnum,
3793 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3794 FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3795 &inbuf, /* in_input_buffer */
3796 0); /* in_additional_info */
3797 cleanup:
3798 TALLOC_FREE(frame);
3799 return status;
3802 struct cli_smb2_read_state {
3803 struct tevent_context *ev;
3804 struct cli_state *cli;
3805 struct smb2_hnd *ph;
3806 uint64_t start_offset;
3807 uint32_t size;
3808 uint32_t received;
3809 uint8_t *buf;
3812 static void cli_smb2_read_done(struct tevent_req *subreq);
3814 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3815 struct tevent_context *ev,
3816 struct cli_state *cli,
3817 uint16_t fnum,
3818 off_t offset,
3819 size_t size)
3821 NTSTATUS status;
3822 struct tevent_req *req, *subreq;
3823 struct cli_smb2_read_state *state;
3825 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3826 if (req == NULL) {
3827 return NULL;
3829 state->ev = ev;
3830 state->cli = cli;
3831 state->start_offset = (uint64_t)offset;
3832 state->size = (uint32_t)size;
3833 state->received = 0;
3834 state->buf = NULL;
3836 status = map_fnum_to_smb2_handle(cli,
3837 fnum,
3838 &state->ph);
3839 if (tevent_req_nterror(req, status)) {
3840 return tevent_req_post(req, ev);
3843 subreq = smb2cli_read_send(state,
3844 state->ev,
3845 state->cli->conn,
3846 state->cli->timeout,
3847 state->cli->smb2.session,
3848 state->cli->smb2.tcon,
3849 state->size,
3850 state->start_offset,
3851 state->ph->fid_persistent,
3852 state->ph->fid_volatile,
3853 0, /* minimum_count */
3854 0); /* remaining_bytes */
3856 if (tevent_req_nomem(subreq, req)) {
3857 return tevent_req_post(req, ev);
3859 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3860 return req;
3863 static void cli_smb2_read_done(struct tevent_req *subreq)
3865 struct tevent_req *req = tevent_req_callback_data(
3866 subreq, struct tevent_req);
3867 struct cli_smb2_read_state *state = tevent_req_data(
3868 req, struct cli_smb2_read_state);
3869 NTSTATUS status;
3871 status = smb2cli_read_recv(subreq, state,
3872 &state->buf, &state->received);
3873 if (tevent_req_nterror(req, status)) {
3874 return;
3877 if (state->received > state->size) {
3878 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3879 return;
3882 tevent_req_done(req);
3885 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3886 ssize_t *received,
3887 uint8_t **rcvbuf)
3889 NTSTATUS status;
3890 struct cli_smb2_read_state *state = tevent_req_data(
3891 req, struct cli_smb2_read_state);
3893 if (tevent_req_is_nterror(req, &status)) {
3894 return status;
3897 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3898 * better make sure that you copy it away before you talloc_free(req).
3899 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3901 *received = (ssize_t)state->received;
3902 *rcvbuf = state->buf;
3903 return NT_STATUS_OK;
3906 struct cli_smb2_write_state {
3907 struct tevent_context *ev;
3908 struct cli_state *cli;
3909 struct smb2_hnd *ph;
3910 uint32_t flags;
3911 const uint8_t *buf;
3912 uint64_t offset;
3913 uint32_t size;
3914 uint32_t written;
3917 static void cli_smb2_write_written(struct tevent_req *req);
3919 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3920 struct tevent_context *ev,
3921 struct cli_state *cli,
3922 uint16_t fnum,
3923 uint16_t mode,
3924 const uint8_t *buf,
3925 off_t offset,
3926 size_t size)
3928 NTSTATUS status;
3929 struct tevent_req *req, *subreq = NULL;
3930 struct cli_smb2_write_state *state = NULL;
3932 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3933 if (req == NULL) {
3934 return NULL;
3936 state->ev = ev;
3937 state->cli = cli;
3938 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
3939 state->flags = (uint32_t)mode;
3940 state->buf = buf;
3941 state->offset = (uint64_t)offset;
3942 state->size = (uint32_t)size;
3943 state->written = 0;
3945 status = map_fnum_to_smb2_handle(cli,
3946 fnum,
3947 &state->ph);
3948 if (tevent_req_nterror(req, status)) {
3949 return tevent_req_post(req, ev);
3952 subreq = smb2cli_write_send(state,
3953 state->ev,
3954 state->cli->conn,
3955 state->cli->timeout,
3956 state->cli->smb2.session,
3957 state->cli->smb2.tcon,
3958 state->size,
3959 state->offset,
3960 state->ph->fid_persistent,
3961 state->ph->fid_volatile,
3962 0, /* remaining_bytes */
3963 state->flags, /* flags */
3964 state->buf);
3966 if (tevent_req_nomem(subreq, req)) {
3967 return tevent_req_post(req, ev);
3969 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
3970 return req;
3973 static void cli_smb2_write_written(struct tevent_req *subreq)
3975 struct tevent_req *req = tevent_req_callback_data(
3976 subreq, struct tevent_req);
3977 struct cli_smb2_write_state *state = tevent_req_data(
3978 req, struct cli_smb2_write_state);
3979 NTSTATUS status;
3980 uint32_t written;
3982 status = smb2cli_write_recv(subreq, &written);
3983 TALLOC_FREE(subreq);
3984 if (tevent_req_nterror(req, status)) {
3985 return;
3988 state->written = written;
3990 tevent_req_done(req);
3993 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
3994 size_t *pwritten)
3996 struct cli_smb2_write_state *state = tevent_req_data(
3997 req, struct cli_smb2_write_state);
3998 NTSTATUS status;
4000 if (tevent_req_is_nterror(req, &status)) {
4001 tevent_req_received(req);
4002 return status;
4005 if (pwritten != NULL) {
4006 *pwritten = (size_t)state->written;
4008 tevent_req_received(req);
4009 return NT_STATUS_OK;
4012 /***************************************************************
4013 Wrapper that allows SMB2 async write using an fnum.
4014 This is mostly cut-and-paste from Volker's code inside
4015 source3/libsmb/clireadwrite.c, adapted for SMB2.
4017 Done this way so I can reuse all the logic inside cli_push()
4018 for free :-).
4019 ***************************************************************/
4021 struct cli_smb2_writeall_state {
4022 struct tevent_context *ev;
4023 struct cli_state *cli;
4024 struct smb2_hnd *ph;
4025 uint32_t flags;
4026 const uint8_t *buf;
4027 uint64_t offset;
4028 uint32_t size;
4029 uint32_t written;
4032 static void cli_smb2_writeall_written(struct tevent_req *req);
4034 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4035 struct tevent_context *ev,
4036 struct cli_state *cli,
4037 uint16_t fnum,
4038 uint16_t mode,
4039 const uint8_t *buf,
4040 off_t offset,
4041 size_t size)
4043 NTSTATUS status;
4044 struct tevent_req *req, *subreq = NULL;
4045 struct cli_smb2_writeall_state *state = NULL;
4046 uint32_t to_write;
4047 uint32_t max_size;
4048 bool ok;
4050 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4051 if (req == NULL) {
4052 return NULL;
4054 state->ev = ev;
4055 state->cli = cli;
4056 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4057 state->flags = (uint32_t)mode;
4058 state->buf = buf;
4059 state->offset = (uint64_t)offset;
4060 state->size = (uint32_t)size;
4061 state->written = 0;
4063 status = map_fnum_to_smb2_handle(cli,
4064 fnum,
4065 &state->ph);
4066 if (tevent_req_nterror(req, status)) {
4067 return tevent_req_post(req, ev);
4070 to_write = state->size;
4071 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4072 to_write = MIN(max_size, to_write);
4073 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4074 if (ok) {
4075 to_write = MIN(max_size, to_write);
4078 subreq = smb2cli_write_send(state,
4079 state->ev,
4080 state->cli->conn,
4081 state->cli->timeout,
4082 state->cli->smb2.session,
4083 state->cli->smb2.tcon,
4084 to_write,
4085 state->offset,
4086 state->ph->fid_persistent,
4087 state->ph->fid_volatile,
4088 0, /* remaining_bytes */
4089 state->flags, /* flags */
4090 state->buf + state->written);
4092 if (tevent_req_nomem(subreq, req)) {
4093 return tevent_req_post(req, ev);
4095 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4096 return req;
4099 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4101 struct tevent_req *req = tevent_req_callback_data(
4102 subreq, struct tevent_req);
4103 struct cli_smb2_writeall_state *state = tevent_req_data(
4104 req, struct cli_smb2_writeall_state);
4105 NTSTATUS status;
4106 uint32_t written, to_write;
4107 uint32_t max_size;
4108 bool ok;
4110 status = smb2cli_write_recv(subreq, &written);
4111 TALLOC_FREE(subreq);
4112 if (tevent_req_nterror(req, status)) {
4113 return;
4116 state->written += written;
4118 if (state->written > state->size) {
4119 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4120 return;
4123 to_write = state->size - state->written;
4125 if (to_write == 0) {
4126 tevent_req_done(req);
4127 return;
4130 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4131 to_write = MIN(max_size, to_write);
4132 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4133 if (ok) {
4134 to_write = MIN(max_size, to_write);
4137 subreq = smb2cli_write_send(state,
4138 state->ev,
4139 state->cli->conn,
4140 state->cli->timeout,
4141 state->cli->smb2.session,
4142 state->cli->smb2.tcon,
4143 to_write,
4144 state->offset + state->written,
4145 state->ph->fid_persistent,
4146 state->ph->fid_volatile,
4147 0, /* remaining_bytes */
4148 state->flags, /* flags */
4149 state->buf + state->written);
4151 if (tevent_req_nomem(subreq, req)) {
4152 return;
4154 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4157 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4158 size_t *pwritten)
4160 struct cli_smb2_writeall_state *state = tevent_req_data(
4161 req, struct cli_smb2_writeall_state);
4162 NTSTATUS status;
4164 if (tevent_req_is_nterror(req, &status)) {
4165 return status;
4167 if (pwritten != NULL) {
4168 *pwritten = (size_t)state->written;
4170 return NT_STATUS_OK;
4173 struct cli_smb2_splice_state {
4174 struct tevent_context *ev;
4175 struct cli_state *cli;
4176 struct smb2_hnd *src_ph;
4177 struct smb2_hnd *dst_ph;
4178 int (*splice_cb)(off_t n, void *priv);
4179 void *priv;
4180 off_t written;
4181 off_t size;
4182 off_t src_offset;
4183 off_t dst_offset;
4184 bool resized;
4185 struct req_resume_key_rsp resume_rsp;
4186 struct srv_copychunk_copy cc_copy;
4189 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4190 struct tevent_req *req);
4192 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4194 struct tevent_req *req = tevent_req_callback_data(
4195 subreq, struct tevent_req);
4196 struct cli_smb2_splice_state *state =
4197 tevent_req_data(req,
4198 struct cli_smb2_splice_state);
4199 struct smbXcli_conn *conn = state->cli->conn;
4200 DATA_BLOB out_input_buffer = data_blob_null;
4201 DATA_BLOB out_output_buffer = data_blob_null;
4202 struct srv_copychunk_rsp cc_copy_rsp;
4203 enum ndr_err_code ndr_ret;
4204 NTSTATUS status;
4206 status = smb2cli_ioctl_recv(subreq, state,
4207 &out_input_buffer,
4208 &out_output_buffer);
4209 TALLOC_FREE(subreq);
4210 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4211 state->resized) && tevent_req_nterror(req, status)) {
4212 return;
4215 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4216 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4217 if (ndr_ret != NDR_ERR_SUCCESS) {
4218 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4219 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4220 return;
4223 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4224 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4225 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4226 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4227 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4228 tevent_req_nterror(req, status)) {
4229 return;
4232 state->resized = true;
4233 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4234 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4235 } else {
4236 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4237 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4238 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4239 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4240 return;
4242 state->src_offset += cc_copy_rsp.total_bytes_written;
4243 state->dst_offset += cc_copy_rsp.total_bytes_written;
4244 state->written += cc_copy_rsp.total_bytes_written;
4245 if (!state->splice_cb(state->written, state->priv)) {
4246 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4247 return;
4251 cli_splice_copychunk_send(state, req);
4254 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4255 struct tevent_req *req)
4257 struct tevent_req *subreq;
4258 enum ndr_err_code ndr_ret;
4259 struct smbXcli_conn *conn = state->cli->conn;
4260 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4261 off_t src_offset = state->src_offset;
4262 off_t dst_offset = state->dst_offset;
4263 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4264 state->size - state->written);
4265 DATA_BLOB in_input_buffer = data_blob_null;
4266 DATA_BLOB in_output_buffer = data_blob_null;
4268 if (state->size - state->written == 0) {
4269 tevent_req_done(req);
4270 return;
4273 cc_copy->chunk_count = 0;
4274 while (req_len) {
4275 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4276 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4277 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4278 smb2cli_conn_cc_chunk_len(conn));
4279 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4280 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4281 return;
4283 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4284 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4285 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4286 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4287 return;
4289 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4290 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4291 cc_copy->chunk_count++;
4294 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4295 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4296 if (ndr_ret != NDR_ERR_SUCCESS) {
4297 DEBUG(0, ("failed to marshall copy chunk req\n"));
4298 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4299 return;
4302 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4303 state->cli->timeout,
4304 state->cli->smb2.session,
4305 state->cli->smb2.tcon,
4306 state->dst_ph->fid_persistent, /* in_fid_persistent */
4307 state->dst_ph->fid_volatile, /* in_fid_volatile */
4308 FSCTL_SRV_COPYCHUNK_WRITE,
4309 0, /* in_max_input_length */
4310 &in_input_buffer,
4311 12, /* in_max_output_length */
4312 &in_output_buffer,
4313 SMB2_IOCTL_FLAG_IS_FSCTL);
4314 if (tevent_req_nomem(subreq, req)) {
4315 return;
4317 tevent_req_set_callback(subreq,
4318 cli_splice_copychunk_done,
4319 req);
4322 static void cli_splice_key_done(struct tevent_req *subreq)
4324 struct tevent_req *req = tevent_req_callback_data(
4325 subreq, struct tevent_req);
4326 struct cli_smb2_splice_state *state =
4327 tevent_req_data(req,
4328 struct cli_smb2_splice_state);
4329 enum ndr_err_code ndr_ret;
4330 NTSTATUS status;
4332 DATA_BLOB out_input_buffer = data_blob_null;
4333 DATA_BLOB out_output_buffer = data_blob_null;
4335 status = smb2cli_ioctl_recv(subreq, state,
4336 &out_input_buffer,
4337 &out_output_buffer);
4338 TALLOC_FREE(subreq);
4339 if (tevent_req_nterror(req, status)) {
4340 return;
4343 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4344 state, &state->resume_rsp,
4345 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4346 if (ndr_ret != NDR_ERR_SUCCESS) {
4347 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4348 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4349 return;
4352 memcpy(&state->cc_copy.source_key,
4353 &state->resume_rsp.resume_key,
4354 sizeof state->resume_rsp.resume_key);
4356 cli_splice_copychunk_send(state, req);
4359 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4360 struct tevent_context *ev,
4361 struct cli_state *cli,
4362 uint16_t src_fnum, uint16_t dst_fnum,
4363 off_t size, off_t src_offset, off_t dst_offset,
4364 int (*splice_cb)(off_t n, void *priv),
4365 void *priv)
4367 struct tevent_req *req;
4368 struct tevent_req *subreq;
4369 struct cli_smb2_splice_state *state;
4370 NTSTATUS status;
4371 DATA_BLOB in_input_buffer = data_blob_null;
4372 DATA_BLOB in_output_buffer = data_blob_null;
4374 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4375 if (req == NULL) {
4376 return NULL;
4378 state->cli = cli;
4379 state->ev = ev;
4380 state->splice_cb = splice_cb;
4381 state->priv = priv;
4382 state->size = size;
4383 state->written = 0;
4384 state->src_offset = src_offset;
4385 state->dst_offset = dst_offset;
4386 state->cc_copy.chunks = talloc_array(state,
4387 struct srv_copychunk,
4388 smb2cli_conn_cc_max_chunks(cli->conn));
4389 if (state->cc_copy.chunks == NULL) {
4390 return NULL;
4393 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4394 if (tevent_req_nterror(req, status))
4395 return tevent_req_post(req, ev);
4397 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4398 if (tevent_req_nterror(req, status))
4399 return tevent_req_post(req, ev);
4401 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4402 cli->timeout,
4403 cli->smb2.session,
4404 cli->smb2.tcon,
4405 state->src_ph->fid_persistent, /* in_fid_persistent */
4406 state->src_ph->fid_volatile, /* in_fid_volatile */
4407 FSCTL_SRV_REQUEST_RESUME_KEY,
4408 0, /* in_max_input_length */
4409 &in_input_buffer,
4410 32, /* in_max_output_length */
4411 &in_output_buffer,
4412 SMB2_IOCTL_FLAG_IS_FSCTL);
4413 if (tevent_req_nomem(subreq, req)) {
4414 return NULL;
4416 tevent_req_set_callback(subreq,
4417 cli_splice_key_done,
4418 req);
4420 return req;
4423 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4425 struct cli_smb2_splice_state *state = tevent_req_data(
4426 req, struct cli_smb2_splice_state);
4427 NTSTATUS status;
4429 if (tevent_req_is_nterror(req, &status)) {
4430 tevent_req_received(req);
4431 return status;
4433 if (written != NULL) {
4434 *written = state->written;
4436 tevent_req_received(req);
4437 return NT_STATUS_OK;
4440 /***************************************************************
4441 SMB2 enum shadow copy data.
4442 ***************************************************************/
4444 struct cli_smb2_shadow_copy_data_fnum_state {
4445 struct cli_state *cli;
4446 uint16_t fnum;
4447 struct smb2_hnd *ph;
4448 DATA_BLOB out_input_buffer;
4449 DATA_BLOB out_output_buffer;
4452 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4454 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4455 TALLOC_CTX *mem_ctx,
4456 struct tevent_context *ev,
4457 struct cli_state *cli,
4458 uint16_t fnum,
4459 bool get_names)
4461 struct tevent_req *req, *subreq;
4462 struct cli_smb2_shadow_copy_data_fnum_state *state;
4463 NTSTATUS status;
4465 req = tevent_req_create(mem_ctx, &state,
4466 struct cli_smb2_shadow_copy_data_fnum_state);
4467 if (req == NULL) {
4468 return NULL;
4471 state->cli = cli;
4472 state->fnum = fnum;
4474 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4475 if (tevent_req_nterror(req, status)) {
4476 return tevent_req_post(req, ev);
4480 * TODO. Under SMB2 we should send a zero max_output_length
4481 * ioctl to get the required size, then send another ioctl
4482 * to get the data, but the current SMB1 implementation just
4483 * does one roundtrip with a 64K buffer size. Do the same
4484 * for now. JRA.
4487 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4488 state->cli->timeout,
4489 state->cli->smb2.session,
4490 state->cli->smb2.tcon,
4491 state->ph->fid_persistent, /* in_fid_persistent */
4492 state->ph->fid_volatile, /* in_fid_volatile */
4493 FSCTL_GET_SHADOW_COPY_DATA,
4494 0, /* in_max_input_length */
4495 NULL, /* in_input_buffer */
4496 get_names ?
4497 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4498 NULL, /* in_output_buffer */
4499 SMB2_IOCTL_FLAG_IS_FSCTL);
4501 if (tevent_req_nomem(subreq, req)) {
4502 return tevent_req_post(req, ev);
4504 tevent_req_set_callback(subreq,
4505 cli_smb2_shadow_copy_data_fnum_done,
4506 req);
4508 return req;
4511 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4513 struct tevent_req *req = tevent_req_callback_data(
4514 subreq, struct tevent_req);
4515 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4516 req, struct cli_smb2_shadow_copy_data_fnum_state);
4517 NTSTATUS status;
4519 status = smb2cli_ioctl_recv(subreq, state,
4520 &state->out_input_buffer,
4521 &state->out_output_buffer);
4522 tevent_req_simple_finish_ntstatus(subreq, status);
4525 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4526 TALLOC_CTX *mem_ctx,
4527 bool get_names,
4528 char ***pnames,
4529 int *pnum_names)
4531 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4532 req, struct cli_smb2_shadow_copy_data_fnum_state);
4533 char **names = NULL;
4534 uint32_t num_names = 0;
4535 uint32_t num_names_returned = 0;
4536 uint32_t dlength = 0;
4537 uint32_t i;
4538 uint8_t *endp = NULL;
4539 NTSTATUS status;
4541 if (tevent_req_is_nterror(req, &status)) {
4542 return status;
4545 if (state->out_output_buffer.length < 16) {
4546 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4549 num_names = IVAL(state->out_output_buffer.data, 0);
4550 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4551 dlength = IVAL(state->out_output_buffer.data, 8);
4553 if (num_names > 0x7FFFFFFF) {
4554 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4557 if (get_names == false) {
4558 *pnum_names = (int)num_names;
4559 return NT_STATUS_OK;
4561 if (num_names != num_names_returned) {
4562 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4564 if (dlength + 12 < 12) {
4565 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4568 * NB. The below is an allowable return if there are
4569 * more snapshots than the buffer size we told the
4570 * server we can receive. We currently don't support
4571 * this.
4573 if (dlength + 12 > state->out_output_buffer.length) {
4574 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4576 if (state->out_output_buffer.length +
4577 (2 * sizeof(SHADOW_COPY_LABEL)) <
4578 state->out_output_buffer.length) {
4579 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4582 names = talloc_array(mem_ctx, char *, num_names_returned);
4583 if (names == NULL) {
4584 return NT_STATUS_NO_MEMORY;
4587 endp = state->out_output_buffer.data +
4588 state->out_output_buffer.length;
4590 for (i=0; i<num_names_returned; i++) {
4591 bool ret;
4592 uint8_t *src;
4593 size_t converted_size;
4595 src = state->out_output_buffer.data + 12 +
4596 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4598 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4599 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4601 ret = convert_string_talloc(
4602 names, CH_UTF16LE, CH_UNIX,
4603 src, 2 * sizeof(SHADOW_COPY_LABEL),
4604 &names[i], &converted_size);
4605 if (!ret) {
4606 TALLOC_FREE(names);
4607 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4610 *pnum_names = num_names;
4611 *pnames = names;
4612 return NT_STATUS_OK;
4615 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4616 struct cli_state *cli,
4617 uint16_t fnum,
4618 bool get_names,
4619 char ***pnames,
4620 int *pnum_names)
4622 TALLOC_CTX *frame = talloc_stackframe();
4623 struct tevent_context *ev;
4624 struct tevent_req *req;
4625 NTSTATUS status = NT_STATUS_NO_MEMORY;
4627 if (smbXcli_conn_has_async_calls(cli->conn)) {
4629 * Can't use sync call while an async call is in flight
4631 status = NT_STATUS_INVALID_PARAMETER;
4632 goto fail;
4634 ev = samba_tevent_context_init(frame);
4635 if (ev == NULL) {
4636 goto fail;
4638 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4640 cli,
4641 fnum,
4642 get_names);
4643 if (req == NULL) {
4644 goto fail;
4646 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4647 goto fail;
4649 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4650 mem_ctx,
4651 get_names,
4652 pnames,
4653 pnum_names);
4654 fail:
4655 TALLOC_FREE(frame);
4656 return status;
4659 /***************************************************************
4660 Wrapper that allows SMB2 to truncate a file.
4661 Synchronous only.
4662 ***************************************************************/
4664 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4665 uint16_t fnum,
4666 uint64_t newsize)
4668 NTSTATUS status;
4669 uint8_t buf[8] = {0};
4670 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4671 TALLOC_CTX *frame = talloc_stackframe();
4673 if (smbXcli_conn_has_async_calls(cli->conn)) {
4675 * Can't use sync call while an async call is in flight
4677 status = NT_STATUS_INVALID_PARAMETER;
4678 goto fail;
4681 SBVAL(buf, 0, newsize);
4683 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4684 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4686 status = cli_smb2_set_info_fnum(
4687 cli,
4688 fnum,
4689 SMB2_0_INFO_FILE, /* in_info_type */
4690 FSCC_FILE_END_OF_FILE_INFORMATION, /* in_file_info_class */
4691 &inbuf, /* in_input_buffer */
4694 fail:
4695 TALLOC_FREE(frame);
4696 return status;
4699 struct cli_smb2_notify_state {
4700 struct tevent_req *subreq;
4701 struct notify_change *changes;
4702 size_t num_changes;
4705 static void cli_smb2_notify_done(struct tevent_req *subreq);
4706 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4708 struct tevent_req *cli_smb2_notify_send(
4709 TALLOC_CTX *mem_ctx,
4710 struct tevent_context *ev,
4711 struct cli_state *cli,
4712 uint16_t fnum,
4713 uint32_t buffer_size,
4714 uint32_t completion_filter,
4715 bool recursive)
4717 struct tevent_req *req = NULL;
4718 struct cli_smb2_notify_state *state = NULL;
4719 struct smb2_hnd *ph = NULL;
4720 NTSTATUS status;
4722 req = tevent_req_create(mem_ctx, &state,
4723 struct cli_smb2_notify_state);
4724 if (req == NULL) {
4725 return NULL;
4728 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4729 if (tevent_req_nterror(req, status)) {
4730 return tevent_req_post(req, ev);
4733 state->subreq = smb2cli_notify_send(
4734 state,
4736 cli->conn,
4737 cli->timeout,
4738 cli->smb2.session,
4739 cli->smb2.tcon,
4740 buffer_size,
4741 ph->fid_persistent,
4742 ph->fid_volatile,
4743 completion_filter,
4744 recursive);
4745 if (tevent_req_nomem(state->subreq, req)) {
4746 return tevent_req_post(req, ev);
4748 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4749 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4750 return req;
4753 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4755 struct cli_smb2_notify_state *state = tevent_req_data(
4756 req, struct cli_smb2_notify_state);
4757 bool ok;
4759 ok = tevent_req_cancel(state->subreq);
4760 return ok;
4763 static void cli_smb2_notify_done(struct tevent_req *subreq)
4765 struct tevent_req *req = tevent_req_callback_data(
4766 subreq, struct tevent_req);
4767 struct cli_smb2_notify_state *state = tevent_req_data(
4768 req, struct cli_smb2_notify_state);
4769 uint8_t *base;
4770 uint32_t len;
4771 uint32_t ofs;
4772 NTSTATUS status;
4774 status = smb2cli_notify_recv(subreq, state, &base, &len);
4775 TALLOC_FREE(subreq);
4777 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4778 tevent_req_done(req);
4779 return;
4781 if (tevent_req_nterror(req, status)) {
4782 return;
4785 ofs = 0;
4787 while (len - ofs >= 12) {
4788 struct notify_change *tmp;
4789 struct notify_change *c;
4790 uint32_t next_ofs = IVAL(base, ofs);
4791 uint32_t file_name_length = IVAL(base, ofs+8);
4792 size_t namelen;
4793 bool ok;
4795 tmp = talloc_realloc(
4796 state,
4797 state->changes,
4798 struct notify_change,
4799 state->num_changes + 1);
4800 if (tevent_req_nomem(tmp, req)) {
4801 return;
4803 state->changes = tmp;
4804 c = &state->changes[state->num_changes];
4805 state->num_changes += 1;
4807 if (smb_buffer_oob(len, ofs, next_ofs) ||
4808 smb_buffer_oob(len, ofs+12, file_name_length)) {
4809 tevent_req_nterror(
4810 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4811 return;
4814 c->action = IVAL(base, ofs+4);
4816 ok = convert_string_talloc(
4817 state->changes,
4818 CH_UTF16LE,
4819 CH_UNIX,
4820 base + ofs + 12,
4821 file_name_length,
4822 &c->name,
4823 &namelen);
4824 if (!ok) {
4825 tevent_req_nterror(
4826 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4827 return;
4830 if (next_ofs == 0) {
4831 break;
4833 ofs += next_ofs;
4836 tevent_req_done(req);
4839 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4840 TALLOC_CTX *mem_ctx,
4841 struct notify_change **pchanges,
4842 uint32_t *pnum_changes)
4844 struct cli_smb2_notify_state *state = tevent_req_data(
4845 req, struct cli_smb2_notify_state);
4846 NTSTATUS status;
4848 if (tevent_req_is_nterror(req, &status)) {
4849 return status;
4851 *pchanges = talloc_move(mem_ctx, &state->changes);
4852 *pnum_changes = state->num_changes;
4853 return NT_STATUS_OK;
4856 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4857 uint32_t buffer_size, uint32_t completion_filter,
4858 bool recursive, TALLOC_CTX *mem_ctx,
4859 struct notify_change **pchanges,
4860 uint32_t *pnum_changes)
4862 TALLOC_CTX *frame = talloc_stackframe();
4863 struct tevent_context *ev;
4864 struct tevent_req *req;
4865 NTSTATUS status = NT_STATUS_NO_MEMORY;
4867 if (smbXcli_conn_has_async_calls(cli->conn)) {
4869 * Can't use sync call while an async call is in flight
4871 status = NT_STATUS_INVALID_PARAMETER;
4872 goto fail;
4874 ev = samba_tevent_context_init(frame);
4875 if (ev == NULL) {
4876 goto fail;
4878 req = cli_smb2_notify_send(
4879 frame,
4881 cli,
4882 fnum,
4883 buffer_size,
4884 completion_filter,
4885 recursive);
4886 if (req == NULL) {
4887 goto fail;
4889 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4890 goto fail;
4892 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4893 fail:
4894 TALLOC_FREE(frame);
4895 return status;
4898 struct cli_smb2_fsctl_state {
4899 DATA_BLOB out;
4902 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4904 struct tevent_req *cli_smb2_fsctl_send(
4905 TALLOC_CTX *mem_ctx,
4906 struct tevent_context *ev,
4907 struct cli_state *cli,
4908 uint16_t fnum,
4909 uint32_t ctl_code,
4910 const DATA_BLOB *in,
4911 uint32_t max_out)
4913 struct tevent_req *req = NULL, *subreq = NULL;
4914 struct cli_smb2_fsctl_state *state = NULL;
4915 struct smb2_hnd *ph = NULL;
4916 NTSTATUS status;
4918 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4919 if (req == NULL) {
4920 return NULL;
4923 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4924 if (tevent_req_nterror(req, status)) {
4925 return tevent_req_post(req, ev);
4928 subreq = smb2cli_ioctl_send(
4929 state,
4931 cli->conn,
4932 cli->timeout,
4933 cli->smb2.session,
4934 cli->smb2.tcon,
4935 ph->fid_persistent,
4936 ph->fid_volatile,
4937 ctl_code,
4938 0, /* in_max_input_length */
4940 max_out,
4941 NULL,
4942 SMB2_IOCTL_FLAG_IS_FSCTL);
4944 if (tevent_req_nomem(subreq, req)) {
4945 return tevent_req_post(req, ev);
4947 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
4948 return req;
4951 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
4953 struct tevent_req *req = tevent_req_callback_data(
4954 subreq, struct tevent_req);
4955 struct cli_smb2_fsctl_state *state = tevent_req_data(
4956 req, struct cli_smb2_fsctl_state);
4957 NTSTATUS status;
4959 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
4960 tevent_req_simple_finish_ntstatus(subreq, status);
4963 NTSTATUS cli_smb2_fsctl_recv(
4964 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
4966 struct cli_smb2_fsctl_state *state = tevent_req_data(
4967 req, struct cli_smb2_fsctl_state);
4968 NTSTATUS status = NT_STATUS_OK;
4970 if (tevent_req_is_nterror(req, &status)) {
4971 tevent_req_received(req);
4972 return status;
4975 if (state->out.length == 0) {
4976 *out = (DATA_BLOB) { .data = NULL, };
4977 } else {
4979 * Can't use talloc_move() here, the outblobs from
4980 * smb2cli_ioctl_recv() are not standalone talloc
4981 * objects but just peek into the larger buffers
4982 * received, hanging off "state".
4984 *out = data_blob_talloc(
4985 mem_ctx, state->out.data, state->out.length);
4986 if (out->data == NULL) {
4987 status = NT_STATUS_NO_MEMORY;
4991 tevent_req_received(req);
4992 return NT_STATUS_OK;
4995 struct cli_smb2_get_posix_fs_info_state {
4996 struct tevent_context *ev;
4997 struct cli_state *cli;
4998 uint16_t fnum;
4999 uint32_t optimal_transfer_size;
5000 uint32_t block_size;
5001 uint64_t total_blocks;
5002 uint64_t blocks_available;
5003 uint64_t user_blocks_available;
5004 uint64_t total_file_nodes;
5005 uint64_t free_file_nodes;
5006 uint64_t fs_identifier;
5009 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req *subreq);
5010 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req *subreq);
5011 static void cli_smb2_get_posix_fs_info_done(struct tevent_req *subreq);
5013 struct tevent_req *cli_smb2_get_posix_fs_info_send(TALLOC_CTX *mem_ctx,
5014 struct tevent_context *ev,
5015 struct cli_state *cli)
5017 struct smb2_create_blobs *cblob = NULL;
5018 struct tevent_req *req = NULL, *subreq = NULL;
5019 struct cli_smb2_get_posix_fs_info_state *state = NULL;
5020 NTSTATUS status;
5022 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_get_posix_fs_info_state);
5023 if (req == NULL) {
5024 return NULL;
5026 *state = (struct cli_smb2_get_posix_fs_info_state) {
5027 .ev = ev,
5028 .cli = cli,
5030 status = make_smb2_posix_create_ctx(state, &cblob, 0);
5031 if (!NT_STATUS_IS_OK(status)) {
5032 return NULL;
5035 /* First open the top level directory. */
5036 subreq = cli_smb2_create_fnum_send(state,
5037 state->ev,
5038 state->cli,
5040 (struct cli_smb2_create_flags){0},
5041 SMB2_IMPERSONATION_IMPERSONATION,
5042 FILE_READ_ATTRIBUTES,
5043 FILE_ATTRIBUTE_DIRECTORY,
5044 FILE_SHARE_READ | FILE_SHARE_WRITE |
5045 FILE_SHARE_DELETE,
5046 FILE_OPEN,
5047 FILE_DIRECTORY_FILE,
5048 cblob);
5049 if (tevent_req_nomem(subreq, req)) {
5050 return tevent_req_post(req, ev);
5053 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_opened, req);
5054 return req;
5057 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req *subreq)
5059 struct tevent_req *req = tevent_req_callback_data(
5060 subreq, struct tevent_req);
5061 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5062 req, struct cli_smb2_get_posix_fs_info_state);
5063 struct smb2_create_blobs *cblob = {0};
5064 NTSTATUS status;
5066 status = cli_smb2_create_fnum_recv(subreq,
5067 &state->fnum,
5068 NULL,
5069 state,
5070 cblob,
5071 NULL);
5072 TALLOC_FREE(subreq);
5074 if (tevent_req_nterror(req, status)) {
5075 return;
5078 subreq = cli_smb2_query_info_fnum_send(
5079 state,
5080 state->ev,
5081 state->cli,
5082 state->fnum,
5083 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
5084 SMB2_FS_POSIX_INFORMATION, /* in_file_info_class */
5085 0xFFFF, /* in_max_output_length */
5086 NULL, /* in_input_buffer */
5087 0, /* in_additional_info */
5088 0); /* in_flags */
5089 if (tevent_req_nomem(subreq, req)) {
5090 return;
5093 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_queried, req);
5096 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req *subreq)
5098 struct tevent_req *req = tevent_req_callback_data(
5099 subreq, struct tevent_req);
5100 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5101 req, struct cli_smb2_get_posix_fs_info_state);
5102 DATA_BLOB outbuf = data_blob_null;
5103 NTSTATUS status;
5105 status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf);
5106 TALLOC_FREE(subreq);
5108 if (tevent_req_nterror(req, status)) {
5109 return;
5112 if (outbuf.length != 56) {
5113 goto close;
5116 state->optimal_transfer_size = PULL_LE_U32(outbuf.data, 0);
5117 state->block_size = PULL_LE_U32(outbuf.data, 4);
5118 state->total_blocks = PULL_LE_U64(outbuf.data, 8);
5119 state->blocks_available = PULL_LE_U64(outbuf.data, 16);
5120 state->user_blocks_available = PULL_LE_U64(outbuf.data, 24);
5121 state->total_file_nodes = PULL_LE_U64(outbuf.data, 32);
5122 state->free_file_nodes = PULL_LE_U64(outbuf.data, 40);
5123 state->fs_identifier = PULL_LE_U64(outbuf.data, 48);
5125 close:
5126 subreq = cli_smb2_close_fnum_send(state,
5127 state->ev,
5128 state->cli,
5129 state->fnum,
5131 if (tevent_req_nomem(subreq, req)) {
5132 return;
5135 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_done, req);
5138 static void cli_smb2_get_posix_fs_info_done(struct tevent_req *subreq)
5140 struct tevent_req *req = tevent_req_callback_data(
5141 subreq, struct tevent_req);
5142 NTSTATUS status;
5144 status = cli_smb2_close_fnum_recv(subreq);
5145 TALLOC_FREE(subreq);
5146 if (tevent_req_nterror(req, status)) {
5147 return;
5150 tevent_req_done(req);
5153 NTSTATUS cli_smb2_get_posix_fs_info_recv(struct tevent_req *req,
5154 uint32_t *optimal_transfer_size,
5155 uint32_t *block_size,
5156 uint64_t *total_blocks,
5157 uint64_t *blocks_available,
5158 uint64_t *user_blocks_available,
5159 uint64_t *total_file_nodes,
5160 uint64_t *free_file_nodes,
5161 uint64_t *fs_identifier)
5163 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5164 req, struct cli_smb2_get_posix_fs_info_state);
5165 NTSTATUS status;
5167 if (tevent_req_is_nterror(req, &status)) {
5168 tevent_req_received(req);
5169 return status;
5171 *optimal_transfer_size = state->optimal_transfer_size;
5172 *block_size = state->block_size;
5173 *total_blocks = state->total_blocks;
5174 *blocks_available = state->blocks_available;
5175 *user_blocks_available = state->user_blocks_available;
5176 *total_file_nodes = state->total_file_nodes;
5177 *free_file_nodes = state->free_file_nodes;
5178 *fs_identifier = state->fs_identifier;
5179 tevent_req_received(req);
5180 return NT_STATUS_OK;