libsmb: Use clistr_smb2_extract_snapshot_token() in cli_smb2_create_fnum_send()
[Samba.git] / source3 / libsmb / cli_smb2_fnum.c
blob6db9fca612c28176a19088417de52d0b246c40cb
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 "lib/util/string_wrappers.h"
47 struct smb2_hnd {
48 uint64_t fid_persistent;
49 uint64_t fid_volatile;
53 * Handle mapping code.
56 /***************************************************************
57 Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
58 Ensures handle is owned by cli struct.
59 ***************************************************************/
61 static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
62 const struct smb2_hnd *ph, /* In */
63 uint16_t *pfnum) /* Out */
65 int ret;
66 struct idr_context *idp = cli->smb2.open_handles;
67 struct smb2_hnd *owned_h = talloc_memdup(cli,
68 ph,
69 sizeof(struct smb2_hnd));
71 if (owned_h == NULL) {
72 return NT_STATUS_NO_MEMORY;
75 if (idp == NULL) {
76 /* Lazy init */
77 cli->smb2.open_handles = idr_init(cli);
78 if (cli->smb2.open_handles == NULL) {
79 TALLOC_FREE(owned_h);
80 return NT_STATUS_NO_MEMORY;
82 idp = cli->smb2.open_handles;
85 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
86 if (ret == -1) {
87 TALLOC_FREE(owned_h);
88 return NT_STATUS_NO_MEMORY;
91 *pfnum = (uint16_t)ret;
92 return NT_STATUS_OK;
95 /***************************************************************
96 Return the smb2_hnd pointer associated with the given fnum.
97 ***************************************************************/
99 static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
100 uint16_t fnum, /* In */
101 struct smb2_hnd **pph) /* Out */
103 struct idr_context *idp = cli->smb2.open_handles;
105 if (idp == NULL) {
106 return NT_STATUS_INVALID_PARAMETER;
108 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
109 if (*pph == NULL) {
110 return NT_STATUS_INVALID_HANDLE;
112 return NT_STATUS_OK;
115 /***************************************************************
116 Delete the fnum to smb2_hnd mapping. Zeros out handle on
117 successful return.
118 ***************************************************************/
120 static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
121 struct smb2_hnd **pph, /* In */
122 uint16_t fnum) /* In */
124 struct idr_context *idp = cli->smb2.open_handles;
125 struct smb2_hnd *ph;
127 if (idp == NULL) {
128 return NT_STATUS_INVALID_PARAMETER;
131 ph = (struct smb2_hnd *)idr_find(idp, fnum);
132 if (ph != *pph) {
133 return NT_STATUS_INVALID_PARAMETER;
135 idr_remove(idp, fnum);
136 TALLOC_FREE(*pph);
137 return NT_STATUS_OK;
140 /***************************************************************
141 Oplock mapping code.
142 ***************************************************************/
144 static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
146 if (create_flags & REQUEST_BATCH_OPLOCK) {
147 return SMB2_OPLOCK_LEVEL_BATCH;
148 } else if (create_flags & REQUEST_OPLOCK) {
149 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
152 /* create_flags doesn't do a level2 request. */
153 return SMB2_OPLOCK_LEVEL_NONE;
156 /***************************************************************
157 If we're on a DFS share, ensure we convert to a full DFS path
158 if this hasn't already been done.
159 ***************************************************************/
161 static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
162 struct cli_state *cli,
163 char *path)
165 bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
166 smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
167 bool is_already_dfs_path = false;
169 if (!is_dfs) {
170 return path;
172 is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
173 if (is_already_dfs_path) {
174 return path;
176 if (path[0] == '\0') {
177 return talloc_asprintf(ctx,
178 "%s\\%s",
179 smbXcli_conn_remote_name(cli->conn),
180 cli->share);
182 while (*path == '\\') {
183 path++;
185 return talloc_asprintf(ctx,
186 "%s\\%s\\%s",
187 smbXcli_conn_remote_name(cli->conn),
188 cli->share,
189 path);
192 /***************************************************************
193 Small wrapper that allows SMB2 create to return a uint16_t fnum.
194 ***************************************************************/
196 struct cli_smb2_create_fnum_state {
197 struct cli_state *cli;
198 struct smb2_create_blobs in_cblobs;
199 struct smb2_create_blobs out_cblobs;
200 struct smb_create_returns cr;
201 struct symlink_reparse_struct *symlink;
202 uint16_t fnum;
203 struct tevent_req *subreq;
206 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
207 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
209 struct tevent_req *cli_smb2_create_fnum_send(
210 TALLOC_CTX *mem_ctx,
211 struct tevent_context *ev,
212 struct cli_state *cli,
213 const char *fname_in,
214 uint32_t create_flags,
215 uint32_t impersonation_level,
216 uint32_t desired_access,
217 uint32_t file_attributes,
218 uint32_t share_access,
219 uint32_t create_disposition,
220 uint32_t create_options,
221 const struct smb2_create_blobs *in_cblobs)
223 struct tevent_req *req, *subreq;
224 struct cli_smb2_create_fnum_state *state;
225 char *fname = NULL;
226 size_t fname_len = 0;
227 bool have_twrp;
228 NTTIME ntt;
229 NTSTATUS status;
231 req = tevent_req_create(mem_ctx, &state,
232 struct cli_smb2_create_fnum_state);
233 if (req == NULL) {
234 return NULL;
236 state->cli = cli;
238 fname = talloc_strdup(state, fname_in);
239 if (tevent_req_nomem(fname, req)) {
240 return tevent_req_post(req, ev);
243 if (cli->backup_intent) {
244 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
247 /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
248 have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
249 if (have_twrp) {
250 status = smb2_create_blob_add(
251 state,
252 &state->in_cblobs,
253 SMB2_CREATE_TAG_TWRP,
254 (DATA_BLOB) {
255 .data = (uint8_t *)&ntt,
256 .length = sizeof(ntt),
258 if (tevent_req_nterror(req, status)) {
259 return tevent_req_post(req, ev);
263 if (in_cblobs != NULL) {
264 uint32_t i;
265 for (i=0; i<in_cblobs->num_blobs; i++) {
266 struct smb2_create_blob *b = &in_cblobs->blobs[i];
267 status = smb2_create_blob_add(
268 state, &state->in_cblobs, b->tag, b->data);
269 if (!NT_STATUS_IS_OK(status)) {
270 tevent_req_nterror(req, status);
271 return tevent_req_post(req, ev);
276 fname = smb2_dfs_share_path(state, cli, fname);
277 if (tevent_req_nomem(fname, req)) {
278 return tevent_req_post(req, ev);
280 fname_len = strlen(fname);
282 /* SMB2 is pickier about pathnames. Ensure it doesn't
283 start in a '\' */
284 if (*fname == '\\') {
285 fname++;
286 fname_len--;
289 /* Or end in a '\' */
290 if (fname_len > 0 && fname[fname_len-1] == '\\') {
291 char *new_fname = talloc_strdup(state, fname);
292 if (tevent_req_nomem(new_fname, req)) {
293 return tevent_req_post(req, ev);
295 new_fname[fname_len-1] = '\0';
296 fname = new_fname;
299 subreq = smb2cli_create_send(state, ev,
300 cli->conn,
301 cli->timeout,
302 cli->smb2.session,
303 cli->smb2.tcon,
304 fname,
305 flags_to_smb2_oplock(create_flags),
306 impersonation_level,
307 desired_access,
308 file_attributes,
309 share_access,
310 create_disposition,
311 create_options,
312 &state->in_cblobs);
313 if (tevent_req_nomem(subreq, req)) {
314 return tevent_req_post(req, ev);
316 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
318 state->subreq = subreq;
319 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
321 return req;
324 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
326 struct tevent_req *req = tevent_req_callback_data(
327 subreq, struct tevent_req);
328 struct cli_smb2_create_fnum_state *state = tevent_req_data(
329 req, struct cli_smb2_create_fnum_state);
330 struct smb2_hnd h;
331 NTSTATUS status;
333 status = smb2cli_create_recv(
334 subreq,
335 &h.fid_persistent,
336 &h.fid_volatile, &state->cr,
337 state,
338 &state->out_cblobs,
339 &state->symlink);
340 TALLOC_FREE(subreq);
341 if (tevent_req_nterror(req, status)) {
342 return;
345 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
346 if (tevent_req_nterror(req, status)) {
347 return;
349 tevent_req_done(req);
352 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
354 struct cli_smb2_create_fnum_state *state = tevent_req_data(
355 req, struct cli_smb2_create_fnum_state);
356 return tevent_req_cancel(state->subreq);
359 NTSTATUS cli_smb2_create_fnum_recv(
360 struct tevent_req *req,
361 uint16_t *pfnum,
362 struct smb_create_returns *cr,
363 TALLOC_CTX *mem_ctx,
364 struct smb2_create_blobs *out_cblobs,
365 struct symlink_reparse_struct **symlink)
367 struct cli_smb2_create_fnum_state *state = tevent_req_data(
368 req, struct cli_smb2_create_fnum_state);
369 NTSTATUS status;
371 if (tevent_req_is_nterror(req, &status)) {
372 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
373 (symlink != NULL)) {
374 *symlink = talloc_move(mem_ctx, &state->symlink);
376 state->cli->raw_status = status;
377 return status;
379 if (pfnum != NULL) {
380 *pfnum = state->fnum;
382 if (cr != NULL) {
383 *cr = state->cr;
385 if (out_cblobs != NULL) {
386 *out_cblobs = (struct smb2_create_blobs) {
387 .num_blobs = state->out_cblobs.num_blobs,
388 .blobs = talloc_move(
389 mem_ctx, &state->out_cblobs.blobs),
392 state->cli->raw_status = NT_STATUS_OK;
393 return NT_STATUS_OK;
396 NTSTATUS cli_smb2_create_fnum(
397 struct cli_state *cli,
398 const char *fname,
399 uint32_t create_flags,
400 uint32_t impersonation_level,
401 uint32_t desired_access,
402 uint32_t file_attributes,
403 uint32_t share_access,
404 uint32_t create_disposition,
405 uint32_t create_options,
406 const struct smb2_create_blobs *in_cblobs,
407 uint16_t *pfid,
408 struct smb_create_returns *cr,
409 TALLOC_CTX *mem_ctx,
410 struct smb2_create_blobs *out_cblobs)
412 TALLOC_CTX *frame = talloc_stackframe();
413 struct tevent_context *ev;
414 struct tevent_req *req;
415 NTSTATUS status = NT_STATUS_NO_MEMORY;
417 if (smbXcli_conn_has_async_calls(cli->conn)) {
419 * Can't use sync call while an async call is in flight
421 status = NT_STATUS_INVALID_PARAMETER;
422 goto fail;
424 ev = samba_tevent_context_init(frame);
425 if (ev == NULL) {
426 goto fail;
428 req = cli_smb2_create_fnum_send(
429 frame,
431 cli,
432 fname,
433 create_flags,
434 impersonation_level,
435 desired_access,
436 file_attributes,
437 share_access,
438 create_disposition,
439 create_options,
440 in_cblobs);
441 if (req == NULL) {
442 goto fail;
444 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
445 goto fail;
447 status = cli_smb2_create_fnum_recv(
448 req, pfid, cr, mem_ctx, out_cblobs, NULL);
449 fail:
450 TALLOC_FREE(frame);
451 return status;
454 /***************************************************************
455 Small wrapper that allows SMB2 close to use a uint16_t fnum.
456 ***************************************************************/
458 struct cli_smb2_close_fnum_state {
459 struct cli_state *cli;
460 uint16_t fnum;
461 struct smb2_hnd *ph;
464 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
466 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
467 struct tevent_context *ev,
468 struct cli_state *cli,
469 uint16_t fnum)
471 struct tevent_req *req, *subreq;
472 struct cli_smb2_close_fnum_state *state;
473 NTSTATUS status;
475 req = tevent_req_create(mem_ctx, &state,
476 struct cli_smb2_close_fnum_state);
477 if (req == NULL) {
478 return NULL;
480 state->cli = cli;
481 state->fnum = fnum;
483 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
484 if (tevent_req_nterror(req, status)) {
485 return tevent_req_post(req, ev);
488 subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
489 cli->smb2.session, cli->smb2.tcon,
490 0, state->ph->fid_persistent,
491 state->ph->fid_volatile);
492 if (tevent_req_nomem(subreq, req)) {
493 return tevent_req_post(req, ev);
495 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
496 return req;
499 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
501 struct tevent_req *req = tevent_req_callback_data(
502 subreq, struct tevent_req);
503 struct cli_smb2_close_fnum_state *state = tevent_req_data(
504 req, struct cli_smb2_close_fnum_state);
505 NTSTATUS status;
507 status = smb2cli_close_recv(subreq);
508 if (tevent_req_nterror(req, status)) {
509 return;
512 /* Delete the fnum -> handle mapping. */
513 status = delete_smb2_handle_mapping(state->cli, &state->ph,
514 state->fnum);
515 if (tevent_req_nterror(req, status)) {
516 return;
518 tevent_req_done(req);
521 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
523 struct cli_smb2_close_fnum_state *state = tevent_req_data(
524 req, struct cli_smb2_close_fnum_state);
525 NTSTATUS status = NT_STATUS_OK;
527 if (tevent_req_is_nterror(req, &status)) {
528 state->cli->raw_status = status;
530 tevent_req_received(req);
531 return status;
534 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
536 TALLOC_CTX *frame = talloc_stackframe();
537 struct tevent_context *ev;
538 struct tevent_req *req;
539 NTSTATUS status = NT_STATUS_NO_MEMORY;
541 if (smbXcli_conn_has_async_calls(cli->conn)) {
543 * Can't use sync call while an async call is in flight
545 status = NT_STATUS_INVALID_PARAMETER;
546 goto fail;
548 ev = samba_tevent_context_init(frame);
549 if (ev == NULL) {
550 goto fail;
552 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
553 if (req == NULL) {
554 goto fail;
556 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
557 goto fail;
559 status = cli_smb2_close_fnum_recv(req);
560 fail:
561 TALLOC_FREE(frame);
562 return status;
565 struct cli_smb2_set_info_fnum_state {
566 uint8_t dummy;
569 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
571 struct tevent_req *cli_smb2_set_info_fnum_send(
572 TALLOC_CTX *mem_ctx,
573 struct tevent_context *ev,
574 struct cli_state *cli,
575 uint16_t fnum,
576 uint8_t in_info_type,
577 uint8_t in_info_class,
578 const DATA_BLOB *in_input_buffer,
579 uint32_t in_additional_info)
581 struct tevent_req *req = NULL, *subreq = NULL;
582 struct cli_smb2_set_info_fnum_state *state = NULL;
583 struct smb2_hnd *ph = NULL;
584 NTSTATUS status;
586 req = tevent_req_create(
587 mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
588 if (req == NULL) {
589 return NULL;
592 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
593 if (tevent_req_nterror(req, status)) {
594 return tevent_req_post(req, ev);
597 subreq = smb2cli_set_info_send(
598 state,
600 cli->conn,
601 cli->timeout,
602 cli->smb2.session,
603 cli->smb2.tcon,
604 in_info_type,
605 in_info_class,
606 in_input_buffer,
607 in_additional_info,
608 ph->fid_persistent,
609 ph->fid_volatile);
610 if (tevent_req_nomem(subreq, req)) {
611 return tevent_req_post(req, ev);
613 tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
614 return req;
617 static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
619 NTSTATUS status = smb2cli_set_info_recv(subreq);
620 tevent_req_simple_finish_ntstatus(subreq, status);
623 NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
625 return tevent_req_simple_recv_ntstatus(req);
628 NTSTATUS cli_smb2_set_info_fnum(
629 struct cli_state *cli,
630 uint16_t fnum,
631 uint8_t in_info_type,
632 uint8_t in_info_class,
633 const DATA_BLOB *in_input_buffer,
634 uint32_t in_additional_info)
636 TALLOC_CTX *frame = talloc_stackframe();
637 struct tevent_context *ev = NULL;
638 struct tevent_req *req = NULL;
639 NTSTATUS status = NT_STATUS_NO_MEMORY;
640 bool ok;
642 if (smbXcli_conn_has_async_calls(cli->conn)) {
644 * Can't use sync call while an async call is in flight
646 status = NT_STATUS_INVALID_PARAMETER;
647 goto fail;
649 ev = samba_tevent_context_init(frame);
650 if (ev == NULL) {
651 goto fail;
653 req = cli_smb2_set_info_fnum_send(
654 frame,
656 cli,
657 fnum,
658 in_info_type,
659 in_info_class,
660 in_input_buffer,
661 in_additional_info);
662 if (req == NULL) {
663 goto fail;
665 ok = tevent_req_poll_ntstatus(req, ev, &status);
666 if (!ok) {
667 goto fail;
669 status = cli_smb2_set_info_fnum_recv(req);
670 fail:
671 TALLOC_FREE(frame);
672 return status;
675 struct cli_smb2_delete_on_close_state {
676 struct cli_state *cli;
677 uint8_t data[1];
678 DATA_BLOB inbuf;
681 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
683 struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
684 struct tevent_context *ev,
685 struct cli_state *cli,
686 uint16_t fnum,
687 bool flag)
689 struct tevent_req *req = NULL;
690 struct cli_smb2_delete_on_close_state *state = NULL;
691 struct tevent_req *subreq = NULL;
692 uint8_t in_info_type;
693 uint8_t in_file_info_class;
695 req = tevent_req_create(mem_ctx, &state,
696 struct cli_smb2_delete_on_close_state);
697 if (req == NULL) {
698 return NULL;
700 state->cli = cli;
703 * setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
704 * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000).
706 in_info_type = 1;
707 in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000;
708 /* Setup data array. */
709 SCVAL(&state->data[0], 0, flag ? 1 : 0);
710 state->inbuf.data = &state->data[0];
711 state->inbuf.length = 1;
713 subreq = cli_smb2_set_info_fnum_send(
714 state,
716 cli,
717 fnum,
718 in_info_type,
719 in_file_info_class,
720 &state->inbuf,
722 if (tevent_req_nomem(subreq, req)) {
723 return tevent_req_post(req, ev);
725 tevent_req_set_callback(subreq,
726 cli_smb2_delete_on_close_done,
727 req);
728 return req;
731 static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
733 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
734 tevent_req_simple_finish_ntstatus(subreq, status);
737 NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
739 struct cli_smb2_delete_on_close_state *state =
740 tevent_req_data(req,
741 struct cli_smb2_delete_on_close_state);
742 NTSTATUS status;
744 if (tevent_req_is_nterror(req, &status)) {
745 state->cli->raw_status = status;
746 tevent_req_received(req);
747 return status;
750 state->cli->raw_status = NT_STATUS_OK;
751 tevent_req_received(req);
752 return NT_STATUS_OK;
755 NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
757 TALLOC_CTX *frame = talloc_stackframe();
758 struct tevent_context *ev;
759 struct tevent_req *req;
760 NTSTATUS status = NT_STATUS_NO_MEMORY;
762 if (smbXcli_conn_has_async_calls(cli->conn)) {
764 * Can't use sync call while an async call is in flight
766 status = NT_STATUS_INVALID_PARAMETER;
767 goto fail;
769 ev = samba_tevent_context_init(frame);
770 if (ev == NULL) {
771 goto fail;
773 req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
774 if (req == NULL) {
775 goto fail;
777 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
778 goto fail;
780 status = cli_smb2_delete_on_close_recv(req);
781 fail:
782 TALLOC_FREE(frame);
783 return status;
786 struct cli_smb2_mkdir_state {
787 struct tevent_context *ev;
788 struct cli_state *cli;
791 static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
792 static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
794 struct tevent_req *cli_smb2_mkdir_send(
795 TALLOC_CTX *mem_ctx,
796 struct tevent_context *ev,
797 struct cli_state *cli,
798 const char *dname)
800 struct tevent_req *req = NULL, *subreq = NULL;
801 struct cli_smb2_mkdir_state *state = NULL;
803 req = tevent_req_create(
804 mem_ctx, &state, struct cli_smb2_mkdir_state);
805 if (req == NULL) {
806 return NULL;
808 state->ev = ev;
809 state->cli = cli;
811 /* Ensure this is a directory. */
812 subreq = cli_smb2_create_fnum_send(
813 state, /* mem_ctx */
814 ev, /* ev */
815 cli, /* cli */
816 dname, /* fname */
817 0, /* create_flags */
818 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
819 FILE_READ_ATTRIBUTES, /* desired_access */
820 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
821 FILE_SHARE_READ|
822 FILE_SHARE_WRITE, /* share_access */
823 FILE_CREATE, /* create_disposition */
824 FILE_DIRECTORY_FILE, /* create_options */
825 NULL); /* in_cblobs */
826 if (tevent_req_nomem(subreq, req)) {
827 return tevent_req_post(req, ev);
829 tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
830 return req;
833 static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
835 struct tevent_req *req = tevent_req_callback_data(
836 subreq, struct tevent_req);
837 struct cli_smb2_mkdir_state *state = tevent_req_data(
838 req, struct cli_smb2_mkdir_state);
839 NTSTATUS status;
840 uint16_t fnum = 0xffff;
842 status = cli_smb2_create_fnum_recv(
843 subreq, &fnum, NULL, NULL, NULL, NULL);
844 TALLOC_FREE(subreq);
845 if (tevent_req_nterror(req, status)) {
846 return;
849 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
850 if (tevent_req_nomem(subreq, req)) {
851 return;
853 tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
856 static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
858 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
859 tevent_req_simple_finish_ntstatus(subreq, status);
862 NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
864 return tevent_req_simple_recv_ntstatus(req);
867 struct cli_smb2_rmdir_state {
868 struct tevent_context *ev;
869 struct cli_state *cli;
870 const char *dname;
871 const struct smb2_create_blobs *in_cblobs;
872 uint16_t fnum;
873 NTSTATUS status;
876 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
877 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
878 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
879 static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
881 struct tevent_req *cli_smb2_rmdir_send(
882 TALLOC_CTX *mem_ctx,
883 struct tevent_context *ev,
884 struct cli_state *cli,
885 const char *dname,
886 const struct smb2_create_blobs *in_cblobs)
888 struct tevent_req *req = NULL, *subreq = NULL;
889 struct cli_smb2_rmdir_state *state = NULL;
891 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
892 if (req == NULL) {
893 return NULL;
895 state->ev = ev;
896 state->cli = cli;
897 state->dname = dname;
898 state->in_cblobs = in_cblobs;
900 subreq = cli_smb2_create_fnum_send(
901 state,
902 state->ev,
903 state->cli,
904 state->dname,
905 0, /* create_flags */
906 SMB2_IMPERSONATION_IMPERSONATION,
907 DELETE_ACCESS, /* desired_access */
908 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
909 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
910 FILE_OPEN, /* create_disposition */
911 FILE_DIRECTORY_FILE, /* create_options */
912 state->in_cblobs); /* in_cblobs */
913 if (tevent_req_nomem(subreq, req)) {
914 return tevent_req_post(req, ev);
916 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
917 return req;
920 static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
922 struct tevent_req *req = tevent_req_callback_data(
923 subreq, struct tevent_req);
924 struct cli_smb2_rmdir_state *state = tevent_req_data(
925 req, struct cli_smb2_rmdir_state);
926 NTSTATUS status;
928 status = cli_smb2_create_fnum_recv(
929 subreq, &state->fnum, NULL, NULL, NULL, NULL);
930 TALLOC_FREE(subreq);
932 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
934 * Naive option to match our SMB1 code. Assume the
935 * symlink path that tripped us up was the last
936 * component and try again. Eventually we will have to
937 * deal with the returned path unprocessed component. JRA.
939 subreq = cli_smb2_create_fnum_send(
940 state,
941 state->ev,
942 state->cli,
943 state->dname,
944 0, /* create_flags */
945 SMB2_IMPERSONATION_IMPERSONATION,
946 DELETE_ACCESS, /* desired_access */
947 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
948 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
949 FILE_OPEN, /* create_disposition */
950 FILE_DIRECTORY_FILE|
951 FILE_DELETE_ON_CLOSE|
952 FILE_OPEN_REPARSE_POINT, /* create_options */
953 state->in_cblobs); /* in_cblobs */
954 if (tevent_req_nomem(subreq, req)) {
955 return;
957 tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
958 return;
961 if (tevent_req_nterror(req, status)) {
962 return;
965 subreq = cli_smb2_delete_on_close_send(
966 state, state->ev, state->cli, state->fnum, true);
967 if (tevent_req_nomem(subreq, req)) {
968 return;
970 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
973 static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
975 struct tevent_req *req = tevent_req_callback_data(
976 subreq, struct tevent_req);
977 struct cli_smb2_rmdir_state *state = tevent_req_data(
978 req, struct cli_smb2_rmdir_state);
979 NTSTATUS status;
981 status = cli_smb2_create_fnum_recv(
982 subreq, &state->fnum, NULL, NULL, NULL, NULL);
983 TALLOC_FREE(subreq);
984 if (tevent_req_nterror(req, status)) {
985 return;
988 subreq = cli_smb2_delete_on_close_send(
989 state, state->ev, state->cli, state->fnum, true);
990 if (tevent_req_nomem(subreq, req)) {
991 return;
993 tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
996 static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
998 struct tevent_req *req = tevent_req_callback_data(
999 subreq, struct tevent_req);
1000 struct cli_smb2_rmdir_state *state = tevent_req_data(
1001 req, struct cli_smb2_rmdir_state);
1003 state->status = cli_smb2_delete_on_close_recv(subreq);
1004 TALLOC_FREE(subreq);
1007 * Close the fd even if the set_disp failed
1010 subreq = cli_smb2_close_fnum_send(
1011 state, state->ev, state->cli, state->fnum);
1012 if (tevent_req_nomem(subreq, req)) {
1013 return;
1015 tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1018 static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1020 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1021 tevent_req_simple_finish_ntstatus(subreq, status);
1024 NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1026 struct cli_smb2_rmdir_state *state = tevent_req_data(
1027 req, struct cli_smb2_rmdir_state);
1028 NTSTATUS status;
1030 if (tevent_req_is_nterror(req, &status)) {
1031 return status;
1033 return state->status;
1036 /***************************************************************
1037 Small wrapper that allows SMB2 to unlink a pathname.
1038 ***************************************************************/
1040 struct cli_smb2_unlink_state {
1041 struct tevent_context *ev;
1042 struct cli_state *cli;
1043 const char *fname;
1044 const struct smb2_create_blobs *in_cblobs;
1047 static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1048 static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1049 static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1051 struct tevent_req *cli_smb2_unlink_send(
1052 TALLOC_CTX *mem_ctx,
1053 struct tevent_context *ev,
1054 struct cli_state *cli,
1055 const char *fname,
1056 const struct smb2_create_blobs *in_cblobs)
1058 struct tevent_req *req = NULL, *subreq = NULL;
1059 struct cli_smb2_unlink_state *state = NULL;
1061 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1062 if (req == NULL) {
1063 return NULL;
1065 state->ev = ev;
1066 state->cli = cli;
1067 state->fname = fname;
1068 state->in_cblobs = in_cblobs;
1070 subreq = cli_smb2_create_fnum_send(
1071 state, /* mem_ctx */
1072 state->ev, /* tevent_context */
1073 state->cli, /* cli_struct */
1074 state->fname, /* filename */
1075 0, /* create_flags */
1076 SMB2_IMPERSONATION_IMPERSONATION,
1077 DELETE_ACCESS, /* desired_access */
1078 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1079 FILE_SHARE_READ|
1080 FILE_SHARE_WRITE|
1081 FILE_SHARE_DELETE, /* share_access */
1082 FILE_OPEN, /* create_disposition */
1083 FILE_DELETE_ON_CLOSE, /* create_options */
1084 state->in_cblobs); /* in_cblobs */
1085 if (tevent_req_nomem(subreq, req)) {
1086 return tevent_req_post(req, ev);
1088 tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1089 return req;
1092 static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1094 struct tevent_req *req = tevent_req_callback_data(
1095 subreq, struct tevent_req);
1096 struct cli_smb2_unlink_state *state = tevent_req_data(
1097 req, struct cli_smb2_unlink_state);
1098 uint16_t fnum = 0xffff;
1099 NTSTATUS status;
1101 status = cli_smb2_create_fnum_recv(
1102 subreq, &fnum, NULL, NULL, NULL, NULL);
1103 TALLOC_FREE(subreq);
1105 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1106 NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1108 * Naive option to match our SMB1 code. Assume the
1109 * symlink path that tripped us up was the last
1110 * component and try again. Eventually we will have to
1111 * deal with the returned path unprocessed component. JRA.
1113 subreq = cli_smb2_create_fnum_send(
1114 state, /* mem_ctx */
1115 state->ev, /* tevent_context */
1116 state->cli, /* cli_struct */
1117 state->fname, /* filename */
1118 0, /* create_flags */
1119 SMB2_IMPERSONATION_IMPERSONATION,
1120 DELETE_ACCESS, /* desired_access */
1121 FILE_ATTRIBUTE_NORMAL, /* file attributes */
1122 FILE_SHARE_READ|
1123 FILE_SHARE_WRITE|
1124 FILE_SHARE_DELETE, /* share_access */
1125 FILE_OPEN, /* create_disposition */
1126 FILE_DELETE_ON_CLOSE|
1127 FILE_OPEN_REPARSE_POINT, /* create_options */
1128 state->in_cblobs); /* in_cblobs */
1129 if (tevent_req_nomem(subreq, req)) {
1130 return;
1132 tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1133 return;
1136 if (tevent_req_nterror(req, status)) {
1137 return;
1140 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1141 if (tevent_req_nomem(subreq, req)) {
1142 return;
1144 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1147 static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1149 struct tevent_req *req = tevent_req_callback_data(
1150 subreq, struct tevent_req);
1151 struct cli_smb2_unlink_state *state = tevent_req_data(
1152 req, struct cli_smb2_unlink_state);
1153 uint16_t fnum = 0xffff;
1154 NTSTATUS status;
1156 status = cli_smb2_create_fnum_recv(
1157 subreq, &fnum, NULL, NULL, NULL, NULL);
1158 TALLOC_FREE(subreq);
1159 if (tevent_req_nterror(req, status)) {
1160 return;
1163 subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum);
1164 if (tevent_req_nomem(subreq, req)) {
1165 return;
1167 tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1170 static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1172 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1173 tevent_req_simple_finish_ntstatus(subreq, status);
1176 NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1178 return tevent_req_simple_recv_ntstatus(req);
1181 static ssize_t sid_parse_wire(TALLOC_CTX *mem_ctx, const uint8_t *data,
1182 struct dom_sid *sid, size_t num_rdata)
1184 size_t sid_size;
1185 enum ndr_err_code ndr_err;
1186 DATA_BLOB in = data_blob_const(data, num_rdata);
1188 ndr_err = ndr_pull_struct_blob(&in,
1189 mem_ctx,
1190 sid,
1191 (ndr_pull_flags_fn_t)ndr_pull_dom_sid);
1192 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1193 return 0;
1196 sid_size = ndr_size_dom_sid(sid, 0);
1197 if (sid_size > num_rdata) {
1198 return 0;
1201 return sid_size;
1204 /***************************************************************
1205 Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1206 ***************************************************************/
1208 static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1209 uint32_t dir_data_length,
1210 struct file_info *finfo,
1211 uint32_t *next_offset)
1213 size_t namelen = 0;
1214 size_t slen = 0, slen2 = 0;
1215 size_t ret = 0;
1216 uint32_t _next_offset = 0;
1218 if (dir_data_length < 4) {
1219 return NT_STATUS_INFO_LENGTH_MISMATCH;
1222 _next_offset = IVAL(dir_data, 0);
1224 if (_next_offset > dir_data_length) {
1225 return NT_STATUS_INFO_LENGTH_MISMATCH;
1228 if (_next_offset != 0) {
1229 /* Ensure we only read what in this record. */
1230 dir_data_length = _next_offset;
1233 if (dir_data_length < 92) {
1234 return NT_STATUS_INFO_LENGTH_MISMATCH;
1237 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1238 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1239 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1240 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1241 finfo->allocated_size = PULL_LE_U64(dir_data, 40);
1242 finfo->size = PULL_LE_U64(dir_data, 48);
1243 finfo->mode = PULL_LE_U32(dir_data, 56);
1244 finfo->ino = PULL_LE_U64(dir_data, 60);
1245 finfo->st_ex_dev = PULL_LE_U32(dir_data, 68);
1246 finfo->st_ex_nlink = PULL_LE_U32(dir_data, 76);
1247 finfo->reparse_tag = PULL_LE_U32(dir_data, 80);
1248 finfo->st_ex_mode = wire_perms_to_unix(PULL_LE_U32(dir_data, 84));
1250 slen = sid_parse_wire(finfo, dir_data+88, &finfo->owner_sid,
1251 dir_data_length-88);
1252 if (slen == 0) {
1253 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1255 slen2 = sid_parse_wire(finfo, dir_data+88+slen, &finfo->group_sid,
1256 dir_data_length-88-slen);
1257 if (slen2 == 0) {
1258 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1260 slen += slen2;
1262 namelen = PULL_LE_U32(dir_data, 88+slen);
1263 ret = pull_string_talloc(finfo,
1264 dir_data,
1265 FLAGS2_UNICODE_STRINGS,
1266 &finfo->name,
1267 dir_data+92+slen,
1268 namelen,
1269 STR_UNICODE);
1270 if (ret == (size_t)-1) {
1271 /* Bad conversion. */
1272 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1275 if (finfo->name == NULL) {
1276 /* Bad conversion. */
1277 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1280 *next_offset = _next_offset;
1281 return NT_STATUS_OK;
1284 /***************************************************************
1285 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1286 ***************************************************************/
1288 static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1289 uint32_t dir_data_length,
1290 struct file_info *finfo,
1291 uint32_t *next_offset)
1293 size_t namelen = 0;
1294 size_t slen = 0;
1295 size_t ret = 0;
1297 if (dir_data_length < 4) {
1298 return NT_STATUS_INFO_LENGTH_MISMATCH;
1301 *next_offset = IVAL(dir_data, 0);
1303 if (*next_offset > dir_data_length) {
1304 return NT_STATUS_INFO_LENGTH_MISMATCH;
1307 if (*next_offset != 0) {
1308 /* Ensure we only read what in this record. */
1309 dir_data_length = *next_offset;
1312 if (dir_data_length < 105) {
1313 return NT_STATUS_INFO_LENGTH_MISMATCH;
1316 finfo->btime_ts = interpret_long_date((const char *)dir_data + 8);
1317 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
1318 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
1319 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
1320 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
1321 finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0);
1322 finfo->attr = IVAL(dir_data + 56, 0);
1323 finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0);
1324 namelen = IVAL(dir_data + 60,0);
1325 if (namelen > (dir_data_length - 104)) {
1326 return NT_STATUS_INFO_LENGTH_MISMATCH;
1328 slen = CVAL(dir_data + 68, 0);
1329 if (slen > 24) {
1330 return NT_STATUS_INFO_LENGTH_MISMATCH;
1332 ret = pull_string_talloc(finfo,
1333 dir_data,
1334 FLAGS2_UNICODE_STRINGS,
1335 &finfo->short_name,
1336 dir_data + 70,
1337 slen,
1338 STR_UNICODE);
1339 if (ret == (size_t)-1) {
1340 /* Bad conversion. */
1341 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1344 ret = pull_string_talloc(finfo,
1345 dir_data,
1346 FLAGS2_UNICODE_STRINGS,
1347 &finfo->name,
1348 dir_data + 104,
1349 namelen,
1350 STR_UNICODE);
1351 if (ret == (size_t)-1) {
1352 /* Bad conversion. */
1353 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1356 if (finfo->name == NULL) {
1357 /* Bad conversion. */
1358 return NT_STATUS_INVALID_NETWORK_RESPONSE;
1361 return NT_STATUS_OK;
1364 /*******************************************************************
1365 Given a filename - get its directory name
1366 ********************************************************************/
1368 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1369 const char *dir,
1370 char **parent,
1371 const char **name)
1373 char *p;
1374 ptrdiff_t len;
1376 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1378 if (p == NULL) {
1379 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1380 return false;
1382 if (name) {
1383 *name = dir;
1385 return true;
1388 len = p-dir;
1390 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1391 return false;
1393 (*parent)[len] = '\0';
1395 if (name) {
1396 *name = p+1;
1398 return true;
1401 struct cli_smb2_list_dir_data {
1402 uint8_t *data;
1403 uint32_t length;
1406 struct cli_smb2_list_state {
1407 struct tevent_context *ev;
1408 struct cli_state *cli;
1409 const char *mask;
1411 uint16_t fnum;
1413 NTSTATUS status;
1414 struct cli_smb2_list_dir_data *response;
1415 uint32_t offset;
1416 unsigned int info_level;
1419 static void cli_smb2_list_opened(struct tevent_req *subreq);
1420 static void cli_smb2_list_done(struct tevent_req *subreq);
1421 static void cli_smb2_list_closed(struct tevent_req *subreq);
1423 struct tevent_req *cli_smb2_list_send(
1424 TALLOC_CTX *mem_ctx,
1425 struct tevent_context *ev,
1426 struct cli_state *cli,
1427 const char *pathname,
1428 unsigned int info_level,
1429 bool posix)
1431 struct tevent_req *req = NULL, *subreq = NULL;
1432 struct cli_smb2_list_state *state = NULL;
1433 char *parent = NULL;
1434 bool ok;
1435 struct smb2_create_blobs *in_cblobs = NULL;
1437 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1438 if (req == NULL) {
1439 return NULL;
1441 state->ev = ev;
1442 state->cli = cli;
1443 state->status = NT_STATUS_OK;
1444 state->info_level = info_level;
1446 ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1447 if (!ok) {
1448 tevent_req_oom(req);
1449 return tevent_req_post(req, ev);
1452 if (smbXcli_conn_have_posix(cli->conn) && posix) {
1453 NTSTATUS status;
1455 /* The mode MUST be 0 when opening an existing file/dir, and
1456 * will be ignored by the server.
1458 uint8_t linear_mode[4] = { 0 };
1459 DATA_BLOB blob = { .data=linear_mode,
1460 .length=sizeof(linear_mode) };
1462 in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1463 if (in_cblobs == NULL) {
1464 return NULL;
1467 status = smb2_create_blob_add(in_cblobs, in_cblobs,
1468 SMB2_CREATE_TAG_POSIX, blob);
1469 if (!NT_STATUS_IS_OK(status)) {
1470 tevent_req_nterror(req, status);
1471 return tevent_req_post(req, ev);
1475 subreq = cli_smb2_create_fnum_send(
1476 state, /* mem_ctx */
1477 ev, /* ev */
1478 cli, /* cli */
1479 parent, /* fname */
1480 0, /* create_flags */
1481 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1482 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1483 FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1484 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1485 FILE_OPEN, /* create_disposition */
1486 FILE_DIRECTORY_FILE, /* create_options */
1487 in_cblobs); /* in_cblobs */
1488 TALLOC_FREE(in_cblobs);
1489 if (tevent_req_nomem(subreq, req)) {
1490 return tevent_req_post(req, ev);
1492 tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1493 return req;
1496 static void cli_smb2_list_opened(struct tevent_req *subreq)
1498 struct tevent_req *req = tevent_req_callback_data(
1499 subreq, struct tevent_req);
1500 struct cli_smb2_list_state *state = tevent_req_data(
1501 req, struct cli_smb2_list_state);
1502 NTSTATUS status;
1504 status = cli_smb2_create_fnum_recv(
1505 subreq, &state->fnum, NULL, NULL, NULL, NULL);
1506 TALLOC_FREE(subreq);
1507 if (tevent_req_nterror(req, status)) {
1508 return;
1512 * Make our caller get back to us via cli_smb2_list_recv(),
1513 * triggering the smb2_query_directory_send()
1515 tevent_req_defer_callback(req, state->ev);
1516 tevent_req_notify_callback(req);
1519 static void cli_smb2_list_done(struct tevent_req *subreq)
1521 struct tevent_req *req = tevent_req_callback_data(
1522 subreq, struct tevent_req);
1523 struct cli_smb2_list_state *state = tevent_req_data(
1524 req, struct cli_smb2_list_state);
1525 struct cli_smb2_list_dir_data *response = NULL;
1527 response = talloc(state, struct cli_smb2_list_dir_data);
1528 if (tevent_req_nomem(response, req)) {
1529 return;
1532 state->status = smb2cli_query_directory_recv(
1533 subreq, response, &response->data, &response->length);
1534 TALLOC_FREE(subreq);
1536 if (NT_STATUS_IS_OK(state->status)) {
1537 state->response = response;
1538 state->offset = 0;
1540 tevent_req_defer_callback(req, state->ev);
1541 tevent_req_notify_callback(req);
1542 return;
1545 TALLOC_FREE(response);
1547 subreq = cli_smb2_close_fnum_send(
1548 state, state->ev, state->cli, state->fnum);
1549 if (tevent_req_nomem(subreq, req)) {
1550 return;
1552 tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1555 static void cli_smb2_list_closed(struct tevent_req *subreq)
1557 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1558 tevent_req_simple_finish_ntstatus(subreq, status);
1562 * Return the next finfo directory.
1564 * This parses the blob returned from QUERY_DIRECTORY step by step. If
1565 * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1566 * NT_STATUS_RETRY, which will then trigger the caller again when the
1567 * QUERY_DIRECTORY has returned with another buffer. This way we
1568 * guarantee that no asynchronous request is open after this call
1569 * returns an entry, so that other synchronous requests can be issued
1570 * on the same connection while the directoy listing proceeds.
1572 NTSTATUS cli_smb2_list_recv(
1573 struct tevent_req *req,
1574 TALLOC_CTX *mem_ctx,
1575 struct file_info **pfinfo)
1577 struct cli_smb2_list_state *state = tevent_req_data(
1578 req, struct cli_smb2_list_state);
1579 struct cli_smb2_list_dir_data *response = NULL;
1580 struct file_info *finfo = NULL;
1581 NTSTATUS status;
1582 uint32_t next_offset = 0;
1583 bool in_progress;
1585 in_progress = tevent_req_is_in_progress(req);
1587 if (!in_progress) {
1588 if (!tevent_req_is_nterror(req, &status)) {
1589 status = NT_STATUS_NO_MORE_FILES;
1591 goto fail;
1594 response = state->response;
1595 if (response == NULL) {
1596 struct tevent_req *subreq = NULL;
1597 struct cli_state *cli = state->cli;
1598 struct smb2_hnd *ph = NULL;
1599 uint32_t max_trans, max_avail_len;
1600 bool ok;
1602 if (!NT_STATUS_IS_OK(state->status)) {
1603 status = state->status;
1604 goto fail;
1607 status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1608 if (!NT_STATUS_IS_OK(status)) {
1609 goto fail;
1612 max_trans = smb2cli_conn_max_trans_size(cli->conn);
1613 ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1614 if (ok) {
1615 max_trans = MIN(max_trans, max_avail_len);
1618 subreq = smb2cli_query_directory_send(
1619 state, /* mem_ctx */
1620 state->ev, /* ev */
1621 cli->conn, /* conn */
1622 cli->timeout, /* timeout_msec */
1623 cli->smb2.session, /* session */
1624 cli->smb2.tcon, /* tcon */
1625 state->info_level, /* level */
1626 0, /* flags */
1627 0, /* file_index */
1628 ph->fid_persistent, /* fid_persistent */
1629 ph->fid_volatile, /* fid_volatile */
1630 state->mask, /* mask */
1631 max_trans); /* outbuf_len */
1632 if (subreq == NULL) {
1633 status = NT_STATUS_NO_MEMORY;
1634 goto fail;
1636 tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1637 return NT_STATUS_RETRY;
1640 SMB_ASSERT(response->length > state->offset);
1642 finfo = talloc_zero(mem_ctx, struct file_info);
1643 if (finfo == NULL) {
1644 status = NT_STATUS_NO_MEMORY;
1645 goto fail;
1648 if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1649 status = parse_finfo_posix_info(
1650 response->data + state->offset,
1651 response->length - state->offset,
1652 finfo,
1653 &next_offset);
1654 } else {
1655 status = parse_finfo_id_both_directory_info(
1656 response->data + state->offset,
1657 response->length - state->offset,
1658 finfo,
1659 &next_offset);
1661 if (!NT_STATUS_IS_OK(status)) {
1662 goto fail;
1665 status = is_bad_finfo_name(state->cli, finfo);
1666 if (!NT_STATUS_IS_OK(status)) {
1667 goto fail;
1671 * parse_finfo_id_both_directory_info() checks for overflow,
1672 * no need to check again here.
1674 state->offset += next_offset;
1676 if (next_offset == 0) {
1677 TALLOC_FREE(state->response);
1680 tevent_req_defer_callback(req, state->ev);
1681 tevent_req_notify_callback(req);
1683 *pfinfo = finfo;
1684 return NT_STATUS_OK;
1686 fail:
1687 TALLOC_FREE(finfo);
1688 tevent_req_received(req);
1689 return status;
1692 /***************************************************************
1693 Wrapper that allows SMB2 to query a path info (basic level).
1694 Synchronous only.
1695 ***************************************************************/
1697 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1698 const char *name,
1699 SMB_STRUCT_STAT *sbuf,
1700 uint32_t *attributes)
1702 NTSTATUS status;
1703 struct smb_create_returns cr;
1704 uint16_t fnum = 0xffff;
1705 size_t namelen = strlen(name);
1707 if (smbXcli_conn_has_async_calls(cli->conn)) {
1709 * Can't use sync call while an async call is in flight
1711 return NT_STATUS_INVALID_PARAMETER;
1714 /* SMB2 is pickier about pathnames. Ensure it doesn't
1715 end in a '\' */
1716 if (namelen > 0 && name[namelen-1] == '\\') {
1717 char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1718 if (modname == NULL) {
1719 return NT_STATUS_NO_MEMORY;
1721 name = modname;
1724 /* This is commonly used as a 'cd'. Try qpathinfo on
1725 a directory handle first. */
1727 status = cli_smb2_create_fnum(cli,
1728 name,
1729 0, /* create_flags */
1730 SMB2_IMPERSONATION_IMPERSONATION,
1731 FILE_READ_ATTRIBUTES, /* desired_access */
1732 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1733 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1734 FILE_OPEN, /* create_disposition */
1735 FILE_DIRECTORY_FILE, /* create_options */
1736 NULL,
1737 &fnum,
1738 &cr,
1739 NULL,
1740 NULL);
1742 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1743 /* Maybe a file ? */
1744 status = cli_smb2_create_fnum(cli,
1745 name,
1746 0, /* create_flags */
1747 SMB2_IMPERSONATION_IMPERSONATION,
1748 FILE_READ_ATTRIBUTES, /* desired_access */
1749 0, /* file attributes */
1750 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1751 FILE_OPEN, /* create_disposition */
1752 0, /* create_options */
1753 NULL,
1754 &fnum,
1755 &cr,
1756 NULL,
1757 NULL);
1760 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1761 /* Maybe a reparse point ? */
1762 status = cli_smb2_create_fnum(cli,
1763 name,
1764 0, /* create_flags */
1765 SMB2_IMPERSONATION_IMPERSONATION,
1766 FILE_READ_ATTRIBUTES, /* desired_access */
1767 0, /* file attributes */
1768 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1769 FILE_OPEN, /* create_disposition */
1770 FILE_OPEN_REPARSE_POINT, /* create_options */
1771 NULL,
1772 &fnum,
1773 &cr,
1774 NULL,
1775 NULL);
1778 if (!NT_STATUS_IS_OK(status)) {
1779 return status;
1782 status = cli_smb2_close_fnum(cli, fnum);
1784 ZERO_STRUCTP(sbuf);
1786 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1787 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1788 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1789 sbuf->st_ex_size = cr.end_of_file;
1790 *attributes = cr.file_attributes;
1792 return status;
1795 struct cli_smb2_query_info_fnum_state {
1796 DATA_BLOB outbuf;
1799 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1801 struct tevent_req *cli_smb2_query_info_fnum_send(
1802 TALLOC_CTX *mem_ctx,
1803 struct tevent_context *ev,
1804 struct cli_state *cli,
1805 uint16_t fnum,
1806 uint8_t in_info_type,
1807 uint8_t in_info_class,
1808 uint32_t in_max_output_length,
1809 const DATA_BLOB *in_input_buffer,
1810 uint32_t in_additional_info,
1811 uint32_t in_flags)
1813 struct tevent_req *req = NULL, *subreq = NULL;
1814 struct cli_smb2_query_info_fnum_state *state = NULL;
1815 struct smb2_hnd *ph = NULL;
1816 NTSTATUS status;
1818 req = tevent_req_create(
1819 mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1820 if (req == NULL) {
1821 return req;
1824 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1825 if (tevent_req_nterror(req, status)) {
1826 return tevent_req_post(req, ev);
1829 subreq = smb2cli_query_info_send(
1830 state,
1832 cli->conn,
1833 cli->timeout,
1834 cli->smb2.session,
1835 cli->smb2.tcon,
1836 in_info_type,
1837 in_info_class,
1838 in_max_output_length,
1839 in_input_buffer,
1840 in_additional_info,
1841 in_flags,
1842 ph->fid_persistent,
1843 ph->fid_volatile);
1844 if (tevent_req_nomem(subreq, req)) {
1845 return tevent_req_post(req, ev);
1847 tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1848 return req;
1851 static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1853 struct tevent_req *req = tevent_req_callback_data(
1854 subreq, struct tevent_req);
1855 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1856 req, struct cli_smb2_query_info_fnum_state);
1857 DATA_BLOB outbuf;
1858 NTSTATUS status;
1860 status = smb2cli_query_info_recv(subreq, state, &outbuf);
1861 TALLOC_FREE(subreq);
1862 if (tevent_req_nterror(req, status)) {
1863 return;
1867 * We have to dup the memory here because outbuf.data is not
1868 * returned as a talloc object by smb2cli_query_info_recv.
1869 * It's a pointer into the received buffer.
1871 state->outbuf = data_blob_dup_talloc(state, outbuf);
1873 if ((outbuf.length != 0) &&
1874 tevent_req_nomem(state->outbuf.data, req)) {
1875 return;
1877 tevent_req_done(req);
1880 NTSTATUS cli_smb2_query_info_fnum_recv(
1881 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1883 struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1884 req, struct cli_smb2_query_info_fnum_state);
1885 NTSTATUS status;
1887 if (tevent_req_is_nterror(req, &status)) {
1888 return status;
1890 *outbuf = (DATA_BLOB) {
1891 .data = talloc_move(mem_ctx, &state->outbuf.data),
1892 .length = state->outbuf.length,
1894 return NT_STATUS_OK;
1897 NTSTATUS cli_smb2_query_info_fnum(
1898 struct cli_state *cli,
1899 uint16_t fnum,
1900 uint8_t in_info_type,
1901 uint8_t in_info_class,
1902 uint32_t in_max_output_length,
1903 const DATA_BLOB *in_input_buffer,
1904 uint32_t in_additional_info,
1905 uint32_t in_flags,
1906 TALLOC_CTX *mem_ctx,
1907 DATA_BLOB *outbuf)
1909 TALLOC_CTX *frame = talloc_stackframe();
1910 struct tevent_context *ev = NULL;
1911 struct tevent_req *req = NULL;
1912 NTSTATUS status = NT_STATUS_NO_MEMORY;
1913 bool ok;
1915 if (smbXcli_conn_has_async_calls(cli->conn)) {
1917 * Can't use sync call while an async call is in flight
1919 status = NT_STATUS_INVALID_PARAMETER;
1920 goto fail;
1922 ev = samba_tevent_context_init(frame);
1923 if (ev == NULL) {
1924 goto fail;
1926 req = cli_smb2_query_info_fnum_send(
1927 frame,
1929 cli,
1930 fnum,
1931 in_info_type,
1932 in_info_class,
1933 in_max_output_length,
1934 in_input_buffer,
1935 in_additional_info,
1936 in_flags);
1937 if (req == NULL) {
1938 goto fail;
1940 ok = tevent_req_poll_ntstatus(req, ev, &status);
1941 if (!ok) {
1942 goto fail;
1944 status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1945 fail:
1946 TALLOC_FREE(frame);
1947 return status;
1950 /***************************************************************
1951 Helper function for pathname operations.
1952 ***************************************************************/
1954 struct get_fnum_from_path_state {
1955 struct tevent_context *ev;
1956 struct cli_state *cli;
1957 const char *name;
1958 uint32_t desired_access;
1959 uint16_t fnum;
1962 static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1963 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1964 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1966 static struct tevent_req *get_fnum_from_path_send(
1967 TALLOC_CTX *mem_ctx,
1968 struct tevent_context *ev,
1969 struct cli_state *cli,
1970 const char *name,
1971 uint32_t desired_access)
1973 struct tevent_req *req = NULL, *subreq = NULL;
1974 struct get_fnum_from_path_state *state = NULL;
1975 size_t namelen = strlen(name);
1977 req = tevent_req_create(
1978 mem_ctx, &state, struct get_fnum_from_path_state);
1979 if (req == NULL) {
1980 return NULL;
1982 state->ev = ev;
1983 state->cli = cli;
1984 state->name = name;
1985 state->desired_access = desired_access;
1988 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
1989 * '\'
1991 if (namelen > 0 && name[namelen-1] == '\\') {
1992 state->name = talloc_strndup(state, name, namelen-1);
1993 if (tevent_req_nomem(state->name, req)) {
1994 return tevent_req_post(req, ev);
1998 subreq = cli_smb2_create_fnum_send(
1999 state, /* mem_ctx, */
2000 ev, /* ev */
2001 cli, /* cli */
2002 state->name, /* fname */
2003 0, /* create_flags */
2004 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2005 desired_access, /* desired_access */
2006 0, /* file_attributes */
2007 FILE_SHARE_READ|
2008 FILE_SHARE_WRITE|
2009 FILE_SHARE_DELETE, /* share_access */
2010 FILE_OPEN, /* create_disposition */
2011 0, /* create_options */
2012 NULL); /* in_cblobs */
2013 if (tevent_req_nomem(subreq, req)) {
2014 return tevent_req_post(req, ev);
2016 tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2017 return req;
2020 static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2022 struct tevent_req *req = tevent_req_callback_data(
2023 subreq, struct tevent_req);
2024 struct get_fnum_from_path_state *state = tevent_req_data(
2025 req, struct get_fnum_from_path_state);
2026 NTSTATUS status;
2028 status = cli_smb2_create_fnum_recv(
2029 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2030 TALLOC_FREE(subreq);
2032 if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
2034 * Naive option to match our SMB1 code. Assume the
2035 * symlink path that tripped us up was the last
2036 * component and try again. Eventually we will have to
2037 * deal with the returned path unprocessed component. JRA.
2039 subreq = cli_smb2_create_fnum_send(
2040 state, /* mem_ctx, */
2041 state->ev, /* ev */
2042 state->cli, /* cli */
2043 state->name, /* fname */
2044 0, /* create_flags */
2045 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2046 state->desired_access, /* desired_access */
2047 0, /* file_attributes */
2048 FILE_SHARE_READ|
2049 FILE_SHARE_WRITE|
2050 FILE_SHARE_DELETE, /* share_access */
2051 FILE_OPEN, /* create_disposition */
2052 FILE_OPEN_REPARSE_POINT, /* create_options */
2053 NULL); /* in_cblobs */
2054 if (tevent_req_nomem(subreq, req)) {
2055 return;
2057 tevent_req_set_callback(
2058 subreq, get_fnum_from_path_opened_reparse, req);
2059 return;
2062 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2063 subreq = cli_smb2_create_fnum_send(
2064 state, /* mem_ctx, */
2065 state->ev, /* ev */
2066 state->cli, /* cli */
2067 state->name, /* fname */
2068 0, /* create_flags */
2069 SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2070 state->desired_access, /* desired_access */
2071 0, /* file_attributes */
2072 FILE_SHARE_READ|
2073 FILE_SHARE_WRITE|
2074 FILE_SHARE_DELETE, /* share_access */
2075 FILE_OPEN, /* create_disposition */
2076 FILE_DIRECTORY_FILE, /* create_options */
2077 NULL); /* in_cblobs */
2078 if (tevent_req_nomem(subreq, req)) {
2079 return;
2081 tevent_req_set_callback(
2082 subreq, get_fnum_from_path_opened_dir, req);
2083 return;
2086 if (tevent_req_nterror(req, status)) {
2087 return;
2089 tevent_req_done(req);
2092 static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2094 struct tevent_req *req = tevent_req_callback_data(
2095 subreq, struct tevent_req);
2096 struct get_fnum_from_path_state *state = tevent_req_data(
2097 req, struct get_fnum_from_path_state);
2098 NTSTATUS status = cli_smb2_create_fnum_recv(
2099 subreq, &state->fnum, NULL, NULL, NULL, NULL);
2100 tevent_req_simple_finish_ntstatus(subreq, status);
2103 static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2105 /* Abstraction violation, but these two are just the same... */
2106 get_fnum_from_path_opened_reparse(subreq);
2109 static NTSTATUS get_fnum_from_path_recv(
2110 struct tevent_req *req, uint16_t *pfnum)
2112 struct get_fnum_from_path_state *state = tevent_req_data(
2113 req, struct get_fnum_from_path_state);
2114 NTSTATUS status = NT_STATUS_OK;
2116 if (!tevent_req_is_nterror(req, &status)) {
2117 *pfnum = state->fnum;
2119 tevent_req_received(req);
2120 return status;
2123 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2124 const char *name,
2125 uint32_t desired_access,
2126 uint16_t *pfnum)
2128 TALLOC_CTX *frame = talloc_stackframe();
2129 struct tevent_context *ev = NULL;
2130 struct tevent_req *req = NULL;
2131 NTSTATUS status = NT_STATUS_NO_MEMORY;
2133 if (smbXcli_conn_has_async_calls(cli->conn)) {
2134 status = NT_STATUS_INVALID_PARAMETER;
2135 goto fail;
2137 ev = samba_tevent_context_init(frame);
2138 if (ev == NULL) {
2139 goto fail;
2141 req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2142 if (req == NULL) {
2143 goto fail;
2145 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2146 goto fail;
2148 status = get_fnum_from_path_recv(req, pfnum);
2149 fail:
2150 TALLOC_FREE(frame);
2151 return status;
2154 /***************************************************************
2155 Wrapper that allows SMB2 to query a path info (ALTNAME level).
2156 Synchronous only.
2157 ***************************************************************/
2159 NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
2160 const char *name,
2161 fstring alt_name)
2163 NTSTATUS status;
2164 DATA_BLOB outbuf = data_blob_null;
2165 uint16_t fnum = 0xffff;
2166 uint32_t altnamelen = 0;
2167 TALLOC_CTX *frame = talloc_stackframe();
2169 if (smbXcli_conn_has_async_calls(cli->conn)) {
2171 * Can't use sync call while an async call is in flight
2173 status = NT_STATUS_INVALID_PARAMETER;
2174 goto fail;
2177 status = get_fnum_from_path(cli,
2178 name,
2179 FILE_READ_ATTRIBUTES,
2180 &fnum);
2182 if (!NT_STATUS_IS_OK(status)) {
2183 goto fail;
2186 status = cli_smb2_query_info_fnum(
2187 cli,
2188 fnum,
2189 1, /* in_info_type */
2190 (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
2191 0xFFFF, /* in_max_output_length */
2192 NULL, /* in_input_buffer */
2193 0, /* in_additional_info */
2194 0, /* in_flags */
2195 frame,
2196 &outbuf);
2198 if (!NT_STATUS_IS_OK(status)) {
2199 goto fail;
2202 /* Parse the reply. */
2203 if (outbuf.length < 4) {
2204 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2205 goto fail;
2208 altnamelen = IVAL(outbuf.data, 0);
2209 if (altnamelen > outbuf.length - 4) {
2210 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2211 goto fail;
2214 if (altnamelen > 0) {
2215 size_t ret = 0;
2216 char *short_name = NULL;
2217 ret = pull_string_talloc(frame,
2218 outbuf.data,
2219 FLAGS2_UNICODE_STRINGS,
2220 &short_name,
2221 outbuf.data + 4,
2222 altnamelen,
2223 STR_UNICODE);
2224 if (ret == (size_t)-1) {
2225 /* Bad conversion. */
2226 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2227 goto fail;
2230 fstrcpy(alt_name, short_name);
2231 } else {
2232 alt_name[0] = '\0';
2235 status = NT_STATUS_OK;
2237 fail:
2239 if (fnum != 0xffff) {
2240 cli_smb2_close_fnum(cli, fnum);
2243 cli->raw_status = status;
2245 TALLOC_FREE(frame);
2246 return status;
2249 /***************************************************************
2250 Wrapper that allows SMB2 to get pathname attributes.
2251 Synchronous only.
2252 ***************************************************************/
2254 NTSTATUS cli_smb2_getatr(struct cli_state *cli,
2255 const char *name,
2256 uint32_t *pattr,
2257 off_t *size,
2258 time_t *write_time)
2260 NTSTATUS status;
2261 uint16_t fnum = 0xffff;
2262 struct timespec write_time_ts;
2263 TALLOC_CTX *frame = talloc_stackframe();
2265 if (smbXcli_conn_has_async_calls(cli->conn)) {
2267 * Can't use sync call while an async call is in flight
2269 status = NT_STATUS_INVALID_PARAMETER;
2270 goto fail;
2273 status = get_fnum_from_path(cli,
2274 name,
2275 FILE_READ_ATTRIBUTES,
2276 &fnum);
2278 if (!NT_STATUS_IS_OK(status)) {
2279 goto fail;
2282 status = cli_qfileinfo_basic(
2283 cli,
2284 fnum,
2285 pattr,
2286 size,
2287 NULL, /* create_time */
2288 NULL, /* access_time */
2289 &write_time_ts,
2290 NULL, /* change_time */
2291 NULL); /* ino */
2292 if (!NT_STATUS_IS_OK(status)) {
2293 goto fail;
2295 if (write_time != NULL) {
2296 *write_time = write_time_ts.tv_sec;
2299 fail:
2301 if (fnum != 0xffff) {
2302 cli_smb2_close_fnum(cli, fnum);
2305 cli->raw_status = status;
2307 TALLOC_FREE(frame);
2308 return status;
2311 /***************************************************************
2312 Wrapper that allows SMB2 to query a pathname info (basic level).
2313 Implement on top of cli_qfileinfo_basic().
2314 Synchronous only.
2315 ***************************************************************/
2317 NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
2318 const char *name,
2319 struct timespec *create_time,
2320 struct timespec *access_time,
2321 struct timespec *write_time,
2322 struct timespec *change_time,
2323 off_t *size,
2324 uint32_t *pattr,
2325 SMB_INO_T *ino)
2327 NTSTATUS status;
2328 uint16_t fnum = 0xffff;
2329 TALLOC_CTX *frame = talloc_stackframe();
2331 if (smbXcli_conn_has_async_calls(cli->conn)) {
2333 * Can't use sync call while an async call is in flight
2335 status = NT_STATUS_INVALID_PARAMETER;
2336 goto fail;
2339 status = get_fnum_from_path(cli,
2340 name,
2341 FILE_READ_ATTRIBUTES,
2342 &fnum);
2344 if (!NT_STATUS_IS_OK(status)) {
2345 goto fail;
2348 status = cli_qfileinfo_basic(
2349 cli,
2350 fnum,
2351 pattr,
2352 size,
2353 create_time,
2354 access_time,
2355 write_time,
2356 change_time,
2357 ino);
2359 fail:
2361 if (fnum != 0xffff) {
2362 cli_smb2_close_fnum(cli, fnum);
2365 cli->raw_status = status;
2367 TALLOC_FREE(frame);
2368 return status;
2371 /***************************************************************
2372 Wrapper that allows SMB2 to query pathname streams.
2373 Synchronous only.
2374 ***************************************************************/
2376 NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
2377 const char *name,
2378 TALLOC_CTX *mem_ctx,
2379 unsigned int *pnum_streams,
2380 struct stream_struct **pstreams)
2382 NTSTATUS status;
2383 uint16_t fnum = 0xffff;
2384 DATA_BLOB outbuf = data_blob_null;
2385 TALLOC_CTX *frame = talloc_stackframe();
2387 if (smbXcli_conn_has_async_calls(cli->conn)) {
2389 * Can't use sync call while an async call is in flight
2391 status = NT_STATUS_INVALID_PARAMETER;
2392 goto fail;
2395 status = get_fnum_from_path(cli,
2396 name,
2397 FILE_READ_ATTRIBUTES,
2398 &fnum);
2400 if (!NT_STATUS_IS_OK(status)) {
2401 goto fail;
2404 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
2405 level 22 (SMB2_FILE_STREAM_INFORMATION). */
2407 status = cli_smb2_query_info_fnum(
2408 cli,
2409 fnum,
2410 1, /* in_info_type */
2411 (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */
2412 0xFFFF, /* in_max_output_length */
2413 NULL, /* in_input_buffer */
2414 0, /* in_additional_info */
2415 0, /* in_flags */
2416 frame,
2417 &outbuf);
2419 if (!NT_STATUS_IS_OK(status)) {
2420 goto fail;
2423 /* Parse the reply. */
2424 if (!parse_streams_blob(mem_ctx,
2425 outbuf.data,
2426 outbuf.length,
2427 pnum_streams,
2428 pstreams)) {
2429 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2430 goto fail;
2433 fail:
2435 if (fnum != 0xffff) {
2436 cli_smb2_close_fnum(cli, fnum);
2439 cli->raw_status = status;
2441 TALLOC_FREE(frame);
2442 return status;
2445 /***************************************************************
2446 Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2447 a pathname.
2448 Synchronous only.
2449 ***************************************************************/
2451 NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2452 const char *name,
2453 uint8_t in_info_type,
2454 uint8_t in_file_info_class,
2455 const DATA_BLOB *p_in_data)
2457 NTSTATUS status;
2458 uint16_t fnum = 0xffff;
2459 TALLOC_CTX *frame = talloc_stackframe();
2461 if (smbXcli_conn_has_async_calls(cli->conn)) {
2463 * Can't use sync call while an async call is in flight
2465 status = NT_STATUS_INVALID_PARAMETER;
2466 goto fail;
2469 status = get_fnum_from_path(cli,
2470 name,
2471 FILE_WRITE_ATTRIBUTES,
2472 &fnum);
2474 if (!NT_STATUS_IS_OK(status)) {
2475 goto fail;
2478 status = cli_smb2_set_info_fnum(
2479 cli,
2480 fnum,
2481 in_info_type,
2482 in_file_info_class,
2483 p_in_data, /* in_input_buffer */
2484 0); /* in_additional_info */
2485 fail:
2487 if (fnum != 0xffff) {
2488 cli_smb2_close_fnum(cli, fnum);
2491 cli->raw_status = status;
2493 TALLOC_FREE(frame);
2494 return status;
2498 /***************************************************************
2499 Wrapper that allows SMB2 to set pathname attributes.
2500 Synchronous only.
2501 ***************************************************************/
2503 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2504 const char *name,
2505 uint32_t attr,
2506 time_t mtime)
2508 uint8_t inbuf_store[40];
2509 DATA_BLOB inbuf = data_blob_null;
2511 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2512 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2514 inbuf.data = inbuf_store;
2515 inbuf.length = sizeof(inbuf_store);
2516 data_blob_clear(&inbuf);
2519 * SMB1 uses attr == 0 to clear all attributes
2520 * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2521 * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2522 * request attribute change.
2524 * SMB2 uses exactly the reverse. Unfortunately as the
2525 * cli_setatr() ABI is exposed inside libsmbclient,
2526 * we must make the SMB2 cli_smb2_setatr() call
2527 * export the same ABI as the SMB1 cli_setatr()
2528 * which calls it. This means reversing the sense
2529 * of the requested attr argument if it's zero
2530 * or FILE_ATTRIBUTE_NORMAL.
2532 * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2535 if (attr == 0) {
2536 attr = FILE_ATTRIBUTE_NORMAL;
2537 } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2538 attr = 0;
2541 SIVAL(inbuf.data, 32, attr);
2542 if (mtime != 0) {
2543 put_long_date((char *)inbuf.data + 16,mtime);
2545 /* Set all the other times to -1. */
2546 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2547 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2548 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2550 return cli_smb2_setpathinfo(cli,
2551 name,
2552 1, /* in_info_type */
2553 /* in_file_info_class */
2554 SMB_FILE_BASIC_INFORMATION - 1000,
2555 &inbuf);
2559 /***************************************************************
2560 Wrapper that allows SMB2 to set file handle times.
2561 Synchronous only.
2562 ***************************************************************/
2564 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2565 uint16_t fnum,
2566 time_t change_time,
2567 time_t access_time,
2568 time_t write_time)
2570 uint8_t inbuf_store[40];
2571 DATA_BLOB inbuf = data_blob_null;
2573 if (smbXcli_conn_has_async_calls(cli->conn)) {
2575 * Can't use sync call while an async call is in flight
2577 return NT_STATUS_INVALID_PARAMETER;
2580 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2581 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2583 inbuf.data = inbuf_store;
2584 inbuf.length = sizeof(inbuf_store);
2585 data_blob_clear(&inbuf);
2587 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2588 if (change_time != 0) {
2589 put_long_date((char *)inbuf.data + 24, change_time);
2591 if (access_time != 0) {
2592 put_long_date((char *)inbuf.data + 8, access_time);
2594 if (write_time != 0) {
2595 put_long_date((char *)inbuf.data + 16, write_time);
2598 cli->raw_status = cli_smb2_set_info_fnum(
2599 cli,
2600 fnum,
2601 1, /* in_info_type */
2602 SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
2603 &inbuf, /* in_input_buffer */
2604 0); /* in_additional_info */
2606 return cli->raw_status;
2609 /***************************************************************
2610 Wrapper that allows SMB2 to query disk attributes (size).
2611 Synchronous only.
2612 ***************************************************************/
2614 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2615 uint64_t *bsize, uint64_t *total, uint64_t *avail)
2617 NTSTATUS status;
2618 uint16_t fnum = 0xffff;
2619 DATA_BLOB outbuf = data_blob_null;
2620 uint32_t sectors_per_unit = 0;
2621 uint32_t bytes_per_sector = 0;
2622 uint64_t total_size = 0;
2623 uint64_t size_free = 0;
2624 TALLOC_CTX *frame = talloc_stackframe();
2626 if (smbXcli_conn_has_async_calls(cli->conn)) {
2628 * Can't use sync call while an async call is in flight
2630 status = NT_STATUS_INVALID_PARAMETER;
2631 goto fail;
2634 /* First open the top level directory. */
2635 status = cli_smb2_create_fnum(cli,
2636 path,
2637 0, /* create_flags */
2638 SMB2_IMPERSONATION_IMPERSONATION,
2639 FILE_READ_ATTRIBUTES, /* desired_access */
2640 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2641 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2642 FILE_OPEN, /* create_disposition */
2643 FILE_DIRECTORY_FILE, /* create_options */
2644 NULL,
2645 &fnum,
2646 NULL,
2647 NULL,
2648 NULL);
2650 if (!NT_STATUS_IS_OK(status)) {
2651 goto fail;
2654 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2655 level 3 (SMB_FS_SIZE_INFORMATION). */
2657 status = cli_smb2_query_info_fnum(
2658 cli,
2659 fnum,
2660 2, /* in_info_type */
2661 3, /* in_file_info_class */
2662 0xFFFF, /* in_max_output_length */
2663 NULL, /* in_input_buffer */
2664 0, /* in_additional_info */
2665 0, /* in_flags */
2666 frame,
2667 &outbuf);
2668 if (!NT_STATUS_IS_OK(status)) {
2669 goto fail;
2672 /* Parse the reply. */
2673 if (outbuf.length != 24) {
2674 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2675 goto fail;
2678 total_size = BVAL(outbuf.data, 0);
2679 size_free = BVAL(outbuf.data, 8);
2680 sectors_per_unit = IVAL(outbuf.data, 16);
2681 bytes_per_sector = IVAL(outbuf.data, 20);
2683 if (bsize) {
2684 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2686 if (total) {
2687 *total = total_size;
2689 if (avail) {
2690 *avail = size_free;
2693 status = NT_STATUS_OK;
2695 fail:
2697 if (fnum != 0xffff) {
2698 cli_smb2_close_fnum(cli, fnum);
2701 cli->raw_status = status;
2703 TALLOC_FREE(frame);
2704 return status;
2707 /***************************************************************
2708 Wrapper that allows SMB2 to query file system sizes.
2709 Synchronous only.
2710 ***************************************************************/
2712 NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2713 uint64_t *total_allocation_units,
2714 uint64_t *caller_allocation_units,
2715 uint64_t *actual_allocation_units,
2716 uint64_t *sectors_per_allocation_unit,
2717 uint64_t *bytes_per_sector)
2719 NTSTATUS status;
2720 uint16_t fnum = 0xffff;
2721 DATA_BLOB outbuf = data_blob_null;
2722 TALLOC_CTX *frame = talloc_stackframe();
2724 if (smbXcli_conn_has_async_calls(cli->conn)) {
2726 * Can't use sync call while an async call is in flight
2728 status = NT_STATUS_INVALID_PARAMETER;
2729 goto fail;
2732 /* First open the top level directory. */
2733 status =
2734 cli_smb2_create_fnum(cli, "", 0, /* create_flags */
2735 SMB2_IMPERSONATION_IMPERSONATION,
2736 FILE_READ_ATTRIBUTES, /* desired_access */
2737 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2738 FILE_SHARE_READ | FILE_SHARE_WRITE |
2739 FILE_SHARE_DELETE, /* share_access */
2740 FILE_OPEN, /* create_disposition */
2741 FILE_DIRECTORY_FILE, /* create_options */
2742 NULL,
2743 &fnum,
2744 NULL,
2745 NULL,
2746 NULL);
2748 if (!NT_STATUS_IS_OK(status)) {
2749 goto fail;
2752 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2753 level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2755 status = cli_smb2_query_info_fnum(
2756 cli,
2757 fnum,
2758 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2759 SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */
2760 0xFFFF, /* in_max_output_length */
2761 NULL, /* in_input_buffer */
2762 0, /* in_additional_info */
2763 0, /* in_flags */
2764 frame,
2765 &outbuf);
2766 if (!NT_STATUS_IS_OK(status)) {
2767 goto fail;
2770 if (outbuf.length < 32) {
2771 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2772 goto fail;
2775 *total_allocation_units = BIG_UINT(outbuf.data, 0);
2776 *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2777 *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2778 *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2779 *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2781 fail:
2783 if (fnum != 0xffff) {
2784 cli_smb2_close_fnum(cli, fnum);
2787 cli->raw_status = status;
2789 TALLOC_FREE(frame);
2790 return status;
2793 /***************************************************************
2794 Wrapper that allows SMB2 to query file system attributes.
2795 Synchronous only.
2796 ***************************************************************/
2798 NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2800 NTSTATUS status;
2801 uint16_t fnum = 0xffff;
2802 DATA_BLOB outbuf = data_blob_null;
2803 TALLOC_CTX *frame = talloc_stackframe();
2805 if (smbXcli_conn_has_async_calls(cli->conn)) {
2807 * Can't use sync call while an async call is in flight
2809 status = NT_STATUS_INVALID_PARAMETER;
2810 goto fail;
2813 /* First open the top level directory. */
2814 status =
2815 cli_smb2_create_fnum(cli, "", 0, /* create_flags */
2816 SMB2_IMPERSONATION_IMPERSONATION,
2817 FILE_READ_ATTRIBUTES, /* desired_access */
2818 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2819 FILE_SHARE_READ | FILE_SHARE_WRITE |
2820 FILE_SHARE_DELETE, /* share_access */
2821 FILE_OPEN, /* create_disposition */
2822 FILE_DIRECTORY_FILE, /* create_options */
2823 NULL,
2824 &fnum,
2825 NULL,
2826 NULL,
2827 NULL);
2829 if (!NT_STATUS_IS_OK(status)) {
2830 goto fail;
2833 status = cli_smb2_query_info_fnum(
2834 cli,
2835 fnum,
2836 2, /* in_info_type */
2837 5, /* in_file_info_class */
2838 0xFFFF, /* in_max_output_length */
2839 NULL, /* in_input_buffer */
2840 0, /* in_additional_info */
2841 0, /* in_flags */
2842 frame,
2843 &outbuf);
2844 if (!NT_STATUS_IS_OK(status)) {
2845 goto fail;
2848 if (outbuf.length < 12) {
2849 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2850 goto fail;
2853 *fs_attr = IVAL(outbuf.data, 0);
2855 fail:
2857 if (fnum != 0xffff) {
2858 cli_smb2_close_fnum(cli, fnum);
2861 cli->raw_status = status;
2863 TALLOC_FREE(frame);
2864 return status;
2867 /***************************************************************
2868 Wrapper that allows SMB2 to query file system volume info.
2869 Synchronous only.
2870 ***************************************************************/
2872 NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2873 TALLOC_CTX *mem_ctx,
2874 char **_volume_name,
2875 uint32_t *pserial_number,
2876 time_t *pdate)
2878 NTSTATUS status;
2879 uint16_t fnum = 0xffff;
2880 DATA_BLOB outbuf = data_blob_null;
2881 uint32_t nlen;
2882 char *volume_name = NULL;
2883 TALLOC_CTX *frame = talloc_stackframe();
2885 if (smbXcli_conn_has_async_calls(cli->conn)) {
2887 * Can't use sync call while an async call is in flight
2889 status = NT_STATUS_INVALID_PARAMETER;
2890 goto fail;
2893 /* First open the top level directory. */
2894 status =
2895 cli_smb2_create_fnum(cli, "", 0, /* create_flags */
2896 SMB2_IMPERSONATION_IMPERSONATION,
2897 FILE_READ_ATTRIBUTES, /* desired_access */
2898 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2899 FILE_SHARE_READ | FILE_SHARE_WRITE |
2900 FILE_SHARE_DELETE, /* share_access */
2901 FILE_OPEN, /* create_disposition */
2902 FILE_DIRECTORY_FILE, /* create_options */
2903 NULL,
2904 &fnum,
2905 NULL,
2906 NULL,
2907 NULL);
2909 if (!NT_STATUS_IS_OK(status)) {
2910 goto fail;
2913 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2914 level 1 (SMB_FS_VOLUME_INFORMATION). */
2916 status = cli_smb2_query_info_fnum(
2917 cli,
2918 fnum,
2919 SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2920 /* in_file_info_class */
2921 SMB_FS_VOLUME_INFORMATION - 1000,
2922 0xFFFF, /* in_max_output_length */
2923 NULL, /* in_input_buffer */
2924 0, /* in_additional_info */
2925 0, /* in_flags */
2926 frame,
2927 &outbuf);
2928 if (!NT_STATUS_IS_OK(status)) {
2929 goto fail;
2932 if (outbuf.length < 24) {
2933 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2934 goto fail;
2937 if (pdate) {
2938 struct timespec ts;
2939 ts = interpret_long_date((char *)outbuf.data);
2940 *pdate = ts.tv_sec;
2942 if (pserial_number) {
2943 *pserial_number = IVAL(outbuf.data,8);
2945 nlen = IVAL(outbuf.data,12);
2946 if (nlen + 18 < 18) {
2947 /* Integer wrap. */
2948 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2949 goto fail;
2952 * The next check is safe as we know outbuf.length >= 24
2953 * from above.
2955 if (nlen > (outbuf.length - 18)) {
2956 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2957 goto fail;
2960 pull_string_talloc(mem_ctx,
2961 (const char *)outbuf.data,
2963 &volume_name,
2964 outbuf.data + 18,
2965 nlen,
2966 STR_UNICODE);
2967 if (volume_name == NULL) {
2968 status = map_nt_error_from_unix(errno);
2969 goto fail;
2972 *_volume_name = volume_name;
2974 fail:
2976 if (fnum != 0xffff) {
2977 cli_smb2_close_fnum(cli, fnum);
2980 cli->raw_status = status;
2982 TALLOC_FREE(frame);
2983 return status;
2986 struct cli_smb2_mxac_state {
2987 struct tevent_context *ev;
2988 struct cli_state *cli;
2989 const char *fname;
2990 struct smb2_create_blobs in_cblobs;
2991 uint16_t fnum;
2992 NTSTATUS status;
2993 uint32_t mxac;
2996 static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2997 static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2999 struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
3000 struct tevent_context *ev,
3001 struct cli_state *cli,
3002 const char *fname)
3004 struct tevent_req *req = NULL, *subreq = NULL;
3005 struct cli_smb2_mxac_state *state = NULL;
3006 NTSTATUS status;
3008 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
3009 if (req == NULL) {
3010 return NULL;
3012 *state = (struct cli_smb2_mxac_state) {
3013 .ev = ev,
3014 .cli = cli,
3015 .fname = fname,
3018 status = smb2_create_blob_add(state,
3019 &state->in_cblobs,
3020 SMB2_CREATE_TAG_MXAC,
3021 data_blob(NULL, 0));
3022 if (tevent_req_nterror(req, status)) {
3023 return tevent_req_post(req, ev);
3026 subreq = cli_smb2_create_fnum_send(
3027 state,
3028 state->ev,
3029 state->cli,
3030 state->fname,
3031 0, /* create_flags */
3032 SMB2_IMPERSONATION_IMPERSONATION,
3033 FILE_READ_ATTRIBUTES,
3034 0, /* file attributes */
3035 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
3036 FILE_OPEN,
3037 0, /* create_options */
3038 &state->in_cblobs);
3039 if (tevent_req_nomem(subreq, req)) {
3040 return tevent_req_post(req, ev);
3042 tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
3043 return req;
3046 static void cli_smb2_mxac_opened(struct tevent_req *subreq)
3048 struct tevent_req *req = tevent_req_callback_data(
3049 subreq, struct tevent_req);
3050 struct cli_smb2_mxac_state *state = tevent_req_data(
3051 req, struct cli_smb2_mxac_state);
3052 struct smb2_create_blobs out_cblobs = {0};
3053 struct smb2_create_blob *mxac_blob = NULL;
3054 NTSTATUS status;
3056 status = cli_smb2_create_fnum_recv(
3057 subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
3058 TALLOC_FREE(subreq);
3060 if (tevent_req_nterror(req, status)) {
3061 return;
3064 mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
3065 if (mxac_blob == NULL) {
3066 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3067 goto close;
3069 if (mxac_blob->data.length != 8) {
3070 state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3071 goto close;
3074 state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
3075 state->mxac = IVAL(mxac_blob->data.data, 4);
3077 close:
3078 subreq = cli_smb2_close_fnum_send(
3079 state, state->ev, state->cli, state->fnum);
3080 if (tevent_req_nomem(subreq, req)) {
3081 return;
3083 tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
3085 return;
3088 static void cli_smb2_mxac_closed(struct tevent_req *subreq)
3090 struct tevent_req *req = tevent_req_callback_data(
3091 subreq, struct tevent_req);
3092 NTSTATUS status;
3094 status = cli_smb2_close_fnum_recv(subreq);
3095 if (tevent_req_nterror(req, status)) {
3096 return;
3099 tevent_req_done(req);
3102 NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
3104 struct cli_smb2_mxac_state *state = tevent_req_data(
3105 req, struct cli_smb2_mxac_state);
3106 NTSTATUS status;
3108 if (tevent_req_is_nterror(req, &status)) {
3109 return status;
3112 if (!NT_STATUS_IS_OK(state->status)) {
3113 return state->status;
3116 *mxac = state->mxac;
3117 return NT_STATUS_OK;
3120 NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3121 const char *fname,
3122 uint32_t *_mxac)
3124 TALLOC_CTX *frame = talloc_stackframe();
3125 struct tevent_context *ev = NULL;
3126 struct tevent_req *req = NULL;
3127 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3128 bool ok;
3130 if (smbXcli_conn_has_async_calls(cli->conn)) {
3132 * Can't use sync call while an async call is in flight
3134 status = NT_STATUS_INVALID_PARAMETER;
3135 goto fail;
3138 ev = samba_tevent_context_init(frame);
3139 if (ev == NULL) {
3140 goto fail;
3142 req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3143 if (req == NULL) {
3144 goto fail;
3146 ok = tevent_req_poll_ntstatus(req, ev, &status);
3147 if (!ok) {
3148 goto fail;
3150 status = cli_smb2_query_mxac_recv(req, _mxac);
3152 fail:
3153 cli->raw_status = status;
3154 TALLOC_FREE(frame);
3155 return status;
3158 struct cli_smb2_rename_fnum_state {
3159 DATA_BLOB inbuf;
3162 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3164 static struct tevent_req *cli_smb2_rename_fnum_send(
3165 TALLOC_CTX *mem_ctx,
3166 struct tevent_context *ev,
3167 struct cli_state *cli,
3168 uint16_t fnum,
3169 const char *fname_dst,
3170 bool replace)
3172 struct tevent_req *req = NULL, *subreq = NULL;
3173 struct cli_smb2_rename_fnum_state *state = NULL;
3174 size_t namelen = strlen(fname_dst);
3175 smb_ucs2_t *converted_str = NULL;
3176 size_t converted_size_bytes = 0;
3177 size_t inbuf_size;
3178 bool ok;
3180 req = tevent_req_create(
3181 mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3182 if (req == NULL) {
3183 return NULL;
3187 * SMB2 is pickier about pathnames. Ensure it doesn't start in
3188 * a '\'
3190 if (*fname_dst == '\\') {
3191 fname_dst++;
3195 * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3196 * '\'
3198 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3199 fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3200 if (tevent_req_nomem(fname_dst, req)) {
3201 return tevent_req_post(req, ev);
3205 ok = push_ucs2_talloc(
3206 state, &converted_str, fname_dst, &converted_size_bytes);
3207 if (!ok) {
3208 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3209 return tevent_req_post(req, ev);
3213 * W2K8 insists the dest name is not null terminated. Remove
3214 * the last 2 zero bytes and reduce the name length.
3216 if (converted_size_bytes < 2) {
3217 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3218 return tevent_req_post(req, ev);
3220 converted_size_bytes -= 2;
3222 inbuf_size = 20 + converted_size_bytes;
3223 if (inbuf_size < 20) {
3224 /* Integer wrap check. */
3225 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3226 return tevent_req_post(req, ev);
3230 * The Windows 10 SMB2 server has a minimum length
3231 * for a SMB2_FILE_RENAME_INFORMATION buffer of
3232 * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3233 * if the length is less. This isn't an alignment
3234 * issue as Windows client happily 2-byte align
3235 * for larget target name sizes. Also the Windows 10
3236 * SMB1 server doesn't have this restriction.
3238 * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3240 inbuf_size = MAX(inbuf_size, 24);
3242 state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3243 if (tevent_req_nomem(state->inbuf.data, req)) {
3244 return tevent_req_post(req, ev);
3247 if (replace) {
3248 SCVAL(state->inbuf.data, 0, 1);
3251 SIVAL(state->inbuf.data, 16, converted_size_bytes);
3252 memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3254 TALLOC_FREE(converted_str);
3256 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3257 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3259 subreq = cli_smb2_set_info_fnum_send(
3260 state, /* mem_ctx */
3261 ev, /* ev */
3262 cli, /* cli */
3263 fnum, /* fnum */
3264 1, /* in_info_type */
3265 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
3266 &state->inbuf, /* in_input_buffer */
3267 0); /* in_additional_info */
3268 if (tevent_req_nomem(subreq, req)) {
3269 return tevent_req_post(req, ev);
3271 tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3272 return req;
3275 static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3277 NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3278 tevent_req_simple_finish_ntstatus(subreq, status);
3281 static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3283 return tevent_req_simple_recv_ntstatus(req);
3286 /***************************************************************
3287 Wrapper that allows SMB2 to rename a file.
3288 ***************************************************************/
3290 struct cli_smb2_rename_state {
3291 struct tevent_context *ev;
3292 struct cli_state *cli;
3293 const char *fname_dst;
3294 bool replace;
3295 uint16_t fnum;
3297 NTSTATUS rename_status;
3300 static void cli_smb2_rename_opened(struct tevent_req *subreq);
3301 static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3302 static void cli_smb2_rename_closed(struct tevent_req *subreq);
3304 struct tevent_req *cli_smb2_rename_send(
3305 TALLOC_CTX *mem_ctx,
3306 struct tevent_context *ev,
3307 struct cli_state *cli,
3308 const char *fname_src,
3309 const char *fname_dst,
3310 bool replace)
3312 struct tevent_req *req = NULL, *subreq = NULL;
3313 struct cli_smb2_rename_state *state = NULL;
3314 NTSTATUS status;
3316 req = tevent_req_create(
3317 mem_ctx, &state, struct cli_smb2_rename_state);
3318 if (req == NULL) {
3319 return NULL;
3323 * Strip a MSDFS path from fname_dst if we were given one.
3325 status = cli_dfs_target_check(state,
3326 cli,
3327 fname_dst,
3328 &fname_dst);
3329 if (tevent_req_nterror(req, status)) {
3330 return tevent_req_post(req, ev);
3333 state->ev = ev;
3334 state->cli = cli;
3335 state->fname_dst = fname_dst;
3336 state->replace = replace;
3338 subreq = get_fnum_from_path_send(
3339 state, ev, cli, fname_src, DELETE_ACCESS);
3340 if (tevent_req_nomem(subreq, req)) {
3341 return tevent_req_post(req, ev);
3343 tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3344 return req;
3347 static void cli_smb2_rename_opened(struct tevent_req *subreq)
3349 struct tevent_req *req = tevent_req_callback_data(
3350 subreq, struct tevent_req);
3351 struct cli_smb2_rename_state *state = tevent_req_data(
3352 req, struct cli_smb2_rename_state);
3353 NTSTATUS status;
3355 status = get_fnum_from_path_recv(subreq, &state->fnum);
3356 TALLOC_FREE(subreq);
3357 if (tevent_req_nterror(req, status)) {
3358 return;
3361 subreq = cli_smb2_rename_fnum_send(
3362 state,
3363 state->ev,
3364 state->cli,
3365 state->fnum,
3366 state->fname_dst,
3367 state->replace);
3368 if (tevent_req_nomem(subreq, req)) {
3369 return;
3371 tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3374 static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3376 struct tevent_req *req = tevent_req_callback_data(
3377 subreq, struct tevent_req);
3378 struct cli_smb2_rename_state *state = tevent_req_data(
3379 req, struct cli_smb2_rename_state);
3381 state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3382 TALLOC_FREE(subreq);
3384 subreq = cli_smb2_close_fnum_send(
3385 state, state->ev, state->cli, state->fnum);
3386 if (tevent_req_nomem(subreq, req)) {
3387 return;
3389 tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3392 static void cli_smb2_rename_closed(struct tevent_req *subreq)
3394 NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3395 tevent_req_simple_finish_ntstatus(subreq, status);
3398 NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3400 struct cli_smb2_rename_state *state = tevent_req_data(
3401 req, struct cli_smb2_rename_state);
3402 NTSTATUS status = NT_STATUS_OK;
3404 if (!tevent_req_is_nterror(req, &status)) {
3405 status = state->rename_status;
3407 tevent_req_received(req);
3408 return status;
3411 /***************************************************************
3412 Wrapper that allows SMB2 to set an EA on a fnum.
3413 Synchronous only.
3414 ***************************************************************/
3416 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3417 uint16_t fnum,
3418 const char *ea_name,
3419 const char *ea_val,
3420 size_t ea_len)
3422 NTSTATUS status;
3423 DATA_BLOB inbuf = data_blob_null;
3424 size_t bloblen = 0;
3425 char *ea_name_ascii = NULL;
3426 size_t namelen = 0;
3427 TALLOC_CTX *frame = talloc_stackframe();
3429 if (smbXcli_conn_has_async_calls(cli->conn)) {
3431 * Can't use sync call while an async call is in flight
3433 status = NT_STATUS_INVALID_PARAMETER;
3434 goto fail;
3437 /* Marshall the SMB2 EA data. */
3438 if (ea_len > 0xFFFF) {
3439 status = NT_STATUS_INVALID_PARAMETER;
3440 goto fail;
3443 if (!push_ascii_talloc(frame,
3444 &ea_name_ascii,
3445 ea_name,
3446 &namelen)) {
3447 status = NT_STATUS_INVALID_PARAMETER;
3448 goto fail;
3451 if (namelen < 2 || namelen > 0xFF) {
3452 status = NT_STATUS_INVALID_PARAMETER;
3453 goto fail;
3456 bloblen = 8 + ea_len + namelen;
3457 /* Round up to a 4 byte boundary. */
3458 bloblen = ((bloblen + 3)&~3);
3460 inbuf = data_blob_talloc_zero(frame, bloblen);
3461 if (inbuf.data == NULL) {
3462 status = NT_STATUS_NO_MEMORY;
3463 goto fail;
3465 /* namelen doesn't include the NULL byte. */
3466 SCVAL(inbuf.data, 5, namelen - 1);
3467 SSVAL(inbuf.data, 6, ea_len);
3468 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3469 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3471 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3472 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3474 status = cli_smb2_set_info_fnum(
3475 cli,
3476 fnum,
3477 1, /* in_info_type */
3478 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3479 &inbuf, /* in_input_buffer */
3480 0); /* in_additional_info */
3482 fail:
3484 cli->raw_status = status;
3486 TALLOC_FREE(frame);
3487 return status;
3490 /***************************************************************
3491 Wrapper that allows SMB2 to set an EA on a pathname.
3492 Synchronous only.
3493 ***************************************************************/
3495 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3496 const char *name,
3497 const char *ea_name,
3498 const char *ea_val,
3499 size_t ea_len)
3501 NTSTATUS status;
3502 uint16_t fnum = 0xffff;
3504 if (smbXcli_conn_has_async_calls(cli->conn)) {
3506 * Can't use sync call while an async call is in flight
3508 status = NT_STATUS_INVALID_PARAMETER;
3509 goto fail;
3512 status = get_fnum_from_path(cli,
3513 name,
3514 FILE_WRITE_EA,
3515 &fnum);
3517 if (!NT_STATUS_IS_OK(status)) {
3518 goto fail;
3521 status = cli_set_ea_fnum(cli,
3522 fnum,
3523 ea_name,
3524 ea_val,
3525 ea_len);
3526 if (!NT_STATUS_IS_OK(status)) {
3527 goto fail;
3530 fail:
3532 if (fnum != 0xffff) {
3533 cli_smb2_close_fnum(cli, fnum);
3536 cli->raw_status = status;
3538 return status;
3541 /***************************************************************
3542 Wrapper that allows SMB2 to get an EA list on a pathname.
3543 Synchronous only.
3544 ***************************************************************/
3546 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3547 const char *name,
3548 TALLOC_CTX *ctx,
3549 size_t *pnum_eas,
3550 struct ea_struct **pea_array)
3552 NTSTATUS status;
3553 uint16_t fnum = 0xffff;
3554 DATA_BLOB outbuf = data_blob_null;
3555 struct ea_list *ea_list = NULL;
3556 struct ea_list *eal = NULL;
3557 size_t ea_count = 0;
3558 TALLOC_CTX *frame = talloc_stackframe();
3560 *pnum_eas = 0;
3561 *pea_array = NULL;
3563 if (smbXcli_conn_has_async_calls(cli->conn)) {
3565 * Can't use sync call while an async call is in flight
3567 status = NT_STATUS_INVALID_PARAMETER;
3568 goto fail;
3571 status = get_fnum_from_path(cli,
3572 name,
3573 FILE_READ_EA,
3574 &fnum);
3576 if (!NT_STATUS_IS_OK(status)) {
3577 goto fail;
3580 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3581 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3583 status = cli_smb2_query_info_fnum(
3584 cli,
3585 fnum,
3586 1, /* in_info_type */
3587 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
3588 0xFFFF, /* in_max_output_length */
3589 NULL, /* in_input_buffer */
3590 0, /* in_additional_info */
3591 0, /* in_flags */
3592 frame,
3593 &outbuf);
3595 if (!NT_STATUS_IS_OK(status)) {
3596 goto fail;
3599 /* Parse the reply. */
3600 ea_list = read_nttrans_ea_list(ctx,
3601 (const char *)outbuf.data,
3602 outbuf.length);
3603 if (ea_list == NULL) {
3604 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3605 goto fail;
3608 /* Convert to an array. */
3609 for (eal = ea_list; eal; eal = eal->next) {
3610 ea_count++;
3613 if (ea_count) {
3614 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3615 if (*pea_array == NULL) {
3616 status = NT_STATUS_NO_MEMORY;
3617 goto fail;
3619 ea_count = 0;
3620 for (eal = ea_list; eal; eal = eal->next) {
3621 (*pea_array)[ea_count++] = eal->ea;
3623 *pnum_eas = ea_count;
3626 fail:
3628 if (fnum != 0xffff) {
3629 cli_smb2_close_fnum(cli, fnum);
3632 cli->raw_status = status;
3634 TALLOC_FREE(frame);
3635 return status;
3638 /***************************************************************
3639 Wrapper that allows SMB2 to get user quota.
3640 Synchronous only.
3641 ***************************************************************/
3643 NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3644 int quota_fnum,
3645 SMB_NTQUOTA_STRUCT *pqt)
3647 NTSTATUS status;
3648 DATA_BLOB inbuf = data_blob_null;
3649 DATA_BLOB info_blob = data_blob_null;
3650 DATA_BLOB outbuf = data_blob_null;
3651 TALLOC_CTX *frame = talloc_stackframe();
3652 unsigned sid_len;
3653 unsigned int offset;
3654 struct smb2_query_quota_info query = {0};
3655 struct file_get_quota_info info = {0};
3656 enum ndr_err_code err;
3657 struct ndr_push *ndr_push = NULL;
3659 if (smbXcli_conn_has_async_calls(cli->conn)) {
3661 * Can't use sync call while an async call is in flight
3663 status = NT_STATUS_INVALID_PARAMETER;
3664 goto fail;
3667 sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3669 query.return_single = 1;
3671 info.next_entry_offset = 0;
3672 info.sid_length = sid_len;
3673 info.sid = pqt->sid;
3675 err = ndr_push_struct_blob(
3676 &info_blob,
3677 frame,
3678 &info,
3679 (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3681 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3682 status = NT_STATUS_INTERNAL_ERROR;
3683 goto fail;
3686 query.sid_list_length = info_blob.length;
3687 ndr_push = ndr_push_init_ctx(frame);
3688 if (!ndr_push) {
3689 status = NT_STATUS_NO_MEMORY;
3690 goto fail;
3693 err = ndr_push_smb2_query_quota_info(ndr_push,
3694 NDR_SCALARS | NDR_BUFFERS,
3695 &query);
3697 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3698 status = NT_STATUS_INTERNAL_ERROR;
3699 goto fail;
3702 err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3703 info_blob.length);
3705 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3706 status = NT_STATUS_INTERNAL_ERROR;
3707 goto fail;
3709 inbuf.data = ndr_push->data;
3710 inbuf.length = ndr_push->offset;
3712 status = cli_smb2_query_info_fnum(
3713 cli,
3714 quota_fnum,
3715 4, /* in_info_type */
3716 0, /* in_file_info_class */
3717 0xFFFF, /* in_max_output_length */
3718 &inbuf, /* in_input_buffer */
3719 0, /* in_additional_info */
3720 0, /* in_flags */
3721 frame,
3722 &outbuf);
3724 if (!NT_STATUS_IS_OK(status)) {
3725 goto fail;
3728 if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3729 pqt)) {
3730 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3731 DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3734 fail:
3735 cli->raw_status = status;
3737 TALLOC_FREE(frame);
3738 return status;
3741 /***************************************************************
3742 Wrapper that allows SMB2 to list user quota.
3743 Synchronous only.
3744 ***************************************************************/
3746 NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3747 TALLOC_CTX *mem_ctx,
3748 int quota_fnum,
3749 SMB_NTQUOTA_LIST **pqt_list,
3750 bool first)
3752 NTSTATUS status;
3753 DATA_BLOB inbuf = data_blob_null;
3754 DATA_BLOB outbuf = data_blob_null;
3755 TALLOC_CTX *frame = talloc_stackframe();
3756 struct smb2_query_quota_info info = {0};
3757 enum ndr_err_code err;
3759 if (smbXcli_conn_has_async_calls(cli->conn)) {
3761 * Can't use sync call while an async call is in flight
3763 status = NT_STATUS_INVALID_PARAMETER;
3764 goto cleanup;
3767 info.restart_scan = first ? 1 : 0;
3769 err = ndr_push_struct_blob(
3770 &inbuf,
3771 frame,
3772 &info,
3773 (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3775 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3776 status = NT_STATUS_INTERNAL_ERROR;
3777 goto cleanup;
3780 status = cli_smb2_query_info_fnum(
3781 cli,
3782 quota_fnum,
3783 4, /* in_info_type */
3784 0, /* in_file_info_class */
3785 0xFFFF, /* in_max_output_length */
3786 &inbuf, /* in_input_buffer */
3787 0, /* in_additional_info */
3788 0, /* in_flags */
3789 frame,
3790 &outbuf);
3793 * safeguard against panic from calling parse_user_quota_list with
3794 * NULL buffer
3796 if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3797 status = NT_STATUS_NO_MORE_ENTRIES;
3800 if (!NT_STATUS_IS_OK(status)) {
3801 goto cleanup;
3804 status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3805 pqt_list);
3807 cleanup:
3808 cli->raw_status = status;
3810 TALLOC_FREE(frame);
3811 return status;
3814 /***************************************************************
3815 Wrapper that allows SMB2 to get file system quota.
3816 Synchronous only.
3817 ***************************************************************/
3819 NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3820 int quota_fnum,
3821 SMB_NTQUOTA_STRUCT *pqt)
3823 NTSTATUS status;
3824 DATA_BLOB outbuf = data_blob_null;
3825 TALLOC_CTX *frame = talloc_stackframe();
3827 if (smbXcli_conn_has_async_calls(cli->conn)) {
3829 * Can't use sync call while an async call is in flight
3831 status = NT_STATUS_INVALID_PARAMETER;
3832 goto cleanup;
3835 status = cli_smb2_query_info_fnum(
3836 cli,
3837 quota_fnum,
3838 2, /* in_info_type */
3839 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3840 0xFFFF, /* in_max_output_length */
3841 NULL, /* in_input_buffer */
3842 0, /* in_additional_info */
3843 0, /* in_flags */
3844 frame,
3845 &outbuf);
3847 if (!NT_STATUS_IS_OK(status)) {
3848 goto cleanup;
3851 status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3853 cleanup:
3854 cli->raw_status = status;
3856 TALLOC_FREE(frame);
3857 return status;
3860 /***************************************************************
3861 Wrapper that allows SMB2 to set user quota.
3862 Synchronous only.
3863 ***************************************************************/
3865 NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3866 int quota_fnum,
3867 SMB_NTQUOTA_LIST *qtl)
3869 NTSTATUS status;
3870 DATA_BLOB inbuf = data_blob_null;
3871 TALLOC_CTX *frame = talloc_stackframe();
3873 if (smbXcli_conn_has_async_calls(cli->conn)) {
3875 * Can't use sync call while an async call is in flight
3877 status = NT_STATUS_INVALID_PARAMETER;
3878 goto cleanup;
3881 status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3882 if (!NT_STATUS_IS_OK(status)) {
3883 goto cleanup;
3886 status = cli_smb2_set_info_fnum(
3887 cli,
3888 quota_fnum,
3889 4, /* in_info_type */
3890 0, /* in_file_info_class */
3891 &inbuf, /* in_input_buffer */
3892 0); /* in_additional_info */
3893 cleanup:
3895 cli->raw_status = status;
3897 TALLOC_FREE(frame);
3899 return status;
3902 NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3903 int quota_fnum,
3904 SMB_NTQUOTA_STRUCT *pqt)
3906 NTSTATUS status;
3907 DATA_BLOB inbuf = data_blob_null;
3908 TALLOC_CTX *frame = talloc_stackframe();
3910 if (smbXcli_conn_has_async_calls(cli->conn)) {
3912 * Can't use sync call while an async call is in flight
3914 status = NT_STATUS_INVALID_PARAMETER;
3915 goto cleanup;
3918 status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3919 if (!NT_STATUS_IS_OK(status)) {
3920 goto cleanup;
3923 status = cli_smb2_set_info_fnum(
3924 cli,
3925 quota_fnum,
3926 2, /* in_info_type */
3927 SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
3928 &inbuf, /* in_input_buffer */
3929 0); /* in_additional_info */
3930 cleanup:
3931 cli->raw_status = status;
3933 TALLOC_FREE(frame);
3934 return status;
3937 struct cli_smb2_read_state {
3938 struct tevent_context *ev;
3939 struct cli_state *cli;
3940 struct smb2_hnd *ph;
3941 uint64_t start_offset;
3942 uint32_t size;
3943 uint32_t received;
3944 uint8_t *buf;
3947 static void cli_smb2_read_done(struct tevent_req *subreq);
3949 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3950 struct tevent_context *ev,
3951 struct cli_state *cli,
3952 uint16_t fnum,
3953 off_t offset,
3954 size_t size)
3956 NTSTATUS status;
3957 struct tevent_req *req, *subreq;
3958 struct cli_smb2_read_state *state;
3960 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3961 if (req == NULL) {
3962 return NULL;
3964 state->ev = ev;
3965 state->cli = cli;
3966 state->start_offset = (uint64_t)offset;
3967 state->size = (uint32_t)size;
3968 state->received = 0;
3969 state->buf = NULL;
3971 status = map_fnum_to_smb2_handle(cli,
3972 fnum,
3973 &state->ph);
3974 if (tevent_req_nterror(req, status)) {
3975 return tevent_req_post(req, ev);
3978 subreq = smb2cli_read_send(state,
3979 state->ev,
3980 state->cli->conn,
3981 state->cli->timeout,
3982 state->cli->smb2.session,
3983 state->cli->smb2.tcon,
3984 state->size,
3985 state->start_offset,
3986 state->ph->fid_persistent,
3987 state->ph->fid_volatile,
3988 0, /* minimum_count */
3989 0); /* remaining_bytes */
3991 if (tevent_req_nomem(subreq, req)) {
3992 return tevent_req_post(req, ev);
3994 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3995 return req;
3998 static void cli_smb2_read_done(struct tevent_req *subreq)
4000 struct tevent_req *req = tevent_req_callback_data(
4001 subreq, struct tevent_req);
4002 struct cli_smb2_read_state *state = tevent_req_data(
4003 req, struct cli_smb2_read_state);
4004 NTSTATUS status;
4006 status = smb2cli_read_recv(subreq, state,
4007 &state->buf, &state->received);
4008 if (tevent_req_nterror(req, status)) {
4009 return;
4012 if (state->received > state->size) {
4013 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4014 return;
4017 tevent_req_done(req);
4020 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
4021 ssize_t *received,
4022 uint8_t **rcvbuf)
4024 NTSTATUS status;
4025 struct cli_smb2_read_state *state = tevent_req_data(
4026 req, struct cli_smb2_read_state);
4028 if (tevent_req_is_nterror(req, &status)) {
4029 state->cli->raw_status = status;
4030 return status;
4033 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
4034 * better make sure that you copy it away before you talloc_free(req).
4035 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
4037 *received = (ssize_t)state->received;
4038 *rcvbuf = state->buf;
4039 state->cli->raw_status = NT_STATUS_OK;
4040 return NT_STATUS_OK;
4043 struct cli_smb2_write_state {
4044 struct tevent_context *ev;
4045 struct cli_state *cli;
4046 struct smb2_hnd *ph;
4047 uint32_t flags;
4048 const uint8_t *buf;
4049 uint64_t offset;
4050 uint32_t size;
4051 uint32_t written;
4054 static void cli_smb2_write_written(struct tevent_req *req);
4056 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
4057 struct tevent_context *ev,
4058 struct cli_state *cli,
4059 uint16_t fnum,
4060 uint16_t mode,
4061 const uint8_t *buf,
4062 off_t offset,
4063 size_t size)
4065 NTSTATUS status;
4066 struct tevent_req *req, *subreq = NULL;
4067 struct cli_smb2_write_state *state = NULL;
4069 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
4070 if (req == NULL) {
4071 return NULL;
4073 state->ev = ev;
4074 state->cli = cli;
4075 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4076 state->flags = (uint32_t)mode;
4077 state->buf = buf;
4078 state->offset = (uint64_t)offset;
4079 state->size = (uint32_t)size;
4080 state->written = 0;
4082 status = map_fnum_to_smb2_handle(cli,
4083 fnum,
4084 &state->ph);
4085 if (tevent_req_nterror(req, status)) {
4086 return tevent_req_post(req, ev);
4089 subreq = smb2cli_write_send(state,
4090 state->ev,
4091 state->cli->conn,
4092 state->cli->timeout,
4093 state->cli->smb2.session,
4094 state->cli->smb2.tcon,
4095 state->size,
4096 state->offset,
4097 state->ph->fid_persistent,
4098 state->ph->fid_volatile,
4099 0, /* remaining_bytes */
4100 state->flags, /* flags */
4101 state->buf);
4103 if (tevent_req_nomem(subreq, req)) {
4104 return tevent_req_post(req, ev);
4106 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
4107 return req;
4110 static void cli_smb2_write_written(struct tevent_req *subreq)
4112 struct tevent_req *req = tevent_req_callback_data(
4113 subreq, struct tevent_req);
4114 struct cli_smb2_write_state *state = tevent_req_data(
4115 req, struct cli_smb2_write_state);
4116 NTSTATUS status;
4117 uint32_t written;
4119 status = smb2cli_write_recv(subreq, &written);
4120 TALLOC_FREE(subreq);
4121 if (tevent_req_nterror(req, status)) {
4122 return;
4125 state->written = written;
4127 tevent_req_done(req);
4130 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4131 size_t *pwritten)
4133 struct cli_smb2_write_state *state = tevent_req_data(
4134 req, struct cli_smb2_write_state);
4135 NTSTATUS status;
4137 if (tevent_req_is_nterror(req, &status)) {
4138 state->cli->raw_status = status;
4139 tevent_req_received(req);
4140 return status;
4143 if (pwritten != NULL) {
4144 *pwritten = (size_t)state->written;
4146 state->cli->raw_status = NT_STATUS_OK;
4147 tevent_req_received(req);
4148 return NT_STATUS_OK;
4151 /***************************************************************
4152 Wrapper that allows SMB2 async write using an fnum.
4153 This is mostly cut-and-paste from Volker's code inside
4154 source3/libsmb/clireadwrite.c, adapted for SMB2.
4156 Done this way so I can reuse all the logic inside cli_push()
4157 for free :-).
4158 ***************************************************************/
4160 struct cli_smb2_writeall_state {
4161 struct tevent_context *ev;
4162 struct cli_state *cli;
4163 struct smb2_hnd *ph;
4164 uint32_t flags;
4165 const uint8_t *buf;
4166 uint64_t offset;
4167 uint32_t size;
4168 uint32_t written;
4171 static void cli_smb2_writeall_written(struct tevent_req *req);
4173 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4174 struct tevent_context *ev,
4175 struct cli_state *cli,
4176 uint16_t fnum,
4177 uint16_t mode,
4178 const uint8_t *buf,
4179 off_t offset,
4180 size_t size)
4182 NTSTATUS status;
4183 struct tevent_req *req, *subreq = NULL;
4184 struct cli_smb2_writeall_state *state = NULL;
4185 uint32_t to_write;
4186 uint32_t max_size;
4187 bool ok;
4189 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4190 if (req == NULL) {
4191 return NULL;
4193 state->ev = ev;
4194 state->cli = cli;
4195 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4196 state->flags = (uint32_t)mode;
4197 state->buf = buf;
4198 state->offset = (uint64_t)offset;
4199 state->size = (uint32_t)size;
4200 state->written = 0;
4202 status = map_fnum_to_smb2_handle(cli,
4203 fnum,
4204 &state->ph);
4205 if (tevent_req_nterror(req, status)) {
4206 return tevent_req_post(req, ev);
4209 to_write = state->size;
4210 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4211 to_write = MIN(max_size, to_write);
4212 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4213 if (ok) {
4214 to_write = MIN(max_size, to_write);
4217 subreq = smb2cli_write_send(state,
4218 state->ev,
4219 state->cli->conn,
4220 state->cli->timeout,
4221 state->cli->smb2.session,
4222 state->cli->smb2.tcon,
4223 to_write,
4224 state->offset,
4225 state->ph->fid_persistent,
4226 state->ph->fid_volatile,
4227 0, /* remaining_bytes */
4228 state->flags, /* flags */
4229 state->buf + state->written);
4231 if (tevent_req_nomem(subreq, req)) {
4232 return tevent_req_post(req, ev);
4234 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4235 return req;
4238 static void cli_smb2_writeall_written(struct tevent_req *subreq)
4240 struct tevent_req *req = tevent_req_callback_data(
4241 subreq, struct tevent_req);
4242 struct cli_smb2_writeall_state *state = tevent_req_data(
4243 req, struct cli_smb2_writeall_state);
4244 NTSTATUS status;
4245 uint32_t written, to_write;
4246 uint32_t max_size;
4247 bool ok;
4249 status = smb2cli_write_recv(subreq, &written);
4250 TALLOC_FREE(subreq);
4251 if (tevent_req_nterror(req, status)) {
4252 return;
4255 state->written += written;
4257 if (state->written > state->size) {
4258 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4259 return;
4262 to_write = state->size - state->written;
4264 if (to_write == 0) {
4265 tevent_req_done(req);
4266 return;
4269 max_size = smb2cli_conn_max_write_size(state->cli->conn);
4270 to_write = MIN(max_size, to_write);
4271 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4272 if (ok) {
4273 to_write = MIN(max_size, to_write);
4276 subreq = smb2cli_write_send(state,
4277 state->ev,
4278 state->cli->conn,
4279 state->cli->timeout,
4280 state->cli->smb2.session,
4281 state->cli->smb2.tcon,
4282 to_write,
4283 state->offset + state->written,
4284 state->ph->fid_persistent,
4285 state->ph->fid_volatile,
4286 0, /* remaining_bytes */
4287 state->flags, /* flags */
4288 state->buf + state->written);
4290 if (tevent_req_nomem(subreq, req)) {
4291 return;
4293 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4296 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4297 size_t *pwritten)
4299 struct cli_smb2_writeall_state *state = tevent_req_data(
4300 req, struct cli_smb2_writeall_state);
4301 NTSTATUS status;
4303 if (tevent_req_is_nterror(req, &status)) {
4304 state->cli->raw_status = status;
4305 return status;
4307 if (pwritten != NULL) {
4308 *pwritten = (size_t)state->written;
4310 state->cli->raw_status = NT_STATUS_OK;
4311 return NT_STATUS_OK;
4314 struct cli_smb2_splice_state {
4315 struct tevent_context *ev;
4316 struct cli_state *cli;
4317 struct smb2_hnd *src_ph;
4318 struct smb2_hnd *dst_ph;
4319 int (*splice_cb)(off_t n, void *priv);
4320 void *priv;
4321 off_t written;
4322 off_t size;
4323 off_t src_offset;
4324 off_t dst_offset;
4325 bool resized;
4326 struct req_resume_key_rsp resume_rsp;
4327 struct srv_copychunk_copy cc_copy;
4330 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4331 struct tevent_req *req);
4333 static void cli_splice_copychunk_done(struct tevent_req *subreq)
4335 struct tevent_req *req = tevent_req_callback_data(
4336 subreq, struct tevent_req);
4337 struct cli_smb2_splice_state *state =
4338 tevent_req_data(req,
4339 struct cli_smb2_splice_state);
4340 struct smbXcli_conn *conn = state->cli->conn;
4341 DATA_BLOB out_input_buffer = data_blob_null;
4342 DATA_BLOB out_output_buffer = data_blob_null;
4343 struct srv_copychunk_rsp cc_copy_rsp;
4344 enum ndr_err_code ndr_ret;
4345 NTSTATUS status;
4347 status = smb2cli_ioctl_recv(subreq, state,
4348 &out_input_buffer,
4349 &out_output_buffer);
4350 TALLOC_FREE(subreq);
4351 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4352 state->resized) && tevent_req_nterror(req, status)) {
4353 return;
4356 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4357 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4358 if (ndr_ret != NDR_ERR_SUCCESS) {
4359 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4360 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4361 return;
4364 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4365 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4366 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4367 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4368 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4369 tevent_req_nterror(req, status)) {
4370 return;
4373 state->resized = true;
4374 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4375 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4376 } else {
4377 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4378 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4379 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4380 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4381 return;
4383 state->src_offset += cc_copy_rsp.total_bytes_written;
4384 state->dst_offset += cc_copy_rsp.total_bytes_written;
4385 state->written += cc_copy_rsp.total_bytes_written;
4386 if (!state->splice_cb(state->written, state->priv)) {
4387 tevent_req_nterror(req, NT_STATUS_CANCELLED);
4388 return;
4392 cli_splice_copychunk_send(state, req);
4395 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4396 struct tevent_req *req)
4398 struct tevent_req *subreq;
4399 enum ndr_err_code ndr_ret;
4400 struct smbXcli_conn *conn = state->cli->conn;
4401 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4402 off_t src_offset = state->src_offset;
4403 off_t dst_offset = state->dst_offset;
4404 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4405 state->size - state->written);
4406 DATA_BLOB in_input_buffer = data_blob_null;
4407 DATA_BLOB in_output_buffer = data_blob_null;
4409 if (state->size - state->written == 0) {
4410 tevent_req_done(req);
4411 return;
4414 cc_copy->chunk_count = 0;
4415 while (req_len) {
4416 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4417 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4418 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4419 smb2cli_conn_cc_chunk_len(conn));
4420 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4421 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4422 return;
4424 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4425 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4426 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4427 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4428 return;
4430 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4431 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4432 cc_copy->chunk_count++;
4435 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4436 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4437 if (ndr_ret != NDR_ERR_SUCCESS) {
4438 DEBUG(0, ("failed to marshall copy chunk req\n"));
4439 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4440 return;
4443 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4444 state->cli->timeout,
4445 state->cli->smb2.session,
4446 state->cli->smb2.tcon,
4447 state->dst_ph->fid_persistent, /* in_fid_persistent */
4448 state->dst_ph->fid_volatile, /* in_fid_volatile */
4449 FSCTL_SRV_COPYCHUNK_WRITE,
4450 0, /* in_max_input_length */
4451 &in_input_buffer,
4452 12, /* in_max_output_length */
4453 &in_output_buffer,
4454 SMB2_IOCTL_FLAG_IS_FSCTL);
4455 if (tevent_req_nomem(subreq, req)) {
4456 return;
4458 tevent_req_set_callback(subreq,
4459 cli_splice_copychunk_done,
4460 req);
4463 static void cli_splice_key_done(struct tevent_req *subreq)
4465 struct tevent_req *req = tevent_req_callback_data(
4466 subreq, struct tevent_req);
4467 struct cli_smb2_splice_state *state =
4468 tevent_req_data(req,
4469 struct cli_smb2_splice_state);
4470 enum ndr_err_code ndr_ret;
4471 NTSTATUS status;
4473 DATA_BLOB out_input_buffer = data_blob_null;
4474 DATA_BLOB out_output_buffer = data_blob_null;
4476 status = smb2cli_ioctl_recv(subreq, state,
4477 &out_input_buffer,
4478 &out_output_buffer);
4479 TALLOC_FREE(subreq);
4480 if (tevent_req_nterror(req, status)) {
4481 return;
4484 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4485 state, &state->resume_rsp,
4486 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4487 if (ndr_ret != NDR_ERR_SUCCESS) {
4488 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4489 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4490 return;
4493 memcpy(&state->cc_copy.source_key,
4494 &state->resume_rsp.resume_key,
4495 sizeof state->resume_rsp.resume_key);
4497 cli_splice_copychunk_send(state, req);
4500 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4501 struct tevent_context *ev,
4502 struct cli_state *cli,
4503 uint16_t src_fnum, uint16_t dst_fnum,
4504 off_t size, off_t src_offset, off_t dst_offset,
4505 int (*splice_cb)(off_t n, void *priv),
4506 void *priv)
4508 struct tevent_req *req;
4509 struct tevent_req *subreq;
4510 struct cli_smb2_splice_state *state;
4511 NTSTATUS status;
4512 DATA_BLOB in_input_buffer = data_blob_null;
4513 DATA_BLOB in_output_buffer = data_blob_null;
4515 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4516 if (req == NULL) {
4517 return NULL;
4519 state->cli = cli;
4520 state->ev = ev;
4521 state->splice_cb = splice_cb;
4522 state->priv = priv;
4523 state->size = size;
4524 state->written = 0;
4525 state->src_offset = src_offset;
4526 state->dst_offset = dst_offset;
4527 state->cc_copy.chunks = talloc_array(state,
4528 struct srv_copychunk,
4529 smb2cli_conn_cc_max_chunks(cli->conn));
4530 if (state->cc_copy.chunks == NULL) {
4531 return NULL;
4534 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4535 if (tevent_req_nterror(req, status))
4536 return tevent_req_post(req, ev);
4538 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4539 if (tevent_req_nterror(req, status))
4540 return tevent_req_post(req, ev);
4542 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4543 cli->timeout,
4544 cli->smb2.session,
4545 cli->smb2.tcon,
4546 state->src_ph->fid_persistent, /* in_fid_persistent */
4547 state->src_ph->fid_volatile, /* in_fid_volatile */
4548 FSCTL_SRV_REQUEST_RESUME_KEY,
4549 0, /* in_max_input_length */
4550 &in_input_buffer,
4551 32, /* in_max_output_length */
4552 &in_output_buffer,
4553 SMB2_IOCTL_FLAG_IS_FSCTL);
4554 if (tevent_req_nomem(subreq, req)) {
4555 return NULL;
4557 tevent_req_set_callback(subreq,
4558 cli_splice_key_done,
4559 req);
4561 return req;
4564 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4566 struct cli_smb2_splice_state *state = tevent_req_data(
4567 req, struct cli_smb2_splice_state);
4568 NTSTATUS status;
4570 if (tevent_req_is_nterror(req, &status)) {
4571 state->cli->raw_status = status;
4572 tevent_req_received(req);
4573 return status;
4575 if (written != NULL) {
4576 *written = state->written;
4578 state->cli->raw_status = NT_STATUS_OK;
4579 tevent_req_received(req);
4580 return NT_STATUS_OK;
4583 /***************************************************************
4584 SMB2 enum shadow copy data.
4585 ***************************************************************/
4587 struct cli_smb2_shadow_copy_data_fnum_state {
4588 struct cli_state *cli;
4589 uint16_t fnum;
4590 struct smb2_hnd *ph;
4591 DATA_BLOB out_input_buffer;
4592 DATA_BLOB out_output_buffer;
4595 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4597 static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4598 TALLOC_CTX *mem_ctx,
4599 struct tevent_context *ev,
4600 struct cli_state *cli,
4601 uint16_t fnum,
4602 bool get_names)
4604 struct tevent_req *req, *subreq;
4605 struct cli_smb2_shadow_copy_data_fnum_state *state;
4606 NTSTATUS status;
4608 req = tevent_req_create(mem_ctx, &state,
4609 struct cli_smb2_shadow_copy_data_fnum_state);
4610 if (req == NULL) {
4611 return NULL;
4614 state->cli = cli;
4615 state->fnum = fnum;
4617 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4618 if (tevent_req_nterror(req, status)) {
4619 return tevent_req_post(req, ev);
4623 * TODO. Under SMB2 we should send a zero max_output_length
4624 * ioctl to get the required size, then send another ioctl
4625 * to get the data, but the current SMB1 implementation just
4626 * does one roundtrip with a 64K buffer size. Do the same
4627 * for now. JRA.
4630 subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4631 state->cli->timeout,
4632 state->cli->smb2.session,
4633 state->cli->smb2.tcon,
4634 state->ph->fid_persistent, /* in_fid_persistent */
4635 state->ph->fid_volatile, /* in_fid_volatile */
4636 FSCTL_GET_SHADOW_COPY_DATA,
4637 0, /* in_max_input_length */
4638 NULL, /* in_input_buffer */
4639 get_names ?
4640 CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4641 NULL, /* in_output_buffer */
4642 SMB2_IOCTL_FLAG_IS_FSCTL);
4644 if (tevent_req_nomem(subreq, req)) {
4645 return tevent_req_post(req, ev);
4647 tevent_req_set_callback(subreq,
4648 cli_smb2_shadow_copy_data_fnum_done,
4649 req);
4651 return req;
4654 static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4656 struct tevent_req *req = tevent_req_callback_data(
4657 subreq, struct tevent_req);
4658 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4659 req, struct cli_smb2_shadow_copy_data_fnum_state);
4660 NTSTATUS status;
4662 status = smb2cli_ioctl_recv(subreq, state,
4663 &state->out_input_buffer,
4664 &state->out_output_buffer);
4665 tevent_req_simple_finish_ntstatus(subreq, status);
4668 static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4669 TALLOC_CTX *mem_ctx,
4670 bool get_names,
4671 char ***pnames,
4672 int *pnum_names)
4674 struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4675 req, struct cli_smb2_shadow_copy_data_fnum_state);
4676 char **names = NULL;
4677 uint32_t num_names = 0;
4678 uint32_t num_names_returned = 0;
4679 uint32_t dlength = 0;
4680 uint32_t i;
4681 uint8_t *endp = NULL;
4682 NTSTATUS status;
4684 if (tevent_req_is_nterror(req, &status)) {
4685 return status;
4688 if (state->out_output_buffer.length < 16) {
4689 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4692 num_names = IVAL(state->out_output_buffer.data, 0);
4693 num_names_returned = IVAL(state->out_output_buffer.data, 4);
4694 dlength = IVAL(state->out_output_buffer.data, 8);
4696 if (num_names > 0x7FFFFFFF) {
4697 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4700 if (get_names == false) {
4701 *pnum_names = (int)num_names;
4702 return NT_STATUS_OK;
4704 if (num_names != num_names_returned) {
4705 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4707 if (dlength + 12 < 12) {
4708 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4711 * NB. The below is an allowable return if there are
4712 * more snapshots than the buffer size we told the
4713 * server we can receive. We currently don't support
4714 * this.
4716 if (dlength + 12 > state->out_output_buffer.length) {
4717 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4719 if (state->out_output_buffer.length +
4720 (2 * sizeof(SHADOW_COPY_LABEL)) <
4721 state->out_output_buffer.length) {
4722 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4725 names = talloc_array(mem_ctx, char *, num_names_returned);
4726 if (names == NULL) {
4727 return NT_STATUS_NO_MEMORY;
4730 endp = state->out_output_buffer.data +
4731 state->out_output_buffer.length;
4733 for (i=0; i<num_names_returned; i++) {
4734 bool ret;
4735 uint8_t *src;
4736 size_t converted_size;
4738 src = state->out_output_buffer.data + 12 +
4739 (i * 2 * sizeof(SHADOW_COPY_LABEL));
4741 if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4742 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4744 ret = convert_string_talloc(
4745 names, CH_UTF16LE, CH_UNIX,
4746 src, 2 * sizeof(SHADOW_COPY_LABEL),
4747 &names[i], &converted_size);
4748 if (!ret) {
4749 TALLOC_FREE(names);
4750 return NT_STATUS_INVALID_NETWORK_RESPONSE;
4753 *pnum_names = num_names;
4754 *pnames = names;
4755 return NT_STATUS_OK;
4758 NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4759 struct cli_state *cli,
4760 uint16_t fnum,
4761 bool get_names,
4762 char ***pnames,
4763 int *pnum_names)
4765 TALLOC_CTX *frame = talloc_stackframe();
4766 struct tevent_context *ev;
4767 struct tevent_req *req;
4768 NTSTATUS status = NT_STATUS_NO_MEMORY;
4770 if (smbXcli_conn_has_async_calls(cli->conn)) {
4772 * Can't use sync call while an async call is in flight
4774 status = NT_STATUS_INVALID_PARAMETER;
4775 goto fail;
4777 ev = samba_tevent_context_init(frame);
4778 if (ev == NULL) {
4779 goto fail;
4781 req = cli_smb2_shadow_copy_data_fnum_send(frame,
4783 cli,
4784 fnum,
4785 get_names);
4786 if (req == NULL) {
4787 goto fail;
4789 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4790 goto fail;
4792 status = cli_smb2_shadow_copy_data_fnum_recv(req,
4793 mem_ctx,
4794 get_names,
4795 pnames,
4796 pnum_names);
4797 fail:
4798 cli->raw_status = status;
4800 TALLOC_FREE(frame);
4801 return status;
4804 /***************************************************************
4805 Wrapper that allows SMB2 to truncate a file.
4806 Synchronous only.
4807 ***************************************************************/
4809 NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4810 uint16_t fnum,
4811 uint64_t newsize)
4813 NTSTATUS status;
4814 uint8_t buf[8] = {0};
4815 DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4816 TALLOC_CTX *frame = talloc_stackframe();
4818 if (smbXcli_conn_has_async_calls(cli->conn)) {
4820 * Can't use sync call while an async call is in flight
4822 status = NT_STATUS_INVALID_PARAMETER;
4823 goto fail;
4826 SBVAL(buf, 0, newsize);
4828 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4829 level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4831 status = cli_smb2_set_info_fnum(
4832 cli,
4833 fnum,
4834 1, /* in_info_type */
4835 SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */
4836 &inbuf, /* in_input_buffer */
4839 fail:
4841 cli->raw_status = status;
4843 TALLOC_FREE(frame);
4844 return status;
4847 struct cli_smb2_notify_state {
4848 struct tevent_req *subreq;
4849 struct notify_change *changes;
4850 size_t num_changes;
4853 static void cli_smb2_notify_done(struct tevent_req *subreq);
4854 static bool cli_smb2_notify_cancel(struct tevent_req *req);
4856 struct tevent_req *cli_smb2_notify_send(
4857 TALLOC_CTX *mem_ctx,
4858 struct tevent_context *ev,
4859 struct cli_state *cli,
4860 uint16_t fnum,
4861 uint32_t buffer_size,
4862 uint32_t completion_filter,
4863 bool recursive)
4865 struct tevent_req *req = NULL;
4866 struct cli_smb2_notify_state *state = NULL;
4867 struct smb2_hnd *ph = NULL;
4868 NTSTATUS status;
4870 req = tevent_req_create(mem_ctx, &state,
4871 struct cli_smb2_notify_state);
4872 if (req == NULL) {
4873 return NULL;
4876 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4877 if (tevent_req_nterror(req, status)) {
4878 return tevent_req_post(req, ev);
4881 state->subreq = smb2cli_notify_send(
4882 state,
4884 cli->conn,
4885 cli->timeout,
4886 cli->smb2.session,
4887 cli->smb2.tcon,
4888 buffer_size,
4889 ph->fid_persistent,
4890 ph->fid_volatile,
4891 completion_filter,
4892 recursive);
4893 if (tevent_req_nomem(state->subreq, req)) {
4894 return tevent_req_post(req, ev);
4896 tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4897 tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4898 return req;
4901 static bool cli_smb2_notify_cancel(struct tevent_req *req)
4903 struct cli_smb2_notify_state *state = tevent_req_data(
4904 req, struct cli_smb2_notify_state);
4905 bool ok;
4907 ok = tevent_req_cancel(state->subreq);
4908 return ok;
4911 static void cli_smb2_notify_done(struct tevent_req *subreq)
4913 struct tevent_req *req = tevent_req_callback_data(
4914 subreq, struct tevent_req);
4915 struct cli_smb2_notify_state *state = tevent_req_data(
4916 req, struct cli_smb2_notify_state);
4917 uint8_t *base;
4918 uint32_t len;
4919 uint32_t ofs;
4920 NTSTATUS status;
4922 status = smb2cli_notify_recv(subreq, state, &base, &len);
4923 TALLOC_FREE(subreq);
4925 if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4926 tevent_req_done(req);
4927 return;
4929 if (tevent_req_nterror(req, status)) {
4930 return;
4933 ofs = 0;
4935 while (len - ofs >= 12) {
4936 struct notify_change *tmp;
4937 struct notify_change *c;
4938 uint32_t next_ofs = IVAL(base, ofs);
4939 uint32_t file_name_length = IVAL(base, ofs+8);
4940 size_t namelen;
4941 bool ok;
4943 tmp = talloc_realloc(
4944 state,
4945 state->changes,
4946 struct notify_change,
4947 state->num_changes + 1);
4948 if (tevent_req_nomem(tmp, req)) {
4949 return;
4951 state->changes = tmp;
4952 c = &state->changes[state->num_changes];
4953 state->num_changes += 1;
4955 if (smb_buffer_oob(len, ofs, next_ofs) ||
4956 smb_buffer_oob(len, ofs+12, file_name_length)) {
4957 tevent_req_nterror(
4958 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4959 return;
4962 c->action = IVAL(base, ofs+4);
4964 ok = convert_string_talloc(
4965 state->changes,
4966 CH_UTF16LE,
4967 CH_UNIX,
4968 base + ofs + 12,
4969 file_name_length,
4970 &c->name,
4971 &namelen);
4972 if (!ok) {
4973 tevent_req_nterror(
4974 req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4975 return;
4978 if (next_ofs == 0) {
4979 break;
4981 ofs += next_ofs;
4984 tevent_req_done(req);
4987 NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4988 TALLOC_CTX *mem_ctx,
4989 struct notify_change **pchanges,
4990 uint32_t *pnum_changes)
4992 struct cli_smb2_notify_state *state = tevent_req_data(
4993 req, struct cli_smb2_notify_state);
4994 NTSTATUS status;
4996 if (tevent_req_is_nterror(req, &status)) {
4997 return status;
4999 *pchanges = talloc_move(mem_ctx, &state->changes);
5000 *pnum_changes = state->num_changes;
5001 return NT_STATUS_OK;
5004 NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
5005 uint32_t buffer_size, uint32_t completion_filter,
5006 bool recursive, TALLOC_CTX *mem_ctx,
5007 struct notify_change **pchanges,
5008 uint32_t *pnum_changes)
5010 TALLOC_CTX *frame = talloc_stackframe();
5011 struct tevent_context *ev;
5012 struct tevent_req *req;
5013 NTSTATUS status = NT_STATUS_NO_MEMORY;
5015 if (smbXcli_conn_has_async_calls(cli->conn)) {
5017 * Can't use sync call while an async call is in flight
5019 status = NT_STATUS_INVALID_PARAMETER;
5020 goto fail;
5022 ev = samba_tevent_context_init(frame);
5023 if (ev == NULL) {
5024 goto fail;
5026 req = cli_smb2_notify_send(
5027 frame,
5029 cli,
5030 fnum,
5031 buffer_size,
5032 completion_filter,
5033 recursive);
5034 if (req == NULL) {
5035 goto fail;
5037 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
5038 goto fail;
5040 status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
5041 fail:
5042 TALLOC_FREE(frame);
5043 return status;
5046 struct cli_smb2_fsctl_state {
5047 DATA_BLOB out;
5050 static void cli_smb2_fsctl_done(struct tevent_req *subreq);
5052 struct tevent_req *cli_smb2_fsctl_send(
5053 TALLOC_CTX *mem_ctx,
5054 struct tevent_context *ev,
5055 struct cli_state *cli,
5056 uint16_t fnum,
5057 uint32_t ctl_code,
5058 const DATA_BLOB *in,
5059 uint32_t max_out)
5061 struct tevent_req *req = NULL, *subreq = NULL;
5062 struct cli_smb2_fsctl_state *state = NULL;
5063 struct smb2_hnd *ph = NULL;
5064 NTSTATUS status;
5066 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
5067 if (req == NULL) {
5068 return NULL;
5071 status = map_fnum_to_smb2_handle(cli, fnum, &ph);
5072 if (tevent_req_nterror(req, status)) {
5073 return tevent_req_post(req, ev);
5076 subreq = smb2cli_ioctl_send(
5077 state,
5079 cli->conn,
5080 cli->timeout,
5081 cli->smb2.session,
5082 cli->smb2.tcon,
5083 ph->fid_persistent,
5084 ph->fid_volatile,
5085 ctl_code,
5086 0, /* in_max_input_length */
5088 max_out,
5089 NULL,
5090 SMB2_IOCTL_FLAG_IS_FSCTL);
5092 if (tevent_req_nomem(subreq, req)) {
5093 return tevent_req_post(req, ev);
5095 tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
5096 return req;
5099 static void cli_smb2_fsctl_done(struct tevent_req *subreq)
5101 struct tevent_req *req = tevent_req_callback_data(
5102 subreq, struct tevent_req);
5103 struct cli_smb2_fsctl_state *state = tevent_req_data(
5104 req, struct cli_smb2_fsctl_state);
5105 NTSTATUS status;
5107 status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
5108 tevent_req_simple_finish_ntstatus(subreq, status);
5111 NTSTATUS cli_smb2_fsctl_recv(
5112 struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5114 struct cli_smb2_fsctl_state *state = tevent_req_data(
5115 req, struct cli_smb2_fsctl_state);
5116 NTSTATUS status = NT_STATUS_OK;
5118 if (tevent_req_is_nterror(req, &status)) {
5119 tevent_req_received(req);
5120 return status;
5123 if (state->out.length == 0) {
5124 *out = (DATA_BLOB) { .data = NULL, };
5125 } else {
5127 * Can't use talloc_move() here, the outblobs from
5128 * smb2cli_ioctl_recv() are not standalone talloc
5129 * objects but just peek into the larger buffers
5130 * received, hanging off "state".
5132 *out = data_blob_talloc(
5133 mem_ctx, state->out.data, state->out.length);
5134 if (out->data == NULL) {
5135 status = NT_STATUS_NO_MEMORY;
5139 tevent_req_received(req);
5140 return NT_STATUS_OK;