s3:utils: Fix 'Usage:' for 'net ads enctypes'
[samba4-gss.git] / source3 / libsmb / cli_smb2_fnum.c
blob155bc6ae353cd05fa6da5af617ee15d2afca9ac0
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 tevent_context *ev;
208 struct cli_state *cli;
209 char *fname;
210 struct cli_smb2_create_flags create_flags;
211 uint32_t impersonation_level;
212 uint32_t desired_access;
213 uint32_t file_attributes;
214 uint32_t share_access;
215 uint32_t create_disposition;
216 uint32_t create_options;
217 struct smb2_create_blobs in_cblobs;
218 struct smb2_create_blobs out_cblobs;
219 struct smb_create_returns cr;
220 struct symlink_reparse_struct *symlink;
221 uint16_t fnum;
222 struct tevent_req *subreq;
225 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
226 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
228 struct tevent_req *cli_smb2_create_fnum_send(
229 TALLOC_CTX *mem_ctx,
230 struct tevent_context *ev,
231 struct cli_state *cli,
232 const char *fname_in,
233 struct cli_smb2_create_flags create_flags,
234 uint32_t impersonation_level,
235 uint32_t desired_access,
236 uint32_t file_attributes,
237 uint32_t share_access,
238 uint32_t create_disposition,
239 uint32_t create_options,
240 const struct smb2_create_blobs *in_cblobs)
242 struct tevent_req *req, *subreq;
243 struct cli_smb2_create_fnum_state *state;
244 char *fname = NULL;
245 size_t fname_len = 0;
246 bool have_twrp;
247 NTTIME ntt;
248 NTSTATUS status;
250 req = tevent_req_create(mem_ctx, &state,
251 struct cli_smb2_create_fnum_state);
252 if (req == NULL) {
253 return NULL;
255 state->ev = ev;
256 state->cli = cli;
257 state->create_flags = create_flags;
258 state->impersonation_level = impersonation_level;
259 state->desired_access = desired_access;
260 state->file_attributes = file_attributes;
261 state->share_access = share_access;
262 state->create_disposition = create_disposition;
264 if (cli->backup_intent) {
265 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
267 state->create_options = create_options;
269 fname = talloc_strdup(state, fname_in);
270 if (tevent_req_nomem(fname, req)) {
271 return tevent_req_post(req, ev);
274 if (cli->smb2.client_smb311_posix) {
275 uint8_t modebuf[4] = {
279 status =
280 smb2_create_blob_add(state,
281 &state->in_cblobs,
282 SMB2_CREATE_TAG_POSIX,
283 (DATA_BLOB){
284 .data = modebuf,
285 .length = sizeof(modebuf),
287 if (tevent_req_nterror(req, status)) {
288 return tevent_req_post(req, ev);
292 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
293 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
294 if (have_twrp) {
295 status = smb2_create_blob_add(
296 state,
297 &state->in_cblobs,
298 SMB2_CREATE_TAG_TWRP,
299 (DATA_BLOB) {
300 .data = (uint8_t *)&ntt,
301 .length = sizeof(ntt),
303 if (tevent_req_nterror(req, status)) {
304 return tevent_req_post(req, ev);
308 if (in_cblobs != NULL) {
309 uint32_t i;
310 for (i=0; i<in_cblobs->num_blobs; i++) {
311 struct smb2_create_blob *b = &in_cblobs->blobs[i];
312 status = smb2_create_blob_add(
313 state, &state->in_cblobs, b->tag, b->data);
314 if (!NT_STATUS_IS_OK(status)) {
315 tevent_req_nterror(req, status);
316 return tevent_req_post(req, ev);
321 fname = smb2_dfs_share_path(state, cli, fname);
322 if (tevent_req_nomem(fname, req)) {
323 return tevent_req_post(req, ev);
325 fname_len = strlen(fname);
327 /* SMB2 is pickier about pathnames. Ensure it doesn't
328 start in a '\' */
329 if (*fname == '\\') {
330 fname++;
331 fname_len--;
334 /* Or end in a '\' */
335 if (fname_len > 0 && fname[fname_len-1] == '\\') {
336 fname_len -= 1;
339 state->fname = talloc_strndup(state, fname, fname_len);
340 if (tevent_req_nomem(state->fname, req)) {
341 return tevent_req_post(req, ev);
344 subreq = smb2cli_create_send(state,
346 cli->conn,
347 cli->timeout,
348 cli->smb2.session,
349 cli->smb2.tcon,
350 state->fname,
351 flags_to_smb2_oplock(create_flags),
352 impersonation_level,
353 desired_access,
354 file_attributes,
355 share_access,
356 create_disposition,
357 create_options,
358 &state->in_cblobs);
359 if (tevent_req_nomem(subreq, req)) {
360 return tevent_req_post(req, ev);
362 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
364 state->subreq = subreq;
365 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
367 return req;
370 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
372 struct tevent_req *req = tevent_req_callback_data(
373 subreq, struct tevent_req);
374 struct cli_smb2_create_fnum_state *state = tevent_req_data(
375 req, struct cli_smb2_create_fnum_state);
376 uint64_t fid_persistent, fid_volatile;
377 struct smb2_create_blob *posix = NULL;
378 struct cli_state *cli = state->cli;
379 NTSTATUS status;
381 status = smb2cli_create_recv(subreq,
382 &fid_persistent,
383 &fid_volatile,
384 &state->cr,
385 state,
386 &state->out_cblobs,
387 &state->symlink);
388 TALLOC_FREE(subreq);
390 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
392 if (state->create_options & FILE_OPEN_REPARSE_POINT) {
394 * Should not happen, but you never know...
396 tevent_req_nterror(
397 req, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED);
398 return;
401 state->create_options |= FILE_OPEN_REPARSE_POINT;
403 subreq = smb2cli_create_send(state,
404 state->ev,
405 cli->conn,
406 cli->timeout,
407 cli->smb2.session,
408 cli->smb2.tcon,
409 state->fname,
410 flags_to_smb2_oplock(
411 state->create_flags),
412 state->impersonation_level,
413 state->desired_access,
414 state->file_attributes,
415 state->share_access,
416 state->create_disposition,
417 state->create_options,
418 &state->in_cblobs);
419 if (tevent_req_nomem(subreq, req)) {
420 return;
422 tevent_req_set_callback(subreq,
423 cli_smb2_create_fnum_done,
424 req);
425 state->subreq = subreq;
426 return;
429 if (tevent_req_nterror(req, status)) {
430 return;
433 posix = smb2_create_blob_find(&state->in_cblobs,
434 SMB2_CREATE_TAG_POSIX);
436 status = map_smb2_handle_to_fnum(state->cli,
437 fid_persistent,
438 fid_volatile,
439 (posix != NULL),
440 &state->fnum);
441 if (tevent_req_nterror(req, status)) {
442 return;
444 tevent_req_done(req);
447 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
449 struct cli_smb2_create_fnum_state *state = tevent_req_data(
450 req, struct cli_smb2_create_fnum_state);
451 return tevent_req_cancel(state->subreq);
454 NTSTATUS cli_smb2_create_fnum_recv(
455 struct tevent_req *req,
456 uint16_t *pfnum,
457 struct smb_create_returns *cr,
458 TALLOC_CTX *mem_ctx,
459 struct smb2_create_blobs *out_cblobs,
460 struct symlink_reparse_struct **symlink)
462 struct cli_smb2_create_fnum_state *state = tevent_req_data(
463 req, struct cli_smb2_create_fnum_state);
464 NTSTATUS status;
466 if (tevent_req_is_nterror(req, &status)) {
467 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
468 (symlink != NULL)) {
469 *symlink = talloc_move(mem_ctx, &state->symlink);
471 return status;
473 if (pfnum != NULL) {
474 *pfnum = state->fnum;
476 if (cr != NULL) {
477 *cr = state->cr;
479 if (out_cblobs != NULL) {
480 *out_cblobs = (struct smb2_create_blobs) {
481 .num_blobs = state->out_cblobs.num_blobs,
482 .blobs = talloc_move(
483 mem_ctx, &state->out_cblobs.blobs),
486 return NT_STATUS_OK;
489 bool cli_smb2_fnum_is_posix(struct cli_state *cli, uint16_t fnum)
491 struct smb2_hnd *ph = NULL;
492 NTSTATUS status;
494 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
495 if (!NT_STATUS_IS_OK(status)) {
496 return false;
498 return ph->posix;
501 NTSTATUS cli_smb2_create_fnum(
502 struct cli_state *cli,
503 const char *fname,
504 struct cli_smb2_create_flags create_flags,
505 uint32_t impersonation_level,
506 uint32_t desired_access,
507 uint32_t file_attributes,
508 uint32_t share_access,
509 uint32_t create_disposition,
510 uint32_t create_options,
511 const struct smb2_create_blobs *in_cblobs,
512 uint16_t *pfid,
513 struct smb_create_returns *cr,
514 TALLOC_CTX *mem_ctx,
515 struct smb2_create_blobs *out_cblobs)
517 TALLOC_CTX *frame = talloc_stackframe();
518 struct tevent_context *ev;
519 struct tevent_req *req;
520 NTSTATUS status = NT_STATUS_NO_MEMORY;
522 if (smbXcli_conn_has_async_calls(cli->conn)) {
524 * Can't use sync call while an async call is in flight
526 status = NT_STATUS_INVALID_PARAMETER;
527 goto fail;
529 ev = samba_tevent_context_init(frame);
530 if (ev == NULL) {
531 goto fail;
533 req = cli_smb2_create_fnum_send(
534 frame,
536 cli,
537 fname,
538 create_flags,
539 impersonation_level,
540 desired_access,
541 file_attributes,
542 share_access,
543 create_disposition,
544 create_options,
545 in_cblobs);
546 if (req == NULL) {
547 goto fail;
549 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
550 goto fail;
552 status = cli_smb2_create_fnum_recv(
553 req, pfid, cr, mem_ctx, out_cblobs, NULL);
554 fail:
555 TALLOC_FREE(frame);
556 return status;
559 /***************************************************************
560 Small wrapper that allows SMB2 close to use a uint16_t fnum.
561 ***************************************************************/
563 struct cli_smb2_close_fnum_state {
564 struct cli_state *cli;
565 uint16_t fnum;
566 struct smb2_hnd *ph;
569 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
571 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
572 struct tevent_context *ev,
573 struct cli_state *cli,
574 uint16_t fnum,
575 uint16_t flags)
577 struct tevent_req *req, *subreq;
578 struct cli_smb2_close_fnum_state *state;
579 NTSTATUS status;
581 req = tevent_req_create(mem_ctx, &state,
582 struct cli_smb2_close_fnum_state);
583 if (req == NULL) {
584 return NULL;
586 state->cli = cli;
587 state->fnum = fnum;
589 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
590 if (tevent_req_nterror(req, status)) {
591 return tevent_req_post(req, ev);
594 subreq = smb2cli_close_send(state,
596 cli->conn,
597 cli->timeout,
598 cli->smb2.session,
599 cli->smb2.tcon,
600 flags,
601 state->ph->fid_persistent,
602 state->ph->fid_volatile);
603 if (tevent_req_nomem(subreq, req)) {
604 return tevent_req_post(req, ev);
606 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
607 return req;
610 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
612 struct tevent_req *req = tevent_req_callback_data(
613 subreq, struct tevent_req);
614 struct cli_smb2_close_fnum_state *state = tevent_req_data(
615 req, struct cli_smb2_close_fnum_state);
616 NTSTATUS status;
618 status = smb2cli_close_recv(subreq);
619 if (tevent_req_nterror(req, status)) {
620 return;
623 /* Delete the fnum -> handle mapping. */
624 status = delete_smb2_handle_mapping(state->cli, &state->ph,
625 state->fnum);
626 if (tevent_req_nterror(req, status)) {
627 return;
629 tevent_req_done(req);
632 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
634 return tevent_req_simple_recv_ntstatus(req);
637 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
639 TALLOC_CTX *frame = talloc_stackframe();
640 struct tevent_context *ev;
641 struct tevent_req *req;
642 NTSTATUS status = NT_STATUS_NO_MEMORY;
644 if (smbXcli_conn_has_async_calls(cli->conn)) {
646 * Can't use sync call while an async call is in flight
648 status = NT_STATUS_INVALID_PARAMETER;
649 goto fail;
651 ev = samba_tevent_context_init(frame);
652 if (ev == NULL) {
653 goto fail;
655 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
656 if (req == NULL) {
657 goto fail;
659 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
660 goto fail;
662 status = cli_smb2_close_fnum_recv(req);
663 fail:
664 TALLOC_FREE(frame);
665 return status;
668 struct cli_smb2_set_info_fnum_state {
669 uint8_t dummy;
672 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
674 struct tevent_req *cli_smb2_set_info_fnum_send(
675 TALLOC_CTX *mem_ctx,
676 struct tevent_context *ev,
677 struct cli_state *cli,
678 uint16_t fnum,
679 uint8_t in_info_type,
680 uint8_t in_info_class,
681 const DATA_BLOB *in_input_buffer,
682 uint32_t in_additional_info)
684 struct tevent_req *req = NULL, *subreq = NULL;
685 struct cli_smb2_set_info_fnum_state *state = NULL;
686 struct smb2_hnd *ph = NULL;
687 NTSTATUS status;
689 req = tevent_req_create(
690 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
691 if (req == NULL) {
692 return NULL;
695 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
696 if (tevent_req_nterror(req, status)) {
697 return tevent_req_post(req, ev);
700 subreq = smb2cli_set_info_send(
701 state,
703 cli->conn,
704 cli->timeout,
705 cli->smb2.session,
706 cli->smb2.tcon,
707 in_info_type,
708 in_info_class,
709 in_input_buffer,
710 in_additional_info,
711 ph->fid_persistent,
712 ph->fid_volatile);
713 if (tevent_req_nomem(subreq, req)) {
714 return tevent_req_post(req, ev);
716 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
717 return req;
720 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
722 NTSTATUS status = smb2cli_set_info_recv(subreq);
723 tevent_req_simple_finish_ntstatus(subreq, status);
726 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
728 return tevent_req_simple_recv_ntstatus(req);
731 NTSTATUS cli_smb2_set_info_fnum(
732 struct cli_state *cli,
733 uint16_t fnum,
734 uint8_t in_info_type,
735 uint8_t in_info_class,
736 const DATA_BLOB *in_input_buffer,
737 uint32_t in_additional_info)
739 TALLOC_CTX *frame = talloc_stackframe();
740 struct tevent_context *ev = NULL;
741 struct tevent_req *req = NULL;
742 NTSTATUS status = NT_STATUS_NO_MEMORY;
743 bool ok;
745 if (smbXcli_conn_has_async_calls(cli->conn)) {
747 * Can't use sync call while an async call is in flight
749 status = NT_STATUS_INVALID_PARAMETER;
750 goto fail;
752 ev = samba_tevent_context_init(frame);
753 if (ev == NULL) {
754 goto fail;
756 req = cli_smb2_set_info_fnum_send(
757 frame,
759 cli,
760 fnum,
761 in_info_type,
762 in_info_class,
763 in_input_buffer,
764 in_additional_info);
765 if (req == NULL) {
766 goto fail;
768 ok = tevent_req_poll_ntstatus(req, ev, &status);
769 if (!ok) {
770 goto fail;
772 status = cli_smb2_set_info_fnum_recv(req);
773 fail:
774 TALLOC_FREE(frame);
775 return status;
778 struct cli_smb2_delete_on_close_state {
779 struct cli_state *cli;
780 uint8_t data[1];
781 DATA_BLOB inbuf;
784 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
786 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
787 struct tevent_context *ev,
788 struct cli_state *cli,
789 uint16_t fnum,
790 bool flag)
792 struct tevent_req *req = NULL;
793 struct cli_smb2_delete_on_close_state *state = NULL;
794 struct tevent_req *subreq = NULL;
796 req = tevent_req_create(mem_ctx, &state,
797 struct cli_smb2_delete_on_close_state);
798 if (req == NULL) {
799 return NULL;
801 state->cli = cli;
803 /* Setup data array. */
804 SCVAL(&state->data[0], 0, flag ? 1 : 0);
805 state->inbuf.data = &state->data[0];
806 state->inbuf.length = 1;
808 subreq = cli_smb2_set_info_fnum_send(state,
810 cli,
811 fnum,
812 SMB2_0_INFO_FILE,
813 FSCC_FILE_DISPOSITION_INFORMATION,
814 &state->inbuf,
816 if (tevent_req_nomem(subreq, req)) {
817 return tevent_req_post(req, ev);
819 tevent_req_set_callback(subreq,
820 cli_smb2_delete_on_close_done,
821 req);
822 return req;
825 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
827 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
828 tevent_req_simple_finish_ntstatus(subreq, status);
831 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
833 return tevent_req_simple_recv_ntstatus(req);
836 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
838 TALLOC_CTX *frame = talloc_stackframe();
839 struct tevent_context *ev;
840 struct tevent_req *req;
841 NTSTATUS status = NT_STATUS_NO_MEMORY;
843 if (smbXcli_conn_has_async_calls(cli->conn)) {
845 * Can't use sync call while an async call is in flight
847 status = NT_STATUS_INVALID_PARAMETER;
848 goto fail;
850 ev = samba_tevent_context_init(frame);
851 if (ev == NULL) {
852 goto fail;
854 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
855 if (req == NULL) {
856 goto fail;
858 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
859 goto fail;
861 status = cli_smb2_delete_on_close_recv(req);
862 fail:
863 TALLOC_FREE(frame);
864 return status;
867 struct cli_smb2_mkdir_state {
868 struct tevent_context *ev;
869 struct cli_state *cli;
872 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
873 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
875 struct tevent_req *cli_smb2_mkdir_send(
876 TALLOC_CTX *mem_ctx,
877 struct tevent_context *ev,
878 struct cli_state *cli,
879 const char *dname)
881 struct tevent_req *req = NULL, *subreq = NULL;
882 struct cli_smb2_mkdir_state *state = NULL;
884 req = tevent_req_create(
885 mem_ctx, &state, struct cli_smb2_mkdir_state);
886 if (req == NULL) {
887 return NULL;
889 state->ev = ev;
890 state->cli = cli;
892 /* Ensure this is a directory. */
893 subreq = cli_smb2_create_fnum_send(
894 state, /* mem_ctx */
895 ev, /* ev */
896 cli, /* cli */
897 dname, /* fname */
898 (struct cli_smb2_create_flags){0}, /* create_flags */
899 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
900 FILE_READ_ATTRIBUTES, /* desired_access */
901 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
902 FILE_SHARE_READ|
903 FILE_SHARE_WRITE, /* share_access */
904 FILE_CREATE, /* create_disposition */
905 FILE_DIRECTORY_FILE, /* create_options */
906 NULL); /* in_cblobs */
907 if (tevent_req_nomem(subreq, req)) {
908 return tevent_req_post(req, ev);
910 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
911 return req;
914 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
916 struct tevent_req *req = tevent_req_callback_data(
917 subreq, struct tevent_req);
918 struct cli_smb2_mkdir_state *state = tevent_req_data(
919 req, struct cli_smb2_mkdir_state);
920 NTSTATUS status;
921 uint16_t fnum = 0xffff;
923 status = cli_smb2_create_fnum_recv(
924 subreq, &fnum, NULL, NULL, NULL, NULL);
925 TALLOC_FREE(subreq);
926 if (tevent_req_nterror(req, status)) {
927 return;
930 subreq =
931 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
932 if (tevent_req_nomem(subreq, req)) {
933 return;
935 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
938 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
940 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
941 tevent_req_simple_finish_ntstatus(subreq, status);
944 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
946 return tevent_req_simple_recv_ntstatus(req);
949 struct cli_smb2_rmdir_state {
950 struct tevent_context *ev;
951 struct cli_state *cli;
952 const char *dname;
953 const struct smb2_create_blobs *in_cblobs;
954 uint16_t fnum;
955 NTSTATUS status;
958 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
959 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
960 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
961 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
963 struct tevent_req *cli_smb2_rmdir_send(
964 TALLOC_CTX *mem_ctx,
965 struct tevent_context *ev,
966 struct cli_state *cli,
967 const char *dname,
968 const struct smb2_create_blobs *in_cblobs)
970 struct tevent_req *req = NULL, *subreq = NULL;
971 struct cli_smb2_rmdir_state *state = NULL;
973 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
974 if (req == NULL) {
975 return NULL;
977 state->ev = ev;
978 state->cli = cli;
979 state->dname = dname;
980 state->in_cblobs = in_cblobs;
982 subreq = cli_smb2_create_fnum_send(
983 state,
984 state->ev,
985 state->cli,
986 state->dname,
987 (struct cli_smb2_create_flags){0},
988 SMB2_IMPERSONATION_IMPERSONATION,
989 DELETE_ACCESS, /* desired_access */
990 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
991 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
992 FILE_OPEN, /* create_disposition */
993 FILE_DIRECTORY_FILE, /* create_options */
994 state->in_cblobs); /* in_cblobs */
995 if (tevent_req_nomem(subreq, req)) {
996 return tevent_req_post(req, ev);
998 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
999 return req;
1002 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
1004 struct tevent_req *req = tevent_req_callback_data(
1005 subreq, struct tevent_req);
1006 struct cli_smb2_rmdir_state *state = tevent_req_data(
1007 req, struct cli_smb2_rmdir_state);
1008 NTSTATUS status;
1010 status = cli_smb2_create_fnum_recv(
1011 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1012 TALLOC_FREE(subreq);
1014 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1016 * Naive option to match our SMB1 code. Assume the
1017 * symlink path that tripped us up was the last
1018 * component and try again. Eventually we will have to
1019 * deal with the returned path unprocessed component. JRA.
1021 subreq = cli_smb2_create_fnum_send(
1022 state,
1023 state->ev,
1024 state->cli,
1025 state->dname,
1026 (struct cli_smb2_create_flags){0},
1027 SMB2_IMPERSONATION_IMPERSONATION,
1028 DELETE_ACCESS, /* desired_access */
1029 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1030 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
1031 FILE_OPEN, /* create_disposition */
1032 FILE_DIRECTORY_FILE|
1033 FILE_DELETE_ON_CLOSE|
1034 FILE_OPEN_REPARSE_POINT, /* create_options */
1035 state->in_cblobs); /* in_cblobs */
1036 if (tevent_req_nomem(subreq, req)) {
1037 return;
1039 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
1040 return;
1043 if (tevent_req_nterror(req, status)) {
1044 return;
1047 subreq = cli_smb2_delete_on_close_send(
1048 state, state->ev, state->cli, state->fnum, true);
1049 if (tevent_req_nomem(subreq, req)) {
1050 return;
1052 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1055 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
1057 struct tevent_req *req = tevent_req_callback_data(
1058 subreq, struct tevent_req);
1059 struct cli_smb2_rmdir_state *state = tevent_req_data(
1060 req, struct cli_smb2_rmdir_state);
1061 NTSTATUS status;
1063 status = cli_smb2_create_fnum_recv(
1064 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1065 TALLOC_FREE(subreq);
1066 if (tevent_req_nterror(req, status)) {
1067 return;
1070 subreq = cli_smb2_delete_on_close_send(
1071 state, state->ev, state->cli, state->fnum, true);
1072 if (tevent_req_nomem(subreq, req)) {
1073 return;
1075 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1078 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1080 struct tevent_req *req = tevent_req_callback_data(
1081 subreq, struct tevent_req);
1082 struct cli_smb2_rmdir_state *state = tevent_req_data(
1083 req, struct cli_smb2_rmdir_state);
1085 state->status = cli_smb2_delete_on_close_recv(subreq);
1086 TALLOC_FREE(subreq);
1089 * Close the fd even if the set_disp failed
1092 subreq = cli_smb2_close_fnum_send(state,
1093 state->ev,
1094 state->cli,
1095 state->fnum,
1097 if (tevent_req_nomem(subreq, req)) {
1098 return;
1100 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1103 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1105 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1106 tevent_req_simple_finish_ntstatus(subreq, status);
1109 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1111 struct cli_smb2_rmdir_state *state = tevent_req_data(
1112 req, struct cli_smb2_rmdir_state);
1113 NTSTATUS status;
1115 if (tevent_req_is_nterror(req, &status)) {
1116 return status;
1118 return state->status;
1121 /***************************************************************
1122 Small wrapper that allows SMB2 to unlink a pathname.
1123 ***************************************************************/
1125 struct cli_smb2_unlink_state {
1126 struct tevent_context *ev;
1127 struct cli_state *cli;
1128 const char *fname;
1129 const struct smb2_create_blobs *in_cblobs;
1132 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1133 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1134 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1136 struct tevent_req *cli_smb2_unlink_send(
1137 TALLOC_CTX *mem_ctx,
1138 struct tevent_context *ev,
1139 struct cli_state *cli,
1140 const char *fname,
1141 const struct smb2_create_blobs *in_cblobs)
1143 struct tevent_req *req = NULL, *subreq = NULL;
1144 struct cli_smb2_unlink_state *state = NULL;
1146 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1147 if (req == NULL) {
1148 return NULL;
1150 state->ev = ev;
1151 state->cli = cli;
1152 state->fname = fname;
1153 state->in_cblobs = in_cblobs;
1155 subreq = cli_smb2_create_fnum_send(
1156 state, /* mem_ctx */
1157 state->ev, /* tevent_context */
1158 state->cli, /* cli_struct */
1159 state->fname, /* filename */
1160 (struct cli_smb2_create_flags){0},
1161 SMB2_IMPERSONATION_IMPERSONATION,
1162 DELETE_ACCESS, /* desired_access */
1163 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1164 FILE_SHARE_READ|
1165 FILE_SHARE_WRITE|
1166 FILE_SHARE_DELETE, /* share_access */
1167 FILE_OPEN, /* create_disposition */
1168 FILE_DELETE_ON_CLOSE, /* create_options */
1169 state->in_cblobs); /* in_cblobs */
1170 if (tevent_req_nomem(subreq, req)) {
1171 return tevent_req_post(req, ev);
1173 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1174 return req;
1177 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1179 struct tevent_req *req = tevent_req_callback_data(
1180 subreq, struct tevent_req);
1181 struct cli_smb2_unlink_state *state = tevent_req_data(
1182 req, struct cli_smb2_unlink_state);
1183 uint16_t fnum = 0xffff;
1184 NTSTATUS status;
1186 status = cli_smb2_create_fnum_recv(
1187 subreq, &fnum, NULL, NULL, NULL, NULL);
1188 TALLOC_FREE(subreq);
1190 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1191 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1193 * Naive option to match our SMB1 code. Assume the
1194 * symlink path that tripped us up was the last
1195 * component and try again. Eventually we will have to
1196 * deal with the returned path unprocessed component. JRA.
1198 subreq = cli_smb2_create_fnum_send(
1199 state, /* mem_ctx */
1200 state->ev, /* tevent_context */
1201 state->cli, /* cli_struct */
1202 state->fname, /* filename */
1203 (struct cli_smb2_create_flags){0},
1204 SMB2_IMPERSONATION_IMPERSONATION,
1205 DELETE_ACCESS, /* desired_access */
1206 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1207 FILE_SHARE_READ|
1208 FILE_SHARE_WRITE|
1209 FILE_SHARE_DELETE, /* share_access */
1210 FILE_OPEN, /* create_disposition */
1211 FILE_DELETE_ON_CLOSE|
1212 FILE_OPEN_REPARSE_POINT, /* create_options */
1213 state->in_cblobs); /* in_cblobs */
1214 if (tevent_req_nomem(subreq, req)) {
1215 return;
1217 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1218 return;
1221 if (tevent_req_nterror(req, status)) {
1222 return;
1225 subreq =
1226 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1227 if (tevent_req_nomem(subreq, req)) {
1228 return;
1230 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1233 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1235 struct tevent_req *req = tevent_req_callback_data(
1236 subreq, struct tevent_req);
1237 struct cli_smb2_unlink_state *state = tevent_req_data(
1238 req, struct cli_smb2_unlink_state);
1239 uint16_t fnum = 0xffff;
1240 NTSTATUS status;
1242 status = cli_smb2_create_fnum_recv(
1243 subreq, &fnum, NULL, NULL, NULL, NULL);
1244 TALLOC_FREE(subreq);
1245 if (tevent_req_nterror(req, status)) {
1246 return;
1249 subreq =
1250 cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1251 if (tevent_req_nomem(subreq, req)) {
1252 return;
1254 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1257 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1259 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1260 tevent_req_simple_finish_ntstatus(subreq, status);
1263 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1265 return tevent_req_simple_recv_ntstatus(req);
1268 /***************************************************************
1269 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1270 ***************************************************************/
1272 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1273 uint32_t dir_data_length,
1274 struct file_info *finfo,
1275 uint32_t *next_offset)
1277 struct smb3_file_posix_information info = {};
1278 size_t consumed;
1279 enum ndr_err_code ndr_err;
1280 size_t namelen = 0;
1281 size_t ret = 0;
1282 uint32_t _next_offset = 0;
1284 if (dir_data_length < 4) {
1285 return NT_STATUS_INFO_LENGTH_MISMATCH;
1288 _next_offset = IVAL(dir_data, 0);
1290 if (_next_offset > dir_data_length) {
1291 return NT_STATUS_INFO_LENGTH_MISMATCH;
1294 if (_next_offset != 0) {
1295 /* Ensure we only read what in this record. */
1296 dir_data_length = _next_offset;
1300 * Skip NextEntryOffset and FileIndex
1302 if (dir_data_length < 8) {
1303 return NT_STATUS_INFO_LENGTH_MISMATCH;
1305 dir_data += 8;
1306 dir_data_length -= 8;
1308 ndr_err = ndr_pull_struct_blob_noalloc(
1309 dir_data,
1310 dir_data_length,
1311 &info,
1312 (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1313 &consumed);
1314 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1315 return ndr_map_error2ntstatus(ndr_err);
1317 if (consumed > dir_data_length) {
1318 return NT_STATUS_INFO_LENGTH_MISMATCH;
1320 dir_data += consumed;
1321 dir_data_length -= consumed;
1323 finfo->btime_ts = interpret_long_date(info.creation_time);
1324 finfo->atime_ts = interpret_long_date(info.last_access_time);
1325 finfo->mtime_ts = interpret_long_date(info.last_write_time);
1326 finfo->ctime_ts = interpret_long_date(info.change_time);
1327 finfo->allocated_size = info.allocation_size;
1328 finfo->size = info.end_of_file;
1329 finfo->attr = info.file_attributes;
1330 finfo->ino = info.inode;
1331 finfo->st_ex_dev = info.device;
1332 finfo->st_ex_nlink = info.cc.nlinks;
1333 finfo->reparse_tag = info.cc.reparse_tag;
1334 finfo->st_ex_mode = wire_mode_to_unix(info.cc.posix_mode);
1335 sid_copy(&finfo->owner_sid, &info.cc.owner);
1336 sid_copy(&finfo->group_sid, &info.cc.group);
1338 if (dir_data_length < 4) {
1339 return NT_STATUS_INFO_LENGTH_MISMATCH;
1341 namelen = PULL_LE_U32(dir_data, 0);
1343 dir_data += 4;
1344 dir_data_length -= 4;
1346 if (namelen > dir_data_length) {
1347 return NT_STATUS_INFO_LENGTH_MISMATCH;
1350 ret = pull_string_talloc(finfo,
1351 dir_data,
1352 FLAGS2_UNICODE_STRINGS,
1353 &finfo->name,
1354 dir_data,
1355 namelen,
1356 STR_UNICODE);
1357 if (ret == (size_t)-1) {
1358 /* Bad conversion. */
1359 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1362 if (finfo->name == NULL) {
1363 /* Bad conversion. */
1364 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1367 *next_offset = _next_offset;
1368 return NT_STATUS_OK;
1371 /***************************************************************
1372 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1373 ***************************************************************/
1375 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1376 uint32_t dir_data_length,
1377 struct file_info *finfo,
1378 uint32_t *next_offset)
1380 size_t namelen = 0;
1381 size_t slen = 0;
1382 size_t ret = 0;
1384 if (dir_data_length < 4) {
1385 return NT_STATUS_INFO_LENGTH_MISMATCH;
1388 *next_offset = IVAL(dir_data, 0);
1390 if (*next_offset > dir_data_length) {
1391 return NT_STATUS_INFO_LENGTH_MISMATCH;
1394 if (*next_offset != 0) {
1395 /* Ensure we only read what in this record. */
1396 dir_data_length = *next_offset;
1399 if (dir_data_length < 105) {
1400 return NT_STATUS_INFO_LENGTH_MISMATCH;
1403 finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1404 finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1405 finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1406 finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1407 finfo->size = BVAL(dir_data + 40, 0);
1408 finfo->allocated_size = BVAL(dir_data + 48, 0);
1409 finfo->attr = IVAL(dir_data + 56, 0);
1410 finfo->ino = BVAL(dir_data + 96, 0);
1411 namelen = IVAL(dir_data + 60,0);
1412 if (namelen > (dir_data_length - 104)) {
1413 return NT_STATUS_INFO_LENGTH_MISMATCH;
1415 finfo->reparse_tag = IVAL(dir_data + 64, 0);
1416 slen = CVAL(dir_data + 68, 0);
1417 if (slen > 24) {
1418 return NT_STATUS_INFO_LENGTH_MISMATCH;
1420 ret = pull_string_talloc(finfo,
1421 dir_data,
1422 FLAGS2_UNICODE_STRINGS,
1423 &finfo->short_name,
1424 dir_data + 70,
1425 slen,
1426 STR_UNICODE);
1427 if (ret == (size_t)-1) {
1428 /* Bad conversion. */
1429 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1432 ret = pull_string_talloc(finfo,
1433 dir_data,
1434 FLAGS2_UNICODE_STRINGS,
1435 &finfo->name,
1436 dir_data + 104,
1437 namelen,
1438 STR_UNICODE);
1439 if (ret == (size_t)-1) {
1440 /* Bad conversion. */
1441 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1444 if (finfo->name == NULL) {
1445 /* Bad conversion. */
1446 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1449 return NT_STATUS_OK;
1452 /*******************************************************************
1453 Given a filename - get its directory name
1454 ********************************************************************/
1456 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1457 const char *dir,
1458 char **parent,
1459 const char **name)
1461 char *p;
1462 ptrdiff_t len;
1464 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1466 if (p == NULL) {
1467 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1468 return false;
1470 if (name) {
1471 *name = dir;
1473 return true;
1476 len = p-dir;
1478 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1479 return false;
1481 (*parent)[len] = '\0';
1483 if (name) {
1484 *name = p+1;
1486 return true;
1489 struct cli_smb2_list_dir_data {
1490 uint8_t *data;
1491 uint32_t length;
1494 struct cli_smb2_list_state {
1495 struct tevent_context *ev;
1496 struct cli_state *cli;
1497 const char *mask;
1499 uint16_t fnum;
1501 NTSTATUS status;
1502 struct cli_smb2_list_dir_data *response;
1503 uint32_t offset;
1504 unsigned int info_level;
1507 static void cli_smb2_list_opened(struct tevent_req *subreq);
1508 static void cli_smb2_list_done(struct tevent_req *subreq);
1509 static void cli_smb2_list_closed(struct tevent_req *subreq);
1511 struct tevent_req *cli_smb2_list_send(
1512 TALLOC_CTX *mem_ctx,
1513 struct tevent_context *ev,
1514 struct cli_state *cli,
1515 const char *pathname,
1516 unsigned int info_level)
1518 struct tevent_req *req = NULL, *subreq = NULL;
1519 struct cli_smb2_list_state *state = NULL;
1520 char *parent = NULL;
1521 bool ok;
1522 struct smb2_create_blobs *in_cblobs = NULL;
1524 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1525 if (req == NULL) {
1526 return NULL;
1528 state->ev = ev;
1529 state->cli = cli;
1530 state->status = NT_STATUS_OK;
1531 state->info_level = info_level;
1533 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1534 if (!ok) {
1535 tevent_req_oom(req);
1536 return tevent_req_post(req, ev);
1539 if (smbXcli_conn_have_posix(cli->conn) &&
1540 info_level == SMB2_FIND_POSIX_INFORMATION)
1542 NTSTATUS status;
1544 /* The mode MUST be 0 when opening an existing file/dir, and
1545 * will be ignored by the server.
1547 uint8_t linear_mode[4] = { 0 };
1548 DATA_BLOB blob = { .data=linear_mode,
1549 .length=sizeof(linear_mode) };
1551 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1552 if (in_cblobs == NULL) {
1553 return NULL;
1556 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1557 SMB2_CREATE_TAG_POSIX, blob);
1558 if (tevent_req_nterror(req, status)) {
1559 tevent_req_nterror(req, status);
1560 return tevent_req_post(req, ev);
1564 subreq = cli_smb2_create_fnum_send(
1565 state, /* mem_ctx */
1566 ev, /* ev */
1567 cli, /* cli */
1568 parent, /* fname */
1569 (struct cli_smb2_create_flags){0}, /* create_flags */
1570 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1571 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1572 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1573 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1574 FILE_OPEN, /* create_disposition */
1575 FILE_DIRECTORY_FILE, /* create_options */
1576 in_cblobs); /* in_cblobs */
1577 TALLOC_FREE(in_cblobs);
1578 if (tevent_req_nomem(subreq, req)) {
1579 return tevent_req_post(req, ev);
1581 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1582 return req;
1585 static void cli_smb2_list_opened(struct tevent_req *subreq)
1587 struct tevent_req *req = tevent_req_callback_data(
1588 subreq, struct tevent_req);
1589 struct cli_smb2_list_state *state = tevent_req_data(
1590 req, struct cli_smb2_list_state);
1591 NTSTATUS status;
1593 status = cli_smb2_create_fnum_recv(
1594 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1595 TALLOC_FREE(subreq);
1596 if (tevent_req_nterror(req, status)) {
1597 return;
1601 * Make our caller get back to us via cli_smb2_list_recv(),
1602 * triggering the smb2_query_directory_send()
1604 tevent_req_defer_callback(req, state->ev);
1605 tevent_req_notify_callback(req);
1608 static void cli_smb2_list_done(struct tevent_req *subreq)
1610 struct tevent_req *req = tevent_req_callback_data(
1611 subreq, struct tevent_req);
1612 struct cli_smb2_list_state *state = tevent_req_data(
1613 req, struct cli_smb2_list_state);
1614 struct cli_smb2_list_dir_data *response = NULL;
1616 response = talloc(state, struct cli_smb2_list_dir_data);
1617 if (tevent_req_nomem(response, req)) {
1618 return;
1621 state->status = smb2cli_query_directory_recv(
1622 subreq, response, &response->data, &response->length);
1623 TALLOC_FREE(subreq);
1625 if (NT_STATUS_IS_OK(state->status)) {
1626 state->response = response;
1627 state->offset = 0;
1629 tevent_req_defer_callback(req, state->ev);
1630 tevent_req_notify_callback(req);
1631 return;
1634 TALLOC_FREE(response);
1636 subreq = cli_smb2_close_fnum_send(state,
1637 state->ev,
1638 state->cli,
1639 state->fnum,
1641 if (tevent_req_nomem(subreq, req)) {
1642 return;
1644 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1647 static void cli_smb2_list_closed(struct tevent_req *subreq)
1649 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1650 tevent_req_simple_finish_ntstatus(subreq, status);
1654 * Return the next finfo directory.
1656 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1657 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1658 * NT_STATUS_RETRY, which will then trigger the caller again when the
1659 * QUERY_DIRECTORY has returned with another buffer. This way we
1660 * guarantee that no asynchronous request is open after this call
1661 * returns an entry, so that other synchronous requests can be issued
1662 * on the same connection while the directory listing proceeds.
1664 NTSTATUS cli_smb2_list_recv(
1665 struct tevent_req *req,
1666 TALLOC_CTX *mem_ctx,
1667 struct file_info **pfinfo)
1669 struct cli_smb2_list_state *state = tevent_req_data(
1670 req, struct cli_smb2_list_state);
1671 struct cli_smb2_list_dir_data *response = NULL;
1672 struct file_info *finfo = NULL;
1673 NTSTATUS status;
1674 uint32_t next_offset = 0;
1675 bool in_progress;
1677 in_progress = tevent_req_is_in_progress(req);
1679 if (!in_progress) {
1680 if (!tevent_req_is_nterror(req, &status)) {
1681 status = NT_STATUS_NO_MORE_FILES;
1683 goto fail;
1686 response = state->response;
1687 if (response == NULL) {
1688 struct tevent_req *subreq = NULL;
1689 struct cli_state *cli = state->cli;
1690 struct smb2_hnd *ph = NULL;
1691 uint32_t max_trans, max_avail_len;
1692 bool ok;
1694 if (!NT_STATUS_IS_OK(state->status)) {
1695 status = state->status;
1696 goto fail;
1699 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1700 if (!NT_STATUS_IS_OK(status)) {
1701 goto fail;
1704 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1705 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1706 if (ok) {
1707 max_trans = MIN(max_trans, max_avail_len);
1710 subreq = smb2cli_query_directory_send(
1711 state, /* mem_ctx */
1712 state->ev, /* ev */
1713 cli->conn, /* conn */
1714 cli->timeout, /* timeout_msec */
1715 cli->smb2.session, /* session */
1716 cli->smb2.tcon, /* tcon */
1717 state->info_level, /* level */
1718 0, /* flags */
1719 0, /* file_index */
1720 ph->fid_persistent, /* fid_persistent */
1721 ph->fid_volatile, /* fid_volatile */
1722 state->mask, /* mask */
1723 max_trans); /* outbuf_len */
1724 if (subreq == NULL) {
1725 status = NT_STATUS_NO_MEMORY;
1726 goto fail;
1728 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1729 return NT_STATUS_RETRY;
1732 SMB_ASSERT(response->length > state->offset);
1734 finfo = talloc_zero(mem_ctx, struct file_info);
1735 if (finfo == NULL) {
1736 status = NT_STATUS_NO_MEMORY;
1737 goto fail;
1740 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1741 status = parse_finfo_posix_info(
1742 response->data + state->offset,
1743 response->length - state->offset,
1744 finfo,
1745 &next_offset);
1746 } else {
1747 status = parse_finfo_id_both_directory_info(
1748 response->data + state->offset,
1749 response->length - state->offset,
1750 finfo,
1751 &next_offset);
1753 if (!NT_STATUS_IS_OK(status)) {
1754 goto fail;
1757 status = is_bad_finfo_name(state->cli, finfo);
1758 if (!NT_STATUS_IS_OK(status)) {
1759 goto fail;
1763 * parse_finfo_id_both_directory_info() checks for overflow,
1764 * no need to check again here.
1766 state->offset += next_offset;
1768 if (next_offset == 0) {
1769 TALLOC_FREE(state->response);
1772 tevent_req_defer_callback(req, state->ev);
1773 tevent_req_notify_callback(req);
1775 *pfinfo = finfo;
1776 return NT_STATUS_OK;
1778 fail:
1779 TALLOC_FREE(finfo);
1780 tevent_req_received(req);
1781 return status;
1784 /***************************************************************
1785 Wrapper that allows SMB2 to query a path info (basic level).
1786 Synchronous only.
1787 ***************************************************************/
1789 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1790 const char *name,
1791 SMB_STRUCT_STAT *sbuf,
1792 uint32_t *attributes)
1794 NTSTATUS status;
1795 struct smb_create_returns cr;
1796 uint16_t fnum = 0xffff;
1797 size_t namelen = strlen(name);
1799 if (smbXcli_conn_has_async_calls(cli->conn)) {
1801 * Can't use sync call while an async call is in flight
1803 return NT_STATUS_INVALID_PARAMETER;
1806 /* SMB2 is pickier about pathnames. Ensure it doesn't
1807 end in a '\' */
1808 if (namelen > 0 && name[namelen-1] == '\\') {
1809 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1810 if (modname == NULL) {
1811 return NT_STATUS_NO_MEMORY;
1813 name = modname;
1816 /* This is commonly used as a 'cd'. Try qpathinfo on
1817 a directory handle first. */
1819 status = cli_smb2_create_fnum(cli,
1820 name,
1821 (struct cli_smb2_create_flags){0},
1822 SMB2_IMPERSONATION_IMPERSONATION,
1823 FILE_READ_ATTRIBUTES, /* desired_access */
1824 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1825 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1826 FILE_OPEN, /* create_disposition */
1827 FILE_DIRECTORY_FILE, /* create_options */
1828 NULL,
1829 &fnum,
1830 &cr,
1831 NULL,
1832 NULL);
1834 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1835 /* Maybe a file ? */
1836 status = cli_smb2_create_fnum(cli,
1837 name,
1838 (struct cli_smb2_create_flags){0},
1839 SMB2_IMPERSONATION_IMPERSONATION,
1840 FILE_READ_ATTRIBUTES, /* desired_access */
1841 0, /* file attributes */
1842 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1843 FILE_OPEN, /* create_disposition */
1844 0, /* create_options */
1845 NULL,
1846 &fnum,
1847 &cr,
1848 NULL,
1849 NULL);
1852 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1853 /* Maybe a reparse point ? */
1854 status = cli_smb2_create_fnum(cli,
1855 name,
1856 (struct cli_smb2_create_flags){0},
1857 SMB2_IMPERSONATION_IMPERSONATION,
1858 FILE_READ_ATTRIBUTES, /* desired_access */
1859 0, /* file attributes */
1860 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1861 FILE_OPEN, /* create_disposition */
1862 FILE_OPEN_REPARSE_POINT, /* create_options */
1863 NULL,
1864 &fnum,
1865 &cr,
1866 NULL,
1867 NULL);
1870 if (!NT_STATUS_IS_OK(status)) {
1871 return status;
1874 status = cli_smb2_close_fnum(cli, fnum);
1876 ZERO_STRUCTP(sbuf);
1878 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1879 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1880 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1881 sbuf->st_ex_size = cr.end_of_file;
1882 *attributes = cr.file_attributes;
1884 return status;
1887 struct cli_smb2_query_info_fnum_state {
1888 DATA_BLOB outbuf;
1891 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1893 struct tevent_req *cli_smb2_query_info_fnum_send(
1894 TALLOC_CTX *mem_ctx,
1895 struct tevent_context *ev,
1896 struct cli_state *cli,
1897 uint16_t fnum,
1898 uint8_t in_info_type,
1899 uint8_t in_info_class,
1900 uint32_t in_max_output_length,
1901 const DATA_BLOB *in_input_buffer,
1902 uint32_t in_additional_info,
1903 uint32_t in_flags)
1905 struct tevent_req *req = NULL, *subreq = NULL;
1906 struct cli_smb2_query_info_fnum_state *state = NULL;
1907 struct smb2_hnd *ph = NULL;
1908 NTSTATUS status;
1910 req = tevent_req_create(
1911 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1912 if (req == NULL) {
1913 return req;
1916 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1917 if (tevent_req_nterror(req, status)) {
1918 return tevent_req_post(req, ev);
1921 subreq = smb2cli_query_info_send(
1922 state,
1924 cli->conn,
1925 cli->timeout,
1926 cli->smb2.session,
1927 cli->smb2.tcon,
1928 in_info_type,
1929 in_info_class,
1930 in_max_output_length,
1931 in_input_buffer,
1932 in_additional_info,
1933 in_flags,
1934 ph->fid_persistent,
1935 ph->fid_volatile);
1936 if (tevent_req_nomem(subreq, req)) {
1937 return tevent_req_post(req, ev);
1939 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1940 return req;
1943 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1945 struct tevent_req *req = tevent_req_callback_data(
1946 subreq, struct tevent_req);
1947 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1948 req, struct cli_smb2_query_info_fnum_state);
1949 DATA_BLOB outbuf;
1950 NTSTATUS status;
1952 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1953 TALLOC_FREE(subreq);
1954 if (tevent_req_nterror(req, status)) {
1955 return;
1959 * We have to dup the memory here because outbuf.data is not
1960 * returned as a talloc object by smb2cli_query_info_recv.
1961 * It's a pointer into the received buffer.
1963 state->outbuf = data_blob_dup_talloc(state, outbuf);
1965 if ((outbuf.length != 0) &&
1966 tevent_req_nomem(state->outbuf.data, req)) {
1967 return;
1969 tevent_req_done(req);
1972 NTSTATUS cli_smb2_query_info_fnum_recv(
1973 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1975 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1976 req, struct cli_smb2_query_info_fnum_state);
1977 NTSTATUS status;
1979 if (tevent_req_is_nterror(req, &status)) {
1980 return status;
1982 *outbuf = (DATA_BLOB) {
1983 .data = talloc_move(mem_ctx, &state->outbuf.data),
1984 .length = state->outbuf.length,
1986 tevent_req_received(req);
1987 return NT_STATUS_OK;
1990 NTSTATUS cli_smb2_query_info_fnum(
1991 struct cli_state *cli,
1992 uint16_t fnum,
1993 uint8_t in_info_type,
1994 uint8_t in_info_class,
1995 uint32_t in_max_output_length,
1996 const DATA_BLOB *in_input_buffer,
1997 uint32_t in_additional_info,
1998 uint32_t in_flags,
1999 TALLOC_CTX *mem_ctx,
2000 DATA_BLOB *outbuf)
2002 TALLOC_CTX *frame = talloc_stackframe();
2003 struct tevent_context *ev = NULL;
2004 struct tevent_req *req = NULL;
2005 NTSTATUS status = NT_STATUS_NO_MEMORY;
2006 bool ok;
2008 if (smbXcli_conn_has_async_calls(cli->conn)) {
2010 * Can't use sync call while an async call is in flight
2012 status = NT_STATUS_INVALID_PARAMETER;
2013 goto fail;
2015 ev = samba_tevent_context_init(frame);
2016 if (ev == NULL) {
2017 goto fail;
2019 req = cli_smb2_query_info_fnum_send(
2020 frame,
2022 cli,
2023 fnum,
2024 in_info_type,
2025 in_info_class,
2026 in_max_output_length,
2027 in_input_buffer,
2028 in_additional_info,
2029 in_flags);
2030 if (req == NULL) {
2031 goto fail;
2033 ok = tevent_req_poll_ntstatus(req, ev, &status);
2034 if (!ok) {
2035 goto fail;
2037 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
2038 fail:
2039 TALLOC_FREE(frame);
2040 return status;
2043 /***************************************************************
2044 Helper function for pathname operations.
2045 ***************************************************************/
2047 struct get_fnum_from_path_state {
2048 struct tevent_context *ev;
2049 struct cli_state *cli;
2050 const char *name;
2051 uint32_t desired_access;
2052 uint16_t fnum;
2055 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
2056 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
2057 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
2059 static struct tevent_req *get_fnum_from_path_send(
2060 TALLOC_CTX *mem_ctx,
2061 struct tevent_context *ev,
2062 struct cli_state *cli,
2063 const char *name,
2064 uint32_t desired_access)
2066 struct tevent_req *req = NULL, *subreq = NULL;
2067 struct get_fnum_from_path_state *state = NULL;
2068 size_t namelen = strlen(name);
2070 req = tevent_req_create(
2071 mem_ctx, &state, struct get_fnum_from_path_state);
2072 if (req == NULL) {
2073 return NULL;
2075 state->ev = ev;
2076 state->cli = cli;
2077 state->name = name;
2078 state->desired_access = desired_access;
2081 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2082 * '\'
2084 if (namelen > 0 && name[namelen-1] == '\\') {
2085 state->name = talloc_strndup(state, name, namelen-1);
2086 if (tevent_req_nomem(state->name, req)) {
2087 return tevent_req_post(req, ev);
2091 subreq = cli_smb2_create_fnum_send(
2092 state, /* mem_ctx, */
2093 ev, /* ev */
2094 cli, /* cli */
2095 state->name, /* fname */
2096 (struct cli_smb2_create_flags){0}, /* create_flags */
2097 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2098 desired_access, /* desired_access */
2099 0, /* file_attributes */
2100 FILE_SHARE_READ|
2101 FILE_SHARE_WRITE|
2102 FILE_SHARE_DELETE, /* share_access */
2103 FILE_OPEN, /* create_disposition */
2104 0, /* create_options */
2105 NULL); /* in_cblobs */
2106 if (tevent_req_nomem(subreq, req)) {
2107 return tevent_req_post(req, ev);
2109 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2110 return req;
2113 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2115 struct tevent_req *req = tevent_req_callback_data(
2116 subreq, struct tevent_req);
2117 struct get_fnum_from_path_state *state = tevent_req_data(
2118 req, struct get_fnum_from_path_state);
2119 NTSTATUS status;
2121 status = cli_smb2_create_fnum_recv(
2122 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2123 TALLOC_FREE(subreq);
2125 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2126 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2128 * Naive option to match our SMB1 code. Assume the
2129 * symlink path that tripped us up was the last
2130 * component and try again. Eventually we will have to
2131 * deal with the returned path unprocessed component. JRA.
2133 subreq = cli_smb2_create_fnum_send(
2134 state, /* mem_ctx, */
2135 state->ev, /* ev */
2136 state->cli, /* cli */
2137 state->name, /* fname */
2138 (struct cli_smb2_create_flags){0}, /* create_flags */
2139 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2140 state->desired_access, /* desired_access */
2141 0, /* file_attributes */
2142 FILE_SHARE_READ|
2143 FILE_SHARE_WRITE|
2144 FILE_SHARE_DELETE, /* share_access */
2145 FILE_OPEN, /* create_disposition */
2146 FILE_OPEN_REPARSE_POINT, /* create_options */
2147 NULL); /* in_cblobs */
2148 if (tevent_req_nomem(subreq, req)) {
2149 return;
2151 tevent_req_set_callback(
2152 subreq, get_fnum_from_path_opened_reparse, req);
2153 return;
2156 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2157 subreq = cli_smb2_create_fnum_send(
2158 state, /* mem_ctx, */
2159 state->ev, /* ev */
2160 state->cli, /* cli */
2161 state->name, /* fname */
2162 (struct cli_smb2_create_flags){0}, /* create_flags */
2163 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2164 state->desired_access, /* desired_access */
2165 0, /* file_attributes */
2166 FILE_SHARE_READ|
2167 FILE_SHARE_WRITE|
2168 FILE_SHARE_DELETE, /* share_access */
2169 FILE_OPEN, /* create_disposition */
2170 FILE_DIRECTORY_FILE, /* create_options */
2171 NULL); /* in_cblobs */
2172 if (tevent_req_nomem(subreq, req)) {
2173 return;
2175 tevent_req_set_callback(
2176 subreq, get_fnum_from_path_opened_dir, req);
2177 return;
2180 if (tevent_req_nterror(req, status)) {
2181 return;
2183 tevent_req_done(req);
2186 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2188 struct tevent_req *req = tevent_req_callback_data(
2189 subreq, struct tevent_req);
2190 struct get_fnum_from_path_state *state = tevent_req_data(
2191 req, struct get_fnum_from_path_state);
2192 NTSTATUS status = cli_smb2_create_fnum_recv(
2193 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2194 tevent_req_simple_finish_ntstatus(subreq, status);
2197 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2199 /* Abstraction violation, but these two are just the same... */
2200 get_fnum_from_path_opened_reparse(subreq);
2203 static NTSTATUS get_fnum_from_path_recv(
2204 struct tevent_req *req, uint16_t *pfnum)
2206 struct get_fnum_from_path_state *state = tevent_req_data(
2207 req, struct get_fnum_from_path_state);
2208 NTSTATUS status = NT_STATUS_OK;
2210 if (!tevent_req_is_nterror(req, &status)) {
2211 *pfnum = state->fnum;
2213 tevent_req_received(req);
2214 return status;
2217 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2218 const char *name,
2219 uint32_t desired_access,
2220 uint16_t *pfnum)
2222 TALLOC_CTX *frame = talloc_stackframe();
2223 struct tevent_context *ev = NULL;
2224 struct tevent_req *req = NULL;
2225 NTSTATUS status = NT_STATUS_NO_MEMORY;
2227 if (smbXcli_conn_has_async_calls(cli->conn)) {
2228 status = NT_STATUS_INVALID_PARAMETER;
2229 goto fail;
2231 ev = samba_tevent_context_init(frame);
2232 if (ev == NULL) {
2233 goto fail;
2235 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2236 if (req == NULL) {
2237 goto fail;
2239 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2240 goto fail;
2242 status = get_fnum_from_path_recv(req, pfnum);
2243 fail:
2244 TALLOC_FREE(frame);
2245 return status;
2248 struct cli_smb2_qpathinfo_state {
2249 struct tevent_context *ev;
2250 struct cli_state *cli;
2251 const char *fname;
2252 uint16_t fnum;
2253 uint16_t level;
2254 uint32_t min_rdata;
2255 uint32_t max_rdata;
2257 NTSTATUS status;
2258 DATA_BLOB out;
2261 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2262 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2263 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2265 struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2266 struct tevent_context *ev,
2267 struct cli_state *cli,
2268 const char *fname,
2269 uint16_t level,
2270 uint32_t min_rdata,
2271 uint32_t max_rdata)
2273 struct tevent_req *req = NULL, *subreq = NULL;
2274 struct cli_smb2_qpathinfo_state *state = NULL;
2276 req = tevent_req_create(mem_ctx,
2277 &state,
2278 struct cli_smb2_qpathinfo_state);
2279 if (req == NULL) {
2280 return NULL;
2282 state->ev = ev;
2283 state->cli = cli;
2284 state->level = level;
2285 state->min_rdata = min_rdata;
2286 state->max_rdata = max_rdata;
2288 subreq = get_fnum_from_path_send(state,
2290 cli,
2291 fname,
2292 FILE_READ_ATTRIBUTES);
2293 if (tevent_req_nomem(subreq, req)) {
2294 return tevent_req_post(req, ev);
2296 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2297 return req;
2300 static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2302 struct tevent_req *req =
2303 tevent_req_callback_data(subreq, struct tevent_req);
2304 struct cli_smb2_qpathinfo_state *state =
2305 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2306 NTSTATUS status;
2308 status = get_fnum_from_path_recv(subreq, &state->fnum);
2309 TALLOC_FREE(subreq);
2310 if (tevent_req_nterror(req, status)) {
2311 return;
2314 subreq = cli_smb2_query_info_fnum_send(state,
2315 state->ev,
2316 state->cli,
2317 state->fnum,
2318 SMB2_0_INFO_FILE,
2319 state->level,
2320 state->max_rdata,
2321 NULL, /* in_input_buffer */
2322 0, /* in_additional_info */
2323 0); /* in_flags */
2324 if (tevent_req_nomem(subreq, req)) {
2325 return;
2327 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2330 static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2332 struct tevent_req *req =
2333 tevent_req_callback_data(subreq, struct tevent_req);
2334 struct cli_smb2_qpathinfo_state *state =
2335 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2337 state->status =
2338 cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2339 TALLOC_FREE(subreq);
2341 if (NT_STATUS_IS_OK(state->status) &&
2342 (state->out.length < state->min_rdata)) {
2343 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2346 subreq = cli_smb2_close_fnum_send(state,
2347 state->ev,
2348 state->cli,
2349 state->fnum,
2351 if (tevent_req_nomem(subreq, req)) {
2352 return;
2354 tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2357 static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2359 struct tevent_req *req =
2360 tevent_req_callback_data(subreq, struct tevent_req);
2361 struct cli_smb2_qpathinfo_state *state =
2362 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2363 NTSTATUS status;
2365 status = cli_smb2_close_fnum_recv(subreq);
2366 TALLOC_FREE(subreq);
2367 if (tevent_req_nterror(req, status)) {
2368 return;
2370 if (tevent_req_nterror(req, state->status)) {
2371 return;
2373 tevent_req_done(req);
2376 NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2377 TALLOC_CTX *mem_ctx,
2378 uint8_t **rdata,
2379 uint32_t *num_rdata)
2381 struct cli_smb2_qpathinfo_state *state =
2382 tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2383 NTSTATUS status;
2385 if (tevent_req_is_nterror(req, &status)) {
2386 return status;
2389 *rdata = talloc_move(mem_ctx, &state->out.data);
2390 *num_rdata = state->out.length;
2391 tevent_req_received(req);
2392 return NT_STATUS_OK;
2395 /***************************************************************
2396 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2397 a pathname.
2398 Synchronous only.
2399 ***************************************************************/
2401 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2402 const char *name,
2403 uint8_t in_info_type,
2404 uint8_t in_file_info_class,
2405 const DATA_BLOB *p_in_data)
2407 NTSTATUS status;
2408 uint16_t fnum = 0xffff;
2409 TALLOC_CTX *frame = talloc_stackframe();
2411 if (smbXcli_conn_has_async_calls(cli->conn)) {
2413 * Can't use sync call while an async call is in flight
2415 status = NT_STATUS_INVALID_PARAMETER;
2416 goto fail;
2419 status = get_fnum_from_path(cli,
2420 name,
2421 FILE_WRITE_ATTRIBUTES,
2422 &fnum);
2424 if (!NT_STATUS_IS_OK(status)) {
2425 goto fail;
2428 status = cli_smb2_set_info_fnum(
2429 cli,
2430 fnum,
2431 in_info_type,
2432 in_file_info_class,
2433 p_in_data, /* in_input_buffer */
2434 0); /* in_additional_info */
2435 fail:
2437 if (fnum != 0xffff) {
2438 cli_smb2_close_fnum(cli, fnum);
2441 TALLOC_FREE(frame);
2442 return status;
2446 /***************************************************************
2447 Wrapper that allows SMB2 to set pathname attributes.
2448 Synchronous only.
2449 ***************************************************************/
2451 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2452 const char *name,
2453 uint32_t attr,
2454 time_t mtime)
2456 uint8_t inbuf_store[40];
2457 DATA_BLOB inbuf = data_blob_null;
2459 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2460 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2462 inbuf.data = inbuf_store;
2463 inbuf.length = sizeof(inbuf_store);
2464 data_blob_clear(&inbuf);
2467 * SMB1 uses attr == 0 to clear all attributes
2468 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2469 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2470 * request attribute change.
2472 * SMB2 uses exactly the reverse. Unfortunately as the
2473 * cli_setatr() ABI is exposed inside libsmbclient,
2474 * we must make the SMB2 cli_smb2_setatr() call
2475 * export the same ABI as the SMB1 cli_setatr()
2476 * which calls it. This means reversing the sense
2477 * of the requested attr argument if it's zero
2478 * or FILE_ATTRIBUTE_NORMAL.
2480 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2483 if (attr == 0) {
2484 attr = FILE_ATTRIBUTE_NORMAL;
2485 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2486 attr = 0;
2489 SIVAL(inbuf.data, 32, attr);
2490 if (mtime != 0) {
2491 put_long_date((char *)inbuf.data + 16,mtime);
2493 /* Set all the other times to -1. */
2494 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2495 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2496 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2498 return cli_smb2_setpathinfo(
2499 cli,
2500 name,
2501 SMB2_0_INFO_FILE, /* in_info_type */
2502 FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2503 &inbuf);
2507 /***************************************************************
2508 Wrapper that allows SMB2 to set file handle times.
2509 Synchronous only.
2510 ***************************************************************/
2512 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2513 uint16_t fnum,
2514 time_t change_time,
2515 time_t access_time,
2516 time_t write_time)
2518 uint8_t inbuf_store[40];
2519 DATA_BLOB inbuf = data_blob_null;
2520 NTSTATUS status;
2522 if (smbXcli_conn_has_async_calls(cli->conn)) {
2524 * Can't use sync call while an async call is in flight
2526 return NT_STATUS_INVALID_PARAMETER;
2529 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2530 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2532 inbuf.data = inbuf_store;
2533 inbuf.length = sizeof(inbuf_store);
2534 data_blob_clear(&inbuf);
2536 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2537 if (change_time != 0) {
2538 put_long_date((char *)inbuf.data + 24, change_time);
2540 if (access_time != 0) {
2541 put_long_date((char *)inbuf.data + 8, access_time);
2543 if (write_time != 0) {
2544 put_long_date((char *)inbuf.data + 16, write_time);
2547 status = cli_smb2_set_info_fnum(
2548 cli,
2549 fnum,
2550 SMB2_0_INFO_FILE, /* in_info_type */
2551 FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2552 &inbuf, /* in_input_buffer */
2553 0); /* in_additional_info */
2554 return status;
2557 /***************************************************************
2558 Wrapper that allows SMB2 to query disk attributes (size).
2559 Synchronous only.
2560 ***************************************************************/
2562 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2563 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2565 NTSTATUS status;
2566 uint16_t fnum = 0xffff;
2567 DATA_BLOB outbuf = data_blob_null;
2568 uint32_t sectors_per_unit = 0;
2569 uint32_t bytes_per_sector = 0;
2570 uint64_t total_size = 0;
2571 uint64_t size_free = 0;
2572 TALLOC_CTX *frame = talloc_stackframe();
2574 if (smbXcli_conn_has_async_calls(cli->conn)) {
2576 * Can't use sync call while an async call is in flight
2578 status = NT_STATUS_INVALID_PARAMETER;
2579 goto fail;
2582 /* First open the top level directory. */
2583 status = cli_smb2_create_fnum(cli,
2584 path,
2585 (struct cli_smb2_create_flags){0},
2586 SMB2_IMPERSONATION_IMPERSONATION,
2587 FILE_READ_ATTRIBUTES, /* desired_access */
2588 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2589 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2590 FILE_OPEN, /* create_disposition */
2591 FILE_DIRECTORY_FILE, /* create_options */
2592 NULL,
2593 &fnum,
2594 NULL,
2595 NULL,
2596 NULL);
2598 if (!NT_STATUS_IS_OK(status)) {
2599 goto fail;
2602 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2603 level 3 (SMB_FS_SIZE_INFORMATION). */
2605 status = cli_smb2_query_info_fnum(
2606 cli,
2607 fnum,
2608 2, /* in_info_type */
2609 3, /* in_file_info_class */
2610 0xFFFF, /* in_max_output_length */
2611 NULL, /* in_input_buffer */
2612 0, /* in_additional_info */
2613 0, /* in_flags */
2614 frame,
2615 &outbuf);
2616 if (!NT_STATUS_IS_OK(status)) {
2617 goto fail;
2620 /* Parse the reply. */
2621 if (outbuf.length != 24) {
2622 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2623 goto fail;
2626 total_size = BVAL(outbuf.data, 0);
2627 size_free = BVAL(outbuf.data, 8);
2628 sectors_per_unit = IVAL(outbuf.data, 16);
2629 bytes_per_sector = IVAL(outbuf.data, 20);
2631 if (bsize) {
2632 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2634 if (total) {
2635 *total = total_size;
2637 if (avail) {
2638 *avail = size_free;
2641 status = NT_STATUS_OK;
2643 fail:
2645 if (fnum != 0xffff) {
2646 cli_smb2_close_fnum(cli, fnum);
2649 TALLOC_FREE(frame);
2650 return status;
2653 /***************************************************************
2654 Wrapper that allows SMB2 to query file system sizes.
2655 Synchronous only.
2656 ***************************************************************/
2658 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2659 uint64_t *total_allocation_units,
2660 uint64_t *caller_allocation_units,
2661 uint64_t *actual_allocation_units,
2662 uint64_t *sectors_per_allocation_unit,
2663 uint64_t *bytes_per_sector)
2665 NTSTATUS status;
2666 uint16_t fnum = 0xffff;
2667 DATA_BLOB outbuf = data_blob_null;
2668 TALLOC_CTX *frame = talloc_stackframe();
2670 if (smbXcli_conn_has_async_calls(cli->conn)) {
2672 * Can't use sync call while an async call is in flight
2674 status = NT_STATUS_INVALID_PARAMETER;
2675 goto fail;
2678 /* First open the top level directory. */
2679 status =
2680 cli_smb2_create_fnum(cli, "",
2681 (struct cli_smb2_create_flags){0},
2682 SMB2_IMPERSONATION_IMPERSONATION,
2683 FILE_READ_ATTRIBUTES, /* desired_access */
2684 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2685 FILE_SHARE_READ | FILE_SHARE_WRITE |
2686 FILE_SHARE_DELETE, /* share_access */
2687 FILE_OPEN, /* create_disposition */
2688 FILE_DIRECTORY_FILE, /* create_options */
2689 NULL,
2690 &fnum,
2691 NULL,
2692 NULL,
2693 NULL);
2695 if (!NT_STATUS_IS_OK(status)) {
2696 goto fail;
2699 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2700 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2702 status = cli_smb2_query_info_fnum(
2703 cli,
2704 fnum,
2705 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2706 FSCC_FS_FULL_SIZE_INFORMATION, /* in_file_info_class */
2707 0xFFFF, /* in_max_output_length */
2708 NULL, /* in_input_buffer */
2709 0, /* in_additional_info */
2710 0, /* in_flags */
2711 frame,
2712 &outbuf);
2713 if (!NT_STATUS_IS_OK(status)) {
2714 goto fail;
2717 if (outbuf.length < 32) {
2718 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2719 goto fail;
2722 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2723 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2724 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2725 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2726 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2728 fail:
2730 if (fnum != 0xffff) {
2731 cli_smb2_close_fnum(cli, fnum);
2734 TALLOC_FREE(frame);
2735 return status;
2738 /***************************************************************
2739 Wrapper that allows SMB2 to query file system attributes.
2740 Synchronous only.
2741 ***************************************************************/
2743 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2745 NTSTATUS status;
2746 uint16_t fnum = 0xffff;
2747 DATA_BLOB outbuf = data_blob_null;
2748 TALLOC_CTX *frame = talloc_stackframe();
2750 if (smbXcli_conn_has_async_calls(cli->conn)) {
2752 * Can't use sync call while an async call is in flight
2754 status = NT_STATUS_INVALID_PARAMETER;
2755 goto fail;
2758 /* First open the top level directory. */
2759 status =
2760 cli_smb2_create_fnum(cli, "",
2761 (struct cli_smb2_create_flags){0},
2762 SMB2_IMPERSONATION_IMPERSONATION,
2763 FILE_READ_ATTRIBUTES, /* desired_access */
2764 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2765 FILE_SHARE_READ | FILE_SHARE_WRITE |
2766 FILE_SHARE_DELETE, /* share_access */
2767 FILE_OPEN, /* create_disposition */
2768 FILE_DIRECTORY_FILE, /* create_options */
2769 NULL,
2770 &fnum,
2771 NULL,
2772 NULL,
2773 NULL);
2775 if (!NT_STATUS_IS_OK(status)) {
2776 goto fail;
2779 status = cli_smb2_query_info_fnum(
2780 cli,
2781 fnum,
2782 2, /* in_info_type */
2783 5, /* in_file_info_class */
2784 0xFFFF, /* in_max_output_length */
2785 NULL, /* in_input_buffer */
2786 0, /* in_additional_info */
2787 0, /* in_flags */
2788 frame,
2789 &outbuf);
2790 if (!NT_STATUS_IS_OK(status)) {
2791 goto fail;
2794 if (outbuf.length < 12) {
2795 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2796 goto fail;
2799 *fs_attr = IVAL(outbuf.data, 0);
2801 fail:
2803 if (fnum != 0xffff) {
2804 cli_smb2_close_fnum(cli, fnum);
2807 TALLOC_FREE(frame);
2808 return status;
2811 /***************************************************************
2812 Wrapper that allows SMB2 to query file system volume info.
2813 Synchronous only.
2814 ***************************************************************/
2816 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2817 TALLOC_CTX *mem_ctx,
2818 char **_volume_name,
2819 uint32_t *pserial_number,
2820 time_t *pdate)
2822 NTSTATUS status;
2823 uint16_t fnum = 0xffff;
2824 DATA_BLOB outbuf = data_blob_null;
2825 uint32_t nlen;
2826 char *volume_name = NULL;
2827 TALLOC_CTX *frame = talloc_stackframe();
2829 if (smbXcli_conn_has_async_calls(cli->conn)) {
2831 * Can't use sync call while an async call is in flight
2833 status = NT_STATUS_INVALID_PARAMETER;
2834 goto fail;
2837 /* First open the top level directory. */
2838 status =
2839 cli_smb2_create_fnum(cli, "",
2840 (struct cli_smb2_create_flags){0},
2841 SMB2_IMPERSONATION_IMPERSONATION,
2842 FILE_READ_ATTRIBUTES, /* desired_access */
2843 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2844 FILE_SHARE_READ | FILE_SHARE_WRITE |
2845 FILE_SHARE_DELETE, /* share_access */
2846 FILE_OPEN, /* create_disposition */
2847 FILE_DIRECTORY_FILE, /* create_options */
2848 NULL,
2849 &fnum,
2850 NULL,
2851 NULL,
2852 NULL);
2854 if (!NT_STATUS_IS_OK(status)) {
2855 goto fail;
2858 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2859 level 1 (SMB_FS_VOLUME_INFORMATION). */
2861 status = cli_smb2_query_info_fnum(
2862 cli,
2863 fnum,
2864 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2865 /* in_file_info_class */
2866 FSCC_FS_VOLUME_INFORMATION,
2867 0xFFFF, /* in_max_output_length */
2868 NULL, /* in_input_buffer */
2869 0, /* in_additional_info */
2870 0, /* in_flags */
2871 frame,
2872 &outbuf);
2873 if (!NT_STATUS_IS_OK(status)) {
2874 goto fail;
2877 if (outbuf.length < 24) {
2878 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2879 goto fail;
2882 if (pdate) {
2883 struct timespec ts;
2884 ts = interpret_long_date(BVAL(outbuf.data, 0));
2885 *pdate = ts.tv_sec;
2887 if (pserial_number) {
2888 *pserial_number = IVAL(outbuf.data,8);
2890 nlen = IVAL(outbuf.data,12);
2891 if (nlen + 18 < 18) {
2892 /* Integer wrap. */
2893 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2894 goto fail;
2897 * The next check is safe as we know outbuf.length >= 24
2898 * from above.
2900 if (nlen > (outbuf.length - 18)) {
2901 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2902 goto fail;
2905 pull_string_talloc(mem_ctx,
2906 (const char *)outbuf.data,
2908 &volume_name,
2909 outbuf.data + 18,
2910 nlen,
2911 STR_UNICODE);
2912 if (volume_name == NULL) {
2913 status = map_nt_error_from_unix(errno);
2914 goto fail;
2917 *_volume_name = volume_name;
2919 fail:
2921 if (fnum != 0xffff) {
2922 cli_smb2_close_fnum(cli, fnum);
2925 TALLOC_FREE(frame);
2926 return status;
2929 struct cli_smb2_mxac_state {
2930 struct tevent_context *ev;
2931 struct cli_state *cli;
2932 const char *fname;
2933 struct smb2_create_blobs in_cblobs;
2934 uint16_t fnum;
2935 NTSTATUS status;
2936 uint32_t mxac;
2939 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2940 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2942 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2943 struct tevent_context *ev,
2944 struct cli_state *cli,
2945 const char *fname)
2947 struct tevent_req *req = NULL, *subreq = NULL;
2948 struct cli_smb2_mxac_state *state = NULL;
2949 NTSTATUS status;
2951 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2952 if (req == NULL) {
2953 return NULL;
2955 *state = (struct cli_smb2_mxac_state) {
2956 .ev = ev,
2957 .cli = cli,
2958 .fname = fname,
2961 status = smb2_create_blob_add(state,
2962 &state->in_cblobs,
2963 SMB2_CREATE_TAG_MXAC,
2964 data_blob(NULL, 0));
2965 if (tevent_req_nterror(req, status)) {
2966 return tevent_req_post(req, ev);
2969 subreq = cli_smb2_create_fnum_send(
2970 state,
2971 state->ev,
2972 state->cli,
2973 state->fname,
2974 (struct cli_smb2_create_flags){0},
2975 SMB2_IMPERSONATION_IMPERSONATION,
2976 FILE_READ_ATTRIBUTES,
2977 0, /* file attributes */
2978 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2979 FILE_OPEN,
2980 0, /* create_options */
2981 &state->in_cblobs);
2982 if (tevent_req_nomem(subreq, req)) {
2983 return tevent_req_post(req, ev);
2985 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2986 return req;
2989 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2991 struct tevent_req *req = tevent_req_callback_data(
2992 subreq, struct tevent_req);
2993 struct cli_smb2_mxac_state *state = tevent_req_data(
2994 req, struct cli_smb2_mxac_state);
2995 struct smb2_create_blobs out_cblobs = {0};
2996 struct smb2_create_blob *mxac_blob = NULL;
2997 NTSTATUS status;
2999 status = cli_smb2_create_fnum_recv(
3000 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
3001 TALLOC_FREE(subreq);
3003 if (tevent_req_nterror(req, status)) {
3004 return;
3007 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
3008 if (mxac_blob == NULL) {
3009 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3010 goto close;
3012 if (mxac_blob->data.length != 8) {
3013 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3014 goto close;
3017 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
3018 state->mxac = IVAL(mxac_blob->data.data, 4);
3020 close:
3021 subreq = cli_smb2_close_fnum_send(state,
3022 state->ev,
3023 state->cli,
3024 state->fnum,
3026 if (tevent_req_nomem(subreq, req)) {
3027 return;
3029 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
3031 return;
3034 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
3036 struct tevent_req *req = tevent_req_callback_data(
3037 subreq, struct tevent_req);
3038 NTSTATUS status;
3040 status = cli_smb2_close_fnum_recv(subreq);
3041 if (tevent_req_nterror(req, status)) {
3042 return;
3045 tevent_req_done(req);
3048 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3050 struct cli_smb2_mxac_state *state = tevent_req_data(
3051 req, struct cli_smb2_mxac_state);
3052 NTSTATUS status;
3054 if (tevent_req_is_nterror(req, &status)) {
3055 return status;
3058 if (!NT_STATUS_IS_OK(state->status)) {
3059 return state->status;
3062 *mxac = state->mxac;
3063 return NT_STATUS_OK;
3066 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3067 const char *fname,
3068 uint32_t *_mxac)
3070 TALLOC_CTX *frame = talloc_stackframe();
3071 struct tevent_context *ev = NULL;
3072 struct tevent_req *req = NULL;
3073 NTSTATUS status = NT_STATUS_NO_MEMORY;
3074 bool ok;
3076 if (smbXcli_conn_has_async_calls(cli->conn)) {
3078 * Can't use sync call while an async call is in flight
3080 status = NT_STATUS_INVALID_PARAMETER;
3081 goto fail;
3084 ev = samba_tevent_context_init(frame);
3085 if (ev == NULL) {
3086 goto fail;
3088 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3089 if (req == NULL) {
3090 goto fail;
3092 ok = tevent_req_poll_ntstatus(req, ev, &status);
3093 if (!ok) {
3094 goto fail;
3096 status = cli_smb2_query_mxac_recv(req, _mxac);
3098 fail:
3099 TALLOC_FREE(frame);
3100 return status;
3103 struct cli_smb2_rename_fnum_state {
3104 DATA_BLOB inbuf;
3107 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3109 static struct tevent_req *cli_smb2_rename_fnum_send(
3110 TALLOC_CTX *mem_ctx,
3111 struct tevent_context *ev,
3112 struct cli_state *cli,
3113 uint16_t fnum,
3114 const char *fname_dst,
3115 bool replace)
3117 struct tevent_req *req = NULL, *subreq = NULL;
3118 struct cli_smb2_rename_fnum_state *state = NULL;
3119 size_t namelen = strlen(fname_dst);
3120 smb_ucs2_t *converted_str = NULL;
3121 size_t converted_size_bytes = 0;
3122 size_t inbuf_size;
3123 bool ok;
3125 req = tevent_req_create(
3126 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3127 if (req == NULL) {
3128 return NULL;
3132 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3133 * a '\'
3135 if (*fname_dst == '\\') {
3136 fname_dst++;
3140 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3141 * '\'
3143 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3144 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3145 if (tevent_req_nomem(fname_dst, req)) {
3146 return tevent_req_post(req, ev);
3150 ok = push_ucs2_talloc(
3151 state, &converted_str, fname_dst, &converted_size_bytes);
3152 if (!ok) {
3153 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3154 return tevent_req_post(req, ev);
3158 * W2K8 insists the dest name is not null terminated. Remove
3159 * the last 2 zero bytes and reduce the name length.
3161 if (converted_size_bytes < 2) {
3162 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3163 return tevent_req_post(req, ev);
3165 converted_size_bytes -= 2;
3167 inbuf_size = 20 + converted_size_bytes;
3168 if (inbuf_size < 20) {
3169 /* Integer wrap check. */
3170 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3171 return tevent_req_post(req, ev);
3175 * The Windows 10 SMB2 server has a minimum length
3176 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3177 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3178 * if the length is less. This isn't an alignment
3179 * issue as Windows client accepts happily 2-byte align
3180 * for larger target name sizes. Also the Windows 10
3181 * SMB1 server doesn't have this restriction.
3183 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3185 inbuf_size = MAX(inbuf_size, 24);
3187 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3188 if (tevent_req_nomem(state->inbuf.data, req)) {
3189 return tevent_req_post(req, ev);
3192 if (replace) {
3193 SCVAL(state->inbuf.data, 0, 1);
3196 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3197 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3199 TALLOC_FREE(converted_str);
3201 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3202 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3204 subreq = cli_smb2_set_info_fnum_send(
3205 state, /* mem_ctx */
3206 ev, /* ev */
3207 cli, /* cli */
3208 fnum, /* fnum */
3209 SMB2_0_INFO_FILE, /* in_info_type */
3210 FSCC_FILE_RENAME_INFORMATION, /* in_file_info_class */
3211 &state->inbuf, /* in_input_buffer */
3212 0); /* in_additional_info */
3213 if (tevent_req_nomem(subreq, req)) {
3214 return tevent_req_post(req, ev);
3216 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3217 return req;
3220 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3222 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3223 tevent_req_simple_finish_ntstatus(subreq, status);
3226 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3228 return tevent_req_simple_recv_ntstatus(req);
3231 /***************************************************************
3232 Wrapper that allows SMB2 to rename a file.
3233 ***************************************************************/
3235 struct cli_smb2_rename_state {
3236 struct tevent_context *ev;
3237 struct cli_state *cli;
3238 const char *fname_dst;
3239 bool replace;
3240 uint16_t fnum;
3242 NTSTATUS rename_status;
3245 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3246 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3247 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3249 struct tevent_req *cli_smb2_rename_send(
3250 TALLOC_CTX *mem_ctx,
3251 struct tevent_context *ev,
3252 struct cli_state *cli,
3253 const char *fname_src,
3254 const char *fname_dst,
3255 bool replace)
3257 struct tevent_req *req = NULL, *subreq = NULL;
3258 struct cli_smb2_rename_state *state = NULL;
3259 NTSTATUS status;
3261 req = tevent_req_create(
3262 mem_ctx, &state, struct cli_smb2_rename_state);
3263 if (req == NULL) {
3264 return NULL;
3268 * Strip a MSDFS path from fname_dst if we were given one.
3270 status = cli_dfs_target_check(state,
3271 cli,
3272 fname_dst,
3273 &fname_dst);
3274 if (tevent_req_nterror(req, status)) {
3275 return tevent_req_post(req, ev);
3278 state->ev = ev;
3279 state->cli = cli;
3280 state->fname_dst = fname_dst;
3281 state->replace = replace;
3283 subreq = get_fnum_from_path_send(
3284 state, ev, cli, fname_src, DELETE_ACCESS);
3285 if (tevent_req_nomem(subreq, req)) {
3286 return tevent_req_post(req, ev);
3288 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3289 return req;
3292 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3294 struct tevent_req *req = tevent_req_callback_data(
3295 subreq, struct tevent_req);
3296 struct cli_smb2_rename_state *state = tevent_req_data(
3297 req, struct cli_smb2_rename_state);
3298 NTSTATUS status;
3300 status = get_fnum_from_path_recv(subreq, &state->fnum);
3301 TALLOC_FREE(subreq);
3302 if (tevent_req_nterror(req, status)) {
3303 return;
3306 subreq = cli_smb2_rename_fnum_send(
3307 state,
3308 state->ev,
3309 state->cli,
3310 state->fnum,
3311 state->fname_dst,
3312 state->replace);
3313 if (tevent_req_nomem(subreq, req)) {
3314 return;
3316 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3319 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3321 struct tevent_req *req = tevent_req_callback_data(
3322 subreq, struct tevent_req);
3323 struct cli_smb2_rename_state *state = tevent_req_data(
3324 req, struct cli_smb2_rename_state);
3326 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3327 TALLOC_FREE(subreq);
3329 subreq = cli_smb2_close_fnum_send(state,
3330 state->ev,
3331 state->cli,
3332 state->fnum,
3334 if (tevent_req_nomem(subreq, req)) {
3335 return;
3337 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3340 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3342 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3343 tevent_req_simple_finish_ntstatus(subreq, status);
3346 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3348 struct cli_smb2_rename_state *state = tevent_req_data(
3349 req, struct cli_smb2_rename_state);
3350 NTSTATUS status = NT_STATUS_OK;
3352 if (!tevent_req_is_nterror(req, &status)) {
3353 status = state->rename_status;
3355 tevent_req_received(req);
3356 return status;
3359 /***************************************************************
3360 Wrapper that allows SMB2 to set an EA on a fnum.
3361 Synchronous only.
3362 ***************************************************************/
3364 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3365 uint16_t fnum,
3366 const char *ea_name,
3367 const char *ea_val,
3368 size_t ea_len)
3370 NTSTATUS status;
3371 DATA_BLOB inbuf = data_blob_null;
3372 size_t bloblen = 0;
3373 char *ea_name_ascii = NULL;
3374 size_t namelen = 0;
3375 TALLOC_CTX *frame = talloc_stackframe();
3377 if (smbXcli_conn_has_async_calls(cli->conn)) {
3379 * Can't use sync call while an async call is in flight
3381 status = NT_STATUS_INVALID_PARAMETER;
3382 goto fail;
3385 /* Marshall the SMB2 EA data. */
3386 if (ea_len > 0xFFFF) {
3387 status = NT_STATUS_INVALID_PARAMETER;
3388 goto fail;
3391 if (!push_ascii_talloc(frame,
3392 &ea_name_ascii,
3393 ea_name,
3394 &namelen)) {
3395 status = NT_STATUS_INVALID_PARAMETER;
3396 goto fail;
3399 if (namelen < 2 || namelen > 0xFF) {
3400 status = NT_STATUS_INVALID_PARAMETER;
3401 goto fail;
3404 bloblen = 8 + ea_len + namelen;
3405 /* Round up to a 4 byte boundary. */
3406 bloblen = ((bloblen + 3)&~3);
3408 inbuf = data_blob_talloc_zero(frame, bloblen);
3409 if (inbuf.data == NULL) {
3410 status = NT_STATUS_NO_MEMORY;
3411 goto fail;
3413 /* namelen doesn't include the NULL byte. */
3414 SCVAL(inbuf.data, 5, namelen - 1);
3415 SSVAL(inbuf.data, 6, ea_len);
3416 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3417 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3419 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3420 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3422 status = cli_smb2_set_info_fnum(
3423 cli,
3424 fnum,
3425 SMB2_0_INFO_FILE, /* in_info_type */
3426 FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3427 &inbuf, /* in_input_buffer */
3428 0); /* in_additional_info */
3430 fail:
3431 TALLOC_FREE(frame);
3432 return status;
3435 /***************************************************************
3436 Wrapper that allows SMB2 to set an EA on a pathname.
3437 Synchronous only.
3438 ***************************************************************/
3440 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3441 const char *name,
3442 const char *ea_name,
3443 const char *ea_val,
3444 size_t ea_len)
3446 NTSTATUS status;
3447 uint16_t fnum = 0xffff;
3449 if (smbXcli_conn_has_async_calls(cli->conn)) {
3451 * Can't use sync call while an async call is in flight
3453 status = NT_STATUS_INVALID_PARAMETER;
3454 goto fail;
3457 status = get_fnum_from_path(cli,
3458 name,
3459 FILE_WRITE_EA,
3460 &fnum);
3462 if (!NT_STATUS_IS_OK(status)) {
3463 goto fail;
3466 status = cli_set_ea_fnum(cli,
3467 fnum,
3468 ea_name,
3469 ea_val,
3470 ea_len);
3471 if (!NT_STATUS_IS_OK(status)) {
3472 goto fail;
3475 fail:
3477 if (fnum != 0xffff) {
3478 cli_smb2_close_fnum(cli, fnum);
3480 return status;
3483 /***************************************************************
3484 Wrapper that allows SMB2 to get an EA list on a pathname.
3485 Synchronous only.
3486 ***************************************************************/
3488 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3489 const char *name,
3490 TALLOC_CTX *ctx,
3491 size_t *pnum_eas,
3492 struct ea_struct **pea_array)
3494 NTSTATUS status;
3495 uint16_t fnum = 0xffff;
3496 DATA_BLOB outbuf = data_blob_null;
3497 struct ea_list *ea_list = NULL;
3498 struct ea_list *eal = NULL;
3499 size_t ea_count = 0;
3500 TALLOC_CTX *frame = talloc_stackframe();
3502 *pnum_eas = 0;
3503 *pea_array = NULL;
3505 if (smbXcli_conn_has_async_calls(cli->conn)) {
3507 * Can't use sync call while an async call is in flight
3509 status = NT_STATUS_INVALID_PARAMETER;
3510 goto fail;
3513 status = get_fnum_from_path(cli,
3514 name,
3515 FILE_READ_EA,
3516 &fnum);
3518 if (!NT_STATUS_IS_OK(status)) {
3519 goto fail;
3522 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3523 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3525 status = cli_smb2_query_info_fnum(
3526 cli,
3527 fnum,
3528 SMB2_0_INFO_FILE, /* in_info_type */
3529 FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3530 0xFFFF, /* in_max_output_length */
3531 NULL, /* in_input_buffer */
3532 0, /* in_additional_info */
3533 0, /* in_flags */
3534 frame,
3535 &outbuf);
3537 if (!NT_STATUS_IS_OK(status)) {
3538 goto fail;
3541 /* Parse the reply. */
3542 ea_list = read_nttrans_ea_list(ctx,
3543 (const char *)outbuf.data,
3544 outbuf.length);
3545 if (ea_list == NULL) {
3546 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3547 goto fail;
3550 /* Convert to an array. */
3551 for (eal = ea_list; eal; eal = eal->next) {
3552 ea_count++;
3555 if (ea_count) {
3556 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3557 if (*pea_array == NULL) {
3558 status = NT_STATUS_NO_MEMORY;
3559 goto fail;
3561 ea_count = 0;
3562 for (eal = ea_list; eal; eal = eal->next) {
3563 (*pea_array)[ea_count++] = eal->ea;
3565 *pnum_eas = ea_count;
3568 fail:
3570 if (fnum != 0xffff) {
3571 cli_smb2_close_fnum(cli, fnum);
3574 TALLOC_FREE(frame);
3575 return status;
3578 /***************************************************************
3579 Wrapper that allows SMB2 to get user quota.
3580 Synchronous only.
3581 ***************************************************************/
3583 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3584 int quota_fnum,
3585 SMB_NTQUOTA_STRUCT *pqt)
3587 NTSTATUS status;
3588 DATA_BLOB inbuf = data_blob_null;
3589 DATA_BLOB info_blob = data_blob_null;
3590 DATA_BLOB outbuf = data_blob_null;
3591 TALLOC_CTX *frame = talloc_stackframe();
3592 unsigned sid_len;
3593 unsigned int offset;
3594 struct smb2_query_quota_info query = {0};
3595 struct file_get_quota_info info = {0};
3596 enum ndr_err_code err;
3597 struct ndr_push *ndr_push = NULL;
3599 if (smbXcli_conn_has_async_calls(cli->conn)) {
3601 * Can't use sync call while an async call is in flight
3603 status = NT_STATUS_INVALID_PARAMETER;
3604 goto fail;
3607 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3609 query.return_single = 1;
3611 info.next_entry_offset = 0;
3612 info.sid_length = sid_len;
3613 info.sid = pqt->sid;
3615 err = ndr_push_struct_blob(
3616 &info_blob,
3617 frame,
3618 &info,
3619 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3621 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3622 status = NT_STATUS_INTERNAL_ERROR;
3623 goto fail;
3626 query.sid_list_length = info_blob.length;
3627 ndr_push = ndr_push_init_ctx(frame);
3628 if (!ndr_push) {
3629 status = NT_STATUS_NO_MEMORY;
3630 goto fail;
3633 err = ndr_push_smb2_query_quota_info(ndr_push,
3634 NDR_SCALARS | NDR_BUFFERS,
3635 &query);
3637 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3638 status = NT_STATUS_INTERNAL_ERROR;
3639 goto fail;
3642 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3643 info_blob.length);
3645 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3646 status = NT_STATUS_INTERNAL_ERROR;
3647 goto fail;
3649 inbuf.data = ndr_push->data;
3650 inbuf.length = ndr_push->offset;
3652 status = cli_smb2_query_info_fnum(
3653 cli,
3654 quota_fnum,
3655 4, /* in_info_type */
3656 0, /* in_file_info_class */
3657 0xFFFF, /* in_max_output_length */
3658 &inbuf, /* in_input_buffer */
3659 0, /* in_additional_info */
3660 0, /* in_flags */
3661 frame,
3662 &outbuf);
3664 if (!NT_STATUS_IS_OK(status)) {
3665 goto fail;
3668 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3669 pqt)) {
3670 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3671 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3674 fail:
3675 TALLOC_FREE(frame);
3676 return status;
3679 /***************************************************************
3680 Wrapper that allows SMB2 to list user quota.
3681 Synchronous only.
3682 ***************************************************************/
3684 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3685 TALLOC_CTX *mem_ctx,
3686 int quota_fnum,
3687 SMB_NTQUOTA_LIST **pqt_list,
3688 bool first)
3690 NTSTATUS status;
3691 DATA_BLOB inbuf = data_blob_null;
3692 DATA_BLOB outbuf = data_blob_null;
3693 TALLOC_CTX *frame = talloc_stackframe();
3694 struct smb2_query_quota_info info = {0};
3695 enum ndr_err_code err;
3697 if (smbXcli_conn_has_async_calls(cli->conn)) {
3699 * Can't use sync call while an async call is in flight
3701 status = NT_STATUS_INVALID_PARAMETER;
3702 goto cleanup;
3705 info.restart_scan = first ? 1 : 0;
3707 err = ndr_push_struct_blob(
3708 &inbuf,
3709 frame,
3710 &info,
3711 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3713 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3714 status = NT_STATUS_INTERNAL_ERROR;
3715 goto cleanup;
3718 status = cli_smb2_query_info_fnum(
3719 cli,
3720 quota_fnum,
3721 4, /* in_info_type */
3722 0, /* in_file_info_class */
3723 0xFFFF, /* in_max_output_length */
3724 &inbuf, /* in_input_buffer */
3725 0, /* in_additional_info */
3726 0, /* in_flags */
3727 frame,
3728 &outbuf);
3731 * safeguard against panic from calling parse_user_quota_list with
3732 * NULL buffer
3734 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3735 status = NT_STATUS_NO_MORE_ENTRIES;
3738 if (!NT_STATUS_IS_OK(status)) {
3739 goto cleanup;
3742 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3743 pqt_list);
3745 cleanup:
3746 TALLOC_FREE(frame);
3747 return status;
3750 /***************************************************************
3751 Wrapper that allows SMB2 to get file system quota.
3752 Synchronous only.
3753 ***************************************************************/
3755 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3756 int quota_fnum,
3757 SMB_NTQUOTA_STRUCT *pqt)
3759 NTSTATUS status;
3760 DATA_BLOB outbuf = data_blob_null;
3761 TALLOC_CTX *frame = talloc_stackframe();
3763 if (smbXcli_conn_has_async_calls(cli->conn)) {
3765 * Can't use sync call while an async call is in flight
3767 status = NT_STATUS_INVALID_PARAMETER;
3768 goto cleanup;
3771 status = cli_smb2_query_info_fnum(
3772 cli,
3773 quota_fnum,
3774 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3775 FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3776 0xFFFF, /* in_max_output_length */
3777 NULL, /* in_input_buffer */
3778 0, /* in_additional_info */
3779 0, /* in_flags */
3780 frame,
3781 &outbuf);
3783 if (!NT_STATUS_IS_OK(status)) {
3784 goto cleanup;
3787 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3789 cleanup:
3790 TALLOC_FREE(frame);
3791 return status;
3794 /***************************************************************
3795 Wrapper that allows SMB2 to set user quota.
3796 Synchronous only.
3797 ***************************************************************/
3799 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3800 int quota_fnum,
3801 SMB_NTQUOTA_LIST *qtl)
3803 NTSTATUS status;
3804 DATA_BLOB inbuf = data_blob_null;
3805 TALLOC_CTX *frame = talloc_stackframe();
3807 if (smbXcli_conn_has_async_calls(cli->conn)) {
3809 * Can't use sync call while an async call is in flight
3811 status = NT_STATUS_INVALID_PARAMETER;
3812 goto cleanup;
3815 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3816 if (!NT_STATUS_IS_OK(status)) {
3817 goto cleanup;
3820 status = cli_smb2_set_info_fnum(
3821 cli,
3822 quota_fnum,
3823 4, /* in_info_type */
3824 0, /* in_file_info_class */
3825 &inbuf, /* in_input_buffer */
3826 0); /* in_additional_info */
3827 cleanup:
3828 TALLOC_FREE(frame);
3830 return status;
3833 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3834 int quota_fnum,
3835 SMB_NTQUOTA_STRUCT *pqt)
3837 NTSTATUS status;
3838 DATA_BLOB inbuf = data_blob_null;
3839 TALLOC_CTX *frame = talloc_stackframe();
3841 if (smbXcli_conn_has_async_calls(cli->conn)) {
3843 * Can't use sync call while an async call is in flight
3845 status = NT_STATUS_INVALID_PARAMETER;
3846 goto cleanup;
3849 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3850 if (!NT_STATUS_IS_OK(status)) {
3851 goto cleanup;
3854 status = cli_smb2_set_info_fnum(
3855 cli,
3856 quota_fnum,
3857 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3858 FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3859 &inbuf, /* in_input_buffer */
3860 0); /* in_additional_info */
3861 cleanup:
3862 TALLOC_FREE(frame);
3863 return status;
3866 struct cli_smb2_read_state {
3867 struct tevent_context *ev;
3868 struct cli_state *cli;
3869 struct smb2_hnd *ph;
3870 uint64_t start_offset;
3871 uint32_t size;
3872 uint32_t received;
3873 uint8_t *buf;
3876 static void cli_smb2_read_done(struct tevent_req *subreq);
3878 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3879 struct tevent_context *ev,
3880 struct cli_state *cli,
3881 uint16_t fnum,
3882 off_t offset,
3883 size_t size)
3885 NTSTATUS status;
3886 struct tevent_req *req, *subreq;
3887 struct cli_smb2_read_state *state;
3889 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3890 if (req == NULL) {
3891 return NULL;
3893 state->ev = ev;
3894 state->cli = cli;
3895 state->start_offset = (uint64_t)offset;
3896 state->size = (uint32_t)size;
3897 state->received = 0;
3898 state->buf = NULL;
3900 status = map_fnum_to_smb2_handle(cli,
3901 fnum,
3902 &state->ph);
3903 if (tevent_req_nterror(req, status)) {
3904 return tevent_req_post(req, ev);
3907 subreq = smb2cli_read_send(state,
3908 state->ev,
3909 state->cli->conn,
3910 state->cli->timeout,
3911 state->cli->smb2.session,
3912 state->cli->smb2.tcon,
3913 state->size,
3914 state->start_offset,
3915 state->ph->fid_persistent,
3916 state->ph->fid_volatile,
3917 0, /* minimum_count */
3918 0); /* remaining_bytes */
3920 if (tevent_req_nomem(subreq, req)) {
3921 return tevent_req_post(req, ev);
3923 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3924 return req;
3927 static void cli_smb2_read_done(struct tevent_req *subreq)
3929 struct tevent_req *req = tevent_req_callback_data(
3930 subreq, struct tevent_req);
3931 struct cli_smb2_read_state *state = tevent_req_data(
3932 req, struct cli_smb2_read_state);
3933 NTSTATUS status;
3935 status = smb2cli_read_recv(subreq, state,
3936 &state->buf, &state->received);
3937 if (tevent_req_nterror(req, status)) {
3938 return;
3941 if (state->received > state->size) {
3942 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3943 return;
3946 tevent_req_done(req);
3949 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3950 ssize_t *received,
3951 uint8_t **rcvbuf)
3953 NTSTATUS status;
3954 struct cli_smb2_read_state *state = tevent_req_data(
3955 req, struct cli_smb2_read_state);
3957 if (tevent_req_is_nterror(req, &status)) {
3958 return status;
3961 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3962 * better make sure that you copy it away before you talloc_free(req).
3963 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3965 *received = (ssize_t)state->received;
3966 *rcvbuf = state->buf;
3967 return NT_STATUS_OK;
3970 struct cli_smb2_write_state {
3971 struct tevent_context *ev;
3972 struct cli_state *cli;
3973 struct smb2_hnd *ph;
3974 uint32_t flags;
3975 const uint8_t *buf;
3976 uint64_t offset;
3977 uint32_t size;
3978 uint32_t written;
3981 static void cli_smb2_write_written(struct tevent_req *req);
3983 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3984 struct tevent_context *ev,
3985 struct cli_state *cli,
3986 uint16_t fnum,
3987 uint16_t mode,
3988 const uint8_t *buf,
3989 off_t offset,
3990 size_t size)
3992 NTSTATUS status;
3993 struct tevent_req *req, *subreq = NULL;
3994 struct cli_smb2_write_state *state = NULL;
3996 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3997 if (req == NULL) {
3998 return NULL;
4000 state->ev = ev;
4001 state->cli = cli;
4002 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4003 state->flags = (uint32_t)mode;
4004 state->buf = buf;
4005 state->offset = (uint64_t)offset;
4006 state->size = (uint32_t)size;
4007 state->written = 0;
4009 status = map_fnum_to_smb2_handle(cli,
4010 fnum,
4011 &state->ph);
4012 if (tevent_req_nterror(req, status)) {
4013 return tevent_req_post(req, ev);
4016 subreq = smb2cli_write_send(state,
4017 state->ev,
4018 state->cli->conn,
4019 state->cli->timeout,
4020 state->cli->smb2.session,
4021 state->cli->smb2.tcon,
4022 state->size,
4023 state->offset,
4024 state->ph->fid_persistent,
4025 state->ph->fid_volatile,
4026 0, /* remaining_bytes */
4027 state->flags, /* flags */
4028 state->buf);
4030 if (tevent_req_nomem(subreq, req)) {
4031 return tevent_req_post(req, ev);
4033 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4034 return req;
4037 static void cli_smb2_write_written(struct tevent_req *subreq)
4039 struct tevent_req *req = tevent_req_callback_data(
4040 subreq, struct tevent_req);
4041 struct cli_smb2_write_state *state = tevent_req_data(
4042 req, struct cli_smb2_write_state);
4043 NTSTATUS status;
4044 uint32_t written;
4046 status = smb2cli_write_recv(subreq, &written);
4047 TALLOC_FREE(subreq);
4048 if (tevent_req_nterror(req, status)) {
4049 return;
4052 state->written = written;
4054 tevent_req_done(req);
4057 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4058 size_t *pwritten)
4060 struct cli_smb2_write_state *state = tevent_req_data(
4061 req, struct cli_smb2_write_state);
4062 NTSTATUS status;
4064 if (tevent_req_is_nterror(req, &status)) {
4065 tevent_req_received(req);
4066 return status;
4069 if (pwritten != NULL) {
4070 *pwritten = (size_t)state->written;
4072 tevent_req_received(req);
4073 return NT_STATUS_OK;
4076 /***************************************************************
4077 Wrapper that allows SMB2 async write using an fnum.
4078 This is mostly cut-and-paste from Volker's code inside
4079 source3/libsmb/clireadwrite.c, adapted for SMB2.
4081 Done this way so I can reuse all the logic inside cli_push()
4082 for free :-).
4083 ***************************************************************/
4085 struct cli_smb2_writeall_state {
4086 struct tevent_context *ev;
4087 struct cli_state *cli;
4088 struct smb2_hnd *ph;
4089 uint32_t flags;
4090 const uint8_t *buf;
4091 uint64_t offset;
4092 uint32_t size;
4093 uint32_t written;
4096 static void cli_smb2_writeall_written(struct tevent_req *req);
4098 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4099 struct tevent_context *ev,
4100 struct cli_state *cli,
4101 uint16_t fnum,
4102 uint16_t mode,
4103 const uint8_t *buf,
4104 off_t offset,
4105 size_t size)
4107 NTSTATUS status;
4108 struct tevent_req *req, *subreq = NULL;
4109 struct cli_smb2_writeall_state *state = NULL;
4110 uint32_t to_write;
4111 uint32_t max_size;
4112 bool ok;
4114 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4115 if (req == NULL) {
4116 return NULL;
4118 state->ev = ev;
4119 state->cli = cli;
4120 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4121 state->flags = (uint32_t)mode;
4122 state->buf = buf;
4123 state->offset = (uint64_t)offset;
4124 state->size = (uint32_t)size;
4125 state->written = 0;
4127 status = map_fnum_to_smb2_handle(cli,
4128 fnum,
4129 &state->ph);
4130 if (tevent_req_nterror(req, status)) {
4131 return tevent_req_post(req, ev);
4134 to_write = state->size;
4135 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4136 to_write = MIN(max_size, to_write);
4137 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4138 if (ok) {
4139 to_write = MIN(max_size, to_write);
4142 subreq = smb2cli_write_send(state,
4143 state->ev,
4144 state->cli->conn,
4145 state->cli->timeout,
4146 state->cli->smb2.session,
4147 state->cli->smb2.tcon,
4148 to_write,
4149 state->offset,
4150 state->ph->fid_persistent,
4151 state->ph->fid_volatile,
4152 0, /* remaining_bytes */
4153 state->flags, /* flags */
4154 state->buf + state->written);
4156 if (tevent_req_nomem(subreq, req)) {
4157 return tevent_req_post(req, ev);
4159 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4160 return req;
4163 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4165 struct tevent_req *req = tevent_req_callback_data(
4166 subreq, struct tevent_req);
4167 struct cli_smb2_writeall_state *state = tevent_req_data(
4168 req, struct cli_smb2_writeall_state);
4169 NTSTATUS status;
4170 uint32_t written, to_write;
4171 uint32_t max_size;
4172 bool ok;
4174 status = smb2cli_write_recv(subreq, &written);
4175 TALLOC_FREE(subreq);
4176 if (tevent_req_nterror(req, status)) {
4177 return;
4180 state->written += written;
4182 if (state->written > state->size) {
4183 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4184 return;
4187 to_write = state->size - state->written;
4189 if (to_write == 0) {
4190 tevent_req_done(req);
4191 return;
4194 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4195 to_write = MIN(max_size, to_write);
4196 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4197 if (ok) {
4198 to_write = MIN(max_size, to_write);
4201 subreq = smb2cli_write_send(state,
4202 state->ev,
4203 state->cli->conn,
4204 state->cli->timeout,
4205 state->cli->smb2.session,
4206 state->cli->smb2.tcon,
4207 to_write,
4208 state->offset + state->written,
4209 state->ph->fid_persistent,
4210 state->ph->fid_volatile,
4211 0, /* remaining_bytes */
4212 state->flags, /* flags */
4213 state->buf + state->written);
4215 if (tevent_req_nomem(subreq, req)) {
4216 return;
4218 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4221 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4222 size_t *pwritten)
4224 struct cli_smb2_writeall_state *state = tevent_req_data(
4225 req, struct cli_smb2_writeall_state);
4226 NTSTATUS status;
4228 if (tevent_req_is_nterror(req, &status)) {
4229 return status;
4231 if (pwritten != NULL) {
4232 *pwritten = (size_t)state->written;
4234 return NT_STATUS_OK;
4237 struct cli_smb2_splice_state {
4238 struct tevent_context *ev;
4239 struct cli_state *cli;
4240 struct smb2_hnd *src_ph;
4241 struct smb2_hnd *dst_ph;
4242 int (*splice_cb)(off_t n, void *priv);
4243 void *priv;
4244 off_t written;
4245 off_t size;
4246 off_t src_offset;
4247 off_t dst_offset;
4248 bool resized;
4249 struct req_resume_key_rsp resume_rsp;
4250 struct srv_copychunk_copy cc_copy;
4253 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4254 struct tevent_req *req);
4256 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4258 struct tevent_req *req = tevent_req_callback_data(
4259 subreq, struct tevent_req);
4260 struct cli_smb2_splice_state *state =
4261 tevent_req_data(req,
4262 struct cli_smb2_splice_state);
4263 struct smbXcli_conn *conn = state->cli->conn;
4264 DATA_BLOB out_input_buffer = data_blob_null;
4265 DATA_BLOB out_output_buffer = data_blob_null;
4266 struct srv_copychunk_rsp cc_copy_rsp;
4267 enum ndr_err_code ndr_ret;
4268 NTSTATUS status;
4270 status = smb2cli_ioctl_recv(subreq, state,
4271 &out_input_buffer,
4272 &out_output_buffer);
4273 TALLOC_FREE(subreq);
4274 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4275 state->resized) && tevent_req_nterror(req, status)) {
4276 return;
4279 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4280 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4281 if (ndr_ret != NDR_ERR_SUCCESS) {
4282 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4283 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4284 return;
4287 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4288 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4289 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4290 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4291 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4292 tevent_req_nterror(req, status)) {
4293 return;
4296 state->resized = true;
4297 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4298 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4299 } else {
4300 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4301 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4302 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4303 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4304 return;
4306 state->src_offset += cc_copy_rsp.total_bytes_written;
4307 state->dst_offset += cc_copy_rsp.total_bytes_written;
4308 state->written += cc_copy_rsp.total_bytes_written;
4309 if (!state->splice_cb(state->written, state->priv)) {
4310 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4311 return;
4315 cli_splice_copychunk_send(state, req);
4318 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4319 struct tevent_req *req)
4321 struct tevent_req *subreq;
4322 enum ndr_err_code ndr_ret;
4323 struct smbXcli_conn *conn = state->cli->conn;
4324 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4325 off_t src_offset = state->src_offset;
4326 off_t dst_offset = state->dst_offset;
4327 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4328 state->size - state->written);
4329 DATA_BLOB in_input_buffer = data_blob_null;
4330 DATA_BLOB in_output_buffer = data_blob_null;
4332 if (state->size - state->written == 0) {
4333 tevent_req_done(req);
4334 return;
4337 cc_copy->chunk_count = 0;
4338 while (req_len) {
4339 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4340 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4341 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4342 smb2cli_conn_cc_chunk_len(conn));
4343 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4344 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4345 return;
4347 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4348 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4349 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4350 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4351 return;
4353 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4354 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4355 cc_copy->chunk_count++;
4358 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4359 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4360 if (ndr_ret != NDR_ERR_SUCCESS) {
4361 DEBUG(0, ("failed to marshall copy chunk req\n"));
4362 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4363 return;
4366 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4367 state->cli->timeout,
4368 state->cli->smb2.session,
4369 state->cli->smb2.tcon,
4370 state->dst_ph->fid_persistent, /* in_fid_persistent */
4371 state->dst_ph->fid_volatile, /* in_fid_volatile */
4372 FSCTL_SRV_COPYCHUNK_WRITE,
4373 0, /* in_max_input_length */
4374 &in_input_buffer,
4375 12, /* in_max_output_length */
4376 &in_output_buffer,
4377 SMB2_IOCTL_FLAG_IS_FSCTL);
4378 if (tevent_req_nomem(subreq, req)) {
4379 return;
4381 tevent_req_set_callback(subreq,
4382 cli_splice_copychunk_done,
4383 req);
4386 static void cli_splice_key_done(struct tevent_req *subreq)
4388 struct tevent_req *req = tevent_req_callback_data(
4389 subreq, struct tevent_req);
4390 struct cli_smb2_splice_state *state =
4391 tevent_req_data(req,
4392 struct cli_smb2_splice_state);
4393 enum ndr_err_code ndr_ret;
4394 NTSTATUS status;
4396 DATA_BLOB out_input_buffer = data_blob_null;
4397 DATA_BLOB out_output_buffer = data_blob_null;
4399 status = smb2cli_ioctl_recv(subreq, state,
4400 &out_input_buffer,
4401 &out_output_buffer);
4402 TALLOC_FREE(subreq);
4403 if (tevent_req_nterror(req, status)) {
4404 return;
4407 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4408 state, &state->resume_rsp,
4409 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4410 if (ndr_ret != NDR_ERR_SUCCESS) {
4411 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4412 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4413 return;
4416 memcpy(&state->cc_copy.source_key,
4417 &state->resume_rsp.resume_key,
4418 sizeof state->resume_rsp.resume_key);
4420 cli_splice_copychunk_send(state, req);
4423 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4424 struct tevent_context *ev,
4425 struct cli_state *cli,
4426 uint16_t src_fnum, uint16_t dst_fnum,
4427 off_t size, off_t src_offset, off_t dst_offset,
4428 int (*splice_cb)(off_t n, void *priv),
4429 void *priv)
4431 struct tevent_req *req;
4432 struct tevent_req *subreq;
4433 struct cli_smb2_splice_state *state;
4434 NTSTATUS status;
4435 DATA_BLOB in_input_buffer = data_blob_null;
4436 DATA_BLOB in_output_buffer = data_blob_null;
4438 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4439 if (req == NULL) {
4440 return NULL;
4442 state->cli = cli;
4443 state->ev = ev;
4444 state->splice_cb = splice_cb;
4445 state->priv = priv;
4446 state->size = size;
4447 state->written = 0;
4448 state->src_offset = src_offset;
4449 state->dst_offset = dst_offset;
4450 state->cc_copy.chunks = talloc_array(state,
4451 struct srv_copychunk,
4452 smb2cli_conn_cc_max_chunks(cli->conn));
4453 if (state->cc_copy.chunks == NULL) {
4454 return NULL;
4457 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4458 if (tevent_req_nterror(req, status))
4459 return tevent_req_post(req, ev);
4461 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4462 if (tevent_req_nterror(req, status))
4463 return tevent_req_post(req, ev);
4465 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4466 cli->timeout,
4467 cli->smb2.session,
4468 cli->smb2.tcon,
4469 state->src_ph->fid_persistent, /* in_fid_persistent */
4470 state->src_ph->fid_volatile, /* in_fid_volatile */
4471 FSCTL_SRV_REQUEST_RESUME_KEY,
4472 0, /* in_max_input_length */
4473 &in_input_buffer,
4474 32, /* in_max_output_length */
4475 &in_output_buffer,
4476 SMB2_IOCTL_FLAG_IS_FSCTL);
4477 if (tevent_req_nomem(subreq, req)) {
4478 return NULL;
4480 tevent_req_set_callback(subreq,
4481 cli_splice_key_done,
4482 req);
4484 return req;
4487 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4489 struct cli_smb2_splice_state *state = tevent_req_data(
4490 req, struct cli_smb2_splice_state);
4491 NTSTATUS status;
4493 if (tevent_req_is_nterror(req, &status)) {
4494 tevent_req_received(req);
4495 return status;
4497 if (written != NULL) {
4498 *written = state->written;
4500 tevent_req_received(req);
4501 return NT_STATUS_OK;
4504 /***************************************************************
4505 SMB2 enum shadow copy data.
4506 ***************************************************************/
4508 struct cli_smb2_shadow_copy_data_fnum_state {
4509 struct cli_state *cli;
4510 uint16_t fnum;
4511 struct smb2_hnd *ph;
4512 DATA_BLOB out_input_buffer;
4513 DATA_BLOB out_output_buffer;
4516 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4518 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4519 TALLOC_CTX *mem_ctx,
4520 struct tevent_context *ev,
4521 struct cli_state *cli,
4522 uint16_t fnum,
4523 bool get_names)
4525 struct tevent_req *req, *subreq;
4526 struct cli_smb2_shadow_copy_data_fnum_state *state;
4527 NTSTATUS status;
4529 req = tevent_req_create(mem_ctx, &state,
4530 struct cli_smb2_shadow_copy_data_fnum_state);
4531 if (req == NULL) {
4532 return NULL;
4535 state->cli = cli;
4536 state->fnum = fnum;
4538 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4539 if (tevent_req_nterror(req, status)) {
4540 return tevent_req_post(req, ev);
4544 * TODO. Under SMB2 we should send a zero max_output_length
4545 * ioctl to get the required size, then send another ioctl
4546 * to get the data, but the current SMB1 implementation just
4547 * does one roundtrip with a 64K buffer size. Do the same
4548 * for now. JRA.
4551 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4552 state->cli->timeout,
4553 state->cli->smb2.session,
4554 state->cli->smb2.tcon,
4555 state->ph->fid_persistent, /* in_fid_persistent */
4556 state->ph->fid_volatile, /* in_fid_volatile */
4557 FSCTL_GET_SHADOW_COPY_DATA,
4558 0, /* in_max_input_length */
4559 NULL, /* in_input_buffer */
4560 get_names ?
4561 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4562 NULL, /* in_output_buffer */
4563 SMB2_IOCTL_FLAG_IS_FSCTL);
4565 if (tevent_req_nomem(subreq, req)) {
4566 return tevent_req_post(req, ev);
4568 tevent_req_set_callback(subreq,
4569 cli_smb2_shadow_copy_data_fnum_done,
4570 req);
4572 return req;
4575 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4577 struct tevent_req *req = tevent_req_callback_data(
4578 subreq, struct tevent_req);
4579 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4580 req, struct cli_smb2_shadow_copy_data_fnum_state);
4581 NTSTATUS status;
4583 status = smb2cli_ioctl_recv(subreq, state,
4584 &state->out_input_buffer,
4585 &state->out_output_buffer);
4586 tevent_req_simple_finish_ntstatus(subreq, status);
4589 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4590 TALLOC_CTX *mem_ctx,
4591 bool get_names,
4592 char ***pnames,
4593 int *pnum_names)
4595 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4596 req, struct cli_smb2_shadow_copy_data_fnum_state);
4597 char **names = NULL;
4598 uint32_t num_names = 0;
4599 uint32_t num_names_returned = 0;
4600 uint32_t dlength = 0;
4601 uint32_t i;
4602 uint8_t *endp = NULL;
4603 NTSTATUS status;
4605 if (tevent_req_is_nterror(req, &status)) {
4606 return status;
4609 if (state->out_output_buffer.length < 16) {
4610 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4613 num_names = IVAL(state->out_output_buffer.data, 0);
4614 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4615 dlength = IVAL(state->out_output_buffer.data, 8);
4617 if (num_names > 0x7FFFFFFF) {
4618 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4621 if (get_names == false) {
4622 *pnum_names = (int)num_names;
4623 return NT_STATUS_OK;
4625 if (num_names != num_names_returned) {
4626 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4628 if (dlength + 12 < 12) {
4629 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4632 * NB. The below is an allowable return if there are
4633 * more snapshots than the buffer size we told the
4634 * server we can receive. We currently don't support
4635 * this.
4637 if (dlength + 12 > state->out_output_buffer.length) {
4638 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4640 if (state->out_output_buffer.length +
4641 (2 * sizeof(SHADOW_COPY_LABEL)) <
4642 state->out_output_buffer.length) {
4643 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4646 names = talloc_array(mem_ctx, char *, num_names_returned);
4647 if (names == NULL) {
4648 return NT_STATUS_NO_MEMORY;
4651 endp = state->out_output_buffer.data +
4652 state->out_output_buffer.length;
4654 for (i=0; i<num_names_returned; i++) {
4655 bool ret;
4656 uint8_t *src;
4657 size_t converted_size;
4659 src = state->out_output_buffer.data + 12 +
4660 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4662 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4663 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4665 ret = convert_string_talloc(
4666 names, CH_UTF16LE, CH_UNIX,
4667 src, 2 * sizeof(SHADOW_COPY_LABEL),
4668 &names[i], &converted_size);
4669 if (!ret) {
4670 TALLOC_FREE(names);
4671 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4674 *pnum_names = num_names;
4675 *pnames = names;
4676 return NT_STATUS_OK;
4679 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4680 struct cli_state *cli,
4681 uint16_t fnum,
4682 bool get_names,
4683 char ***pnames,
4684 int *pnum_names)
4686 TALLOC_CTX *frame = talloc_stackframe();
4687 struct tevent_context *ev;
4688 struct tevent_req *req;
4689 NTSTATUS status = NT_STATUS_NO_MEMORY;
4691 if (smbXcli_conn_has_async_calls(cli->conn)) {
4693 * Can't use sync call while an async call is in flight
4695 status = NT_STATUS_INVALID_PARAMETER;
4696 goto fail;
4698 ev = samba_tevent_context_init(frame);
4699 if (ev == NULL) {
4700 goto fail;
4702 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4704 cli,
4705 fnum,
4706 get_names);
4707 if (req == NULL) {
4708 goto fail;
4710 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4711 goto fail;
4713 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4714 mem_ctx,
4715 get_names,
4716 pnames,
4717 pnum_names);
4718 fail:
4719 TALLOC_FREE(frame);
4720 return status;
4723 /***************************************************************
4724 Wrapper that allows SMB2 to truncate a file.
4725 Synchronous only.
4726 ***************************************************************/
4728 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4729 uint16_t fnum,
4730 uint64_t newsize)
4732 NTSTATUS status;
4733 uint8_t buf[8] = {0};
4734 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4735 TALLOC_CTX *frame = talloc_stackframe();
4737 if (smbXcli_conn_has_async_calls(cli->conn)) {
4739 * Can't use sync call while an async call is in flight
4741 status = NT_STATUS_INVALID_PARAMETER;
4742 goto fail;
4745 SBVAL(buf, 0, newsize);
4747 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4748 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4750 status = cli_smb2_set_info_fnum(
4751 cli,
4752 fnum,
4753 SMB2_0_INFO_FILE, /* in_info_type */
4754 FSCC_FILE_END_OF_FILE_INFORMATION, /* in_file_info_class */
4755 &inbuf, /* in_input_buffer */
4758 fail:
4759 TALLOC_FREE(frame);
4760 return status;
4763 struct cli_smb2_notify_state {
4764 struct tevent_req *subreq;
4765 struct notify_change *changes;
4766 size_t num_changes;
4769 static void cli_smb2_notify_done(struct tevent_req *subreq);
4770 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4772 struct tevent_req *cli_smb2_notify_send(
4773 TALLOC_CTX *mem_ctx,
4774 struct tevent_context *ev,
4775 struct cli_state *cli,
4776 uint16_t fnum,
4777 uint32_t buffer_size,
4778 uint32_t completion_filter,
4779 bool recursive)
4781 struct tevent_req *req = NULL;
4782 struct cli_smb2_notify_state *state = NULL;
4783 struct smb2_hnd *ph = NULL;
4784 NTSTATUS status;
4786 req = tevent_req_create(mem_ctx, &state,
4787 struct cli_smb2_notify_state);
4788 if (req == NULL) {
4789 return NULL;
4792 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4793 if (tevent_req_nterror(req, status)) {
4794 return tevent_req_post(req, ev);
4797 state->subreq = smb2cli_notify_send(
4798 state,
4800 cli->conn,
4801 cli->timeout,
4802 cli->smb2.session,
4803 cli->smb2.tcon,
4804 buffer_size,
4805 ph->fid_persistent,
4806 ph->fid_volatile,
4807 completion_filter,
4808 recursive);
4809 if (tevent_req_nomem(state->subreq, req)) {
4810 return tevent_req_post(req, ev);
4812 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4813 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4814 return req;
4817 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4819 struct cli_smb2_notify_state *state = tevent_req_data(
4820 req, struct cli_smb2_notify_state);
4821 bool ok;
4823 ok = tevent_req_cancel(state->subreq);
4824 return ok;
4827 static void cli_smb2_notify_done(struct tevent_req *subreq)
4829 struct tevent_req *req = tevent_req_callback_data(
4830 subreq, struct tevent_req);
4831 struct cli_smb2_notify_state *state = tevent_req_data(
4832 req, struct cli_smb2_notify_state);
4833 uint8_t *base;
4834 uint32_t len;
4835 uint32_t ofs;
4836 NTSTATUS status;
4838 status = smb2cli_notify_recv(subreq, state, &base, &len);
4839 TALLOC_FREE(subreq);
4841 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4842 tevent_req_done(req);
4843 return;
4845 if (tevent_req_nterror(req, status)) {
4846 return;
4849 ofs = 0;
4851 while (len - ofs >= 12) {
4852 struct notify_change *tmp;
4853 struct notify_change *c;
4854 uint32_t next_ofs = IVAL(base, ofs);
4855 uint32_t file_name_length = IVAL(base, ofs+8);
4856 size_t namelen;
4857 bool ok;
4859 tmp = talloc_realloc(
4860 state,
4861 state->changes,
4862 struct notify_change,
4863 state->num_changes + 1);
4864 if (tevent_req_nomem(tmp, req)) {
4865 return;
4867 state->changes = tmp;
4868 c = &state->changes[state->num_changes];
4869 state->num_changes += 1;
4871 if (smb_buffer_oob(len, ofs, next_ofs) ||
4872 smb_buffer_oob(len, ofs+12, file_name_length)) {
4873 tevent_req_nterror(
4874 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4875 return;
4878 c->action = IVAL(base, ofs+4);
4880 ok = convert_string_talloc(
4881 state->changes,
4882 CH_UTF16LE,
4883 CH_UNIX,
4884 base + ofs + 12,
4885 file_name_length,
4886 &c->name,
4887 &namelen);
4888 if (!ok) {
4889 tevent_req_nterror(
4890 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4891 return;
4894 if (next_ofs == 0) {
4895 break;
4897 ofs += next_ofs;
4900 tevent_req_done(req);
4903 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4904 TALLOC_CTX *mem_ctx,
4905 struct notify_change **pchanges,
4906 uint32_t *pnum_changes)
4908 struct cli_smb2_notify_state *state = tevent_req_data(
4909 req, struct cli_smb2_notify_state);
4910 NTSTATUS status;
4912 if (tevent_req_is_nterror(req, &status)) {
4913 return status;
4915 *pchanges = talloc_move(mem_ctx, &state->changes);
4916 *pnum_changes = state->num_changes;
4917 return NT_STATUS_OK;
4920 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4921 uint32_t buffer_size, uint32_t completion_filter,
4922 bool recursive, TALLOC_CTX *mem_ctx,
4923 struct notify_change **pchanges,
4924 uint32_t *pnum_changes)
4926 TALLOC_CTX *frame = talloc_stackframe();
4927 struct tevent_context *ev;
4928 struct tevent_req *req;
4929 NTSTATUS status = NT_STATUS_NO_MEMORY;
4931 if (smbXcli_conn_has_async_calls(cli->conn)) {
4933 * Can't use sync call while an async call is in flight
4935 status = NT_STATUS_INVALID_PARAMETER;
4936 goto fail;
4938 ev = samba_tevent_context_init(frame);
4939 if (ev == NULL) {
4940 goto fail;
4942 req = cli_smb2_notify_send(
4943 frame,
4945 cli,
4946 fnum,
4947 buffer_size,
4948 completion_filter,
4949 recursive);
4950 if (req == NULL) {
4951 goto fail;
4953 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4954 goto fail;
4956 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4957 fail:
4958 TALLOC_FREE(frame);
4959 return status;
4962 struct cli_smb2_fsctl_state {
4963 DATA_BLOB out;
4966 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4968 struct tevent_req *cli_smb2_fsctl_send(
4969 TALLOC_CTX *mem_ctx,
4970 struct tevent_context *ev,
4971 struct cli_state *cli,
4972 uint16_t fnum,
4973 uint32_t ctl_code,
4974 const DATA_BLOB *in,
4975 uint32_t max_out)
4977 struct tevent_req *req = NULL, *subreq = NULL;
4978 struct cli_smb2_fsctl_state *state = NULL;
4979 struct smb2_hnd *ph = NULL;
4980 NTSTATUS status;
4982 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4983 if (req == NULL) {
4984 return NULL;
4987 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4988 if (tevent_req_nterror(req, status)) {
4989 return tevent_req_post(req, ev);
4992 subreq = smb2cli_ioctl_send(
4993 state,
4995 cli->conn,
4996 cli->timeout,
4997 cli->smb2.session,
4998 cli->smb2.tcon,
4999 ph->fid_persistent,
5000 ph->fid_volatile,
5001 ctl_code,
5002 0, /* in_max_input_length */
5004 max_out,
5005 NULL,
5006 SMB2_IOCTL_FLAG_IS_FSCTL);
5008 if (tevent_req_nomem(subreq, req)) {
5009 return tevent_req_post(req, ev);
5011 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
5012 return req;
5015 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5017 struct tevent_req *req = tevent_req_callback_data(
5018 subreq, struct tevent_req);
5019 struct cli_smb2_fsctl_state *state = tevent_req_data(
5020 req, struct cli_smb2_fsctl_state);
5021 NTSTATUS status;
5023 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5024 tevent_req_simple_finish_ntstatus(subreq, status);
5027 NTSTATUS cli_smb2_fsctl_recv(
5028 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5030 struct cli_smb2_fsctl_state *state = tevent_req_data(
5031 req, struct cli_smb2_fsctl_state);
5032 NTSTATUS status = NT_STATUS_OK;
5034 if (tevent_req_is_nterror(req, &status)) {
5035 tevent_req_received(req);
5036 return status;
5039 if (state->out.length == 0) {
5040 *out = (DATA_BLOB) { .data = NULL, };
5041 } else {
5043 * Can't use talloc_move() here, the outblobs from
5044 * smb2cli_ioctl_recv() are not standalone talloc
5045 * objects but just peek into the larger buffers
5046 * received, hanging off "state".
5048 *out = data_blob_talloc(
5049 mem_ctx, state->out.data, state->out.length);
5050 if (out->data == NULL) {
5051 status = NT_STATUS_NO_MEMORY;
5055 tevent_req_received(req);
5056 return NT_STATUS_OK;
5059 struct cli_smb2_get_posix_fs_info_state {
5060 struct tevent_context *ev;
5061 struct cli_state *cli;
5062 uint16_t fnum;
5063 uint32_t optimal_transfer_size;
5064 uint32_t block_size;
5065 uint64_t total_blocks;
5066 uint64_t blocks_available;
5067 uint64_t user_blocks_available;
5068 uint64_t total_file_nodes;
5069 uint64_t free_file_nodes;
5070 uint64_t fs_identifier;
5073 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req *subreq);
5074 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req *subreq);
5075 static void cli_smb2_get_posix_fs_info_done(struct tevent_req *subreq);
5077 struct tevent_req *cli_smb2_get_posix_fs_info_send(TALLOC_CTX *mem_ctx,
5078 struct tevent_context *ev,
5079 struct cli_state *cli)
5081 struct smb2_create_blobs *cblob = NULL;
5082 struct tevent_req *req = NULL, *subreq = NULL;
5083 struct cli_smb2_get_posix_fs_info_state *state = NULL;
5084 NTSTATUS status;
5086 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_get_posix_fs_info_state);
5087 if (req == NULL) {
5088 return NULL;
5090 *state = (struct cli_smb2_get_posix_fs_info_state) {
5091 .ev = ev,
5092 .cli = cli,
5094 status = make_smb2_posix_create_ctx(state, &cblob, 0);
5095 if (!NT_STATUS_IS_OK(status)) {
5096 return NULL;
5099 /* First open the top level directory. */
5100 subreq = cli_smb2_create_fnum_send(state,
5101 state->ev,
5102 state->cli,
5104 (struct cli_smb2_create_flags){0},
5105 SMB2_IMPERSONATION_IMPERSONATION,
5106 FILE_READ_ATTRIBUTES,
5107 FILE_ATTRIBUTE_DIRECTORY,
5108 FILE_SHARE_READ | FILE_SHARE_WRITE |
5109 FILE_SHARE_DELETE,
5110 FILE_OPEN,
5111 FILE_DIRECTORY_FILE,
5112 cblob);
5113 if (tevent_req_nomem(subreq, req)) {
5114 return tevent_req_post(req, ev);
5117 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_opened, req);
5118 return req;
5121 static void cli_smb2_get_posix_fs_info_opened(struct tevent_req *subreq)
5123 struct tevent_req *req = tevent_req_callback_data(
5124 subreq, struct tevent_req);
5125 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5126 req, struct cli_smb2_get_posix_fs_info_state);
5127 struct smb2_create_blobs *cblob = {0};
5128 NTSTATUS status;
5130 status = cli_smb2_create_fnum_recv(subreq,
5131 &state->fnum,
5132 NULL,
5133 state,
5134 cblob,
5135 NULL);
5136 TALLOC_FREE(subreq);
5138 if (tevent_req_nterror(req, status)) {
5139 return;
5142 subreq = cli_smb2_query_info_fnum_send(
5143 state,
5144 state->ev,
5145 state->cli,
5146 state->fnum,
5147 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
5148 FSCC_FS_POSIX_INFORMATION, /* in_file_info_class */
5149 0xFFFF, /* in_max_output_length */
5150 NULL, /* in_input_buffer */
5151 0, /* in_additional_info */
5152 0); /* in_flags */
5153 if (tevent_req_nomem(subreq, req)) {
5154 return;
5157 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_queried, req);
5160 static void cli_smb2_get_posix_fs_info_queried(struct tevent_req *subreq)
5162 struct tevent_req *req = tevent_req_callback_data(
5163 subreq, struct tevent_req);
5164 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5165 req, struct cli_smb2_get_posix_fs_info_state);
5166 DATA_BLOB outbuf = data_blob_null;
5167 NTSTATUS status;
5169 status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf);
5170 TALLOC_FREE(subreq);
5172 if (tevent_req_nterror(req, status)) {
5173 return;
5176 if (outbuf.length != 56) {
5177 goto close;
5180 state->optimal_transfer_size = PULL_LE_U32(outbuf.data, 0);
5181 state->block_size = PULL_LE_U32(outbuf.data, 4);
5182 state->total_blocks = PULL_LE_U64(outbuf.data, 8);
5183 state->blocks_available = PULL_LE_U64(outbuf.data, 16);
5184 state->user_blocks_available = PULL_LE_U64(outbuf.data, 24);
5185 state->total_file_nodes = PULL_LE_U64(outbuf.data, 32);
5186 state->free_file_nodes = PULL_LE_U64(outbuf.data, 40);
5187 state->fs_identifier = PULL_LE_U64(outbuf.data, 48);
5189 close:
5190 subreq = cli_smb2_close_fnum_send(state,
5191 state->ev,
5192 state->cli,
5193 state->fnum,
5195 if (tevent_req_nomem(subreq, req)) {
5196 return;
5199 tevent_req_set_callback(subreq, cli_smb2_get_posix_fs_info_done, req);
5202 static void cli_smb2_get_posix_fs_info_done(struct tevent_req *subreq)
5204 struct tevent_req *req = tevent_req_callback_data(
5205 subreq, struct tevent_req);
5206 NTSTATUS status;
5208 status = cli_smb2_close_fnum_recv(subreq);
5209 TALLOC_FREE(subreq);
5210 if (tevent_req_nterror(req, status)) {
5211 return;
5214 tevent_req_done(req);
5217 NTSTATUS cli_smb2_get_posix_fs_info_recv(struct tevent_req *req,
5218 uint32_t *optimal_transfer_size,
5219 uint32_t *block_size,
5220 uint64_t *total_blocks,
5221 uint64_t *blocks_available,
5222 uint64_t *user_blocks_available,
5223 uint64_t *total_file_nodes,
5224 uint64_t *free_file_nodes,
5225 uint64_t *fs_identifier)
5227 struct cli_smb2_get_posix_fs_info_state *state = tevent_req_data(
5228 req, struct cli_smb2_get_posix_fs_info_state);
5229 NTSTATUS status;
5231 if (tevent_req_is_nterror(req, &status)) {
5232 tevent_req_received(req);
5233 return status;
5235 *optimal_transfer_size = state->optimal_transfer_size;
5236 *block_size = state->block_size;
5237 *total_blocks = state->total_blocks;
5238 *blocks_available = state->blocks_available;
5239 *user_blocks_available = state->user_blocks_available;
5240 *total_file_nodes = state->total_file_nodes;
5241 *free_file_nodes = state->free_file_nodes;
5242 *fs_identifier = state->fs_identifier;
5243 tevent_req_received(req);
5244 return NT_STATUS_OK;