fssd: include config.h before any glibc headers
[Samba.git] / source3 / libsmb / cli_smb2_fnum.c
blob816ad1374a30928953a04f5484f8e0436e4d809a
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 "lib/util_ea.h"
41 #include "librpc/gen_ndr/ndr_ioctl.h"
43 struct smb2_hnd {
44 uint64_t fid_persistent;
45 uint64_t fid_volatile;
49 * Handle mapping code.
52 /***************************************************************
53 Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
54 Ensures handle is owned by cli struct.
55 ***************************************************************/
57 static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
58 const struct smb2_hnd *ph, /* In */
59 uint16_t *pfnum) /* Out */
61 int ret;
62 struct idr_context *idp = cli->smb2.open_handles;
63 struct smb2_hnd *owned_h = talloc_memdup(cli,
64 ph,
65 sizeof(struct smb2_hnd));
67 if (owned_h == NULL) {
68 return NT_STATUS_NO_MEMORY;
71 if (idp == NULL) {
72 /* Lazy init */
73 cli->smb2.open_handles = idr_init(cli);
74 if (cli->smb2.open_handles == NULL) {
75 TALLOC_FREE(owned_h);
76 return NT_STATUS_NO_MEMORY;
78 idp = cli->smb2.open_handles;
81 ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
82 if (ret == -1) {
83 TALLOC_FREE(owned_h);
84 return NT_STATUS_NO_MEMORY;
87 *pfnum = (uint16_t)ret;
88 return NT_STATUS_OK;
91 /***************************************************************
92 Return the smb2_hnd pointer associated with the given fnum.
93 ***************************************************************/
95 static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
96 uint16_t fnum, /* In */
97 struct smb2_hnd **pph) /* Out */
99 struct idr_context *idp = cli->smb2.open_handles;
101 if (idp == NULL) {
102 return NT_STATUS_INVALID_PARAMETER;
104 *pph = (struct smb2_hnd *)idr_find(idp, fnum);
105 if (*pph == NULL) {
106 return NT_STATUS_INVALID_HANDLE;
108 return NT_STATUS_OK;
111 /***************************************************************
112 Delete the fnum to smb2_hnd mapping. Zeros out handle on
113 successful return.
114 ***************************************************************/
116 static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
117 struct smb2_hnd **pph, /* In */
118 uint16_t fnum) /* In */
120 struct idr_context *idp = cli->smb2.open_handles;
121 struct smb2_hnd *ph;
123 if (idp == NULL) {
124 return NT_STATUS_INVALID_PARAMETER;
127 ph = (struct smb2_hnd *)idr_find(idp, fnum);
128 if (ph != *pph) {
129 return NT_STATUS_INVALID_PARAMETER;
131 idr_remove(idp, fnum);
132 TALLOC_FREE(*pph);
133 return NT_STATUS_OK;
136 /***************************************************************
137 Oplock mapping code.
138 ***************************************************************/
140 static uint8_t flags_to_smb2_oplock(uint32_t create_flags)
142 if (create_flags & REQUEST_BATCH_OPLOCK) {
143 return SMB2_OPLOCK_LEVEL_BATCH;
144 } else if (create_flags & REQUEST_OPLOCK) {
145 return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
148 /* create_flags doesn't do a level2 request. */
149 return SMB2_OPLOCK_LEVEL_NONE;
152 /***************************************************************
153 Small wrapper that allows SMB2 create to return a uint16_t fnum.
154 ***************************************************************/
156 struct cli_smb2_create_fnum_state {
157 struct cli_state *cli;
158 struct smb_create_returns cr;
159 uint16_t fnum;
160 struct tevent_req *subreq;
163 static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
164 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
166 struct tevent_req *cli_smb2_create_fnum_send(TALLOC_CTX *mem_ctx,
167 struct tevent_context *ev,
168 struct cli_state *cli,
169 const char *fname,
170 uint32_t create_flags,
171 uint32_t desired_access,
172 uint32_t file_attributes,
173 uint32_t share_access,
174 uint32_t create_disposition,
175 uint32_t create_options)
177 struct tevent_req *req, *subreq;
178 struct cli_smb2_create_fnum_state *state;
180 req = tevent_req_create(mem_ctx, &state,
181 struct cli_smb2_create_fnum_state);
182 if (req == NULL) {
183 return NULL;
185 state->cli = cli;
187 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
188 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
189 return tevent_req_post(req, ev);
192 if (cli->backup_intent) {
193 create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
196 /* SMB2 is pickier about pathnames. Ensure it doesn't
197 start in a '\' */
198 if (*fname == '\\') {
199 fname++;
202 subreq = smb2cli_create_send(state, ev,
203 cli->conn,
204 cli->timeout,
205 cli->smb2.session,
206 cli->smb2.tcon,
207 fname,
208 flags_to_smb2_oplock(create_flags),
209 SMB2_IMPERSONATION_IMPERSONATION,
210 desired_access,
211 file_attributes,
212 share_access,
213 create_disposition,
214 create_options,
215 NULL);
216 if (tevent_req_nomem(subreq, req)) {
217 return tevent_req_post(req, ev);
219 tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
221 state->subreq = subreq;
222 tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
224 return req;
227 static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
229 struct tevent_req *req = tevent_req_callback_data(
230 subreq, struct tevent_req);
231 struct cli_smb2_create_fnum_state *state = tevent_req_data(
232 req, struct cli_smb2_create_fnum_state);
233 struct smb2_hnd h;
234 NTSTATUS status;
236 status = smb2cli_create_recv(subreq, &h.fid_persistent,
237 &h.fid_volatile, &state->cr, NULL, NULL);
238 TALLOC_FREE(subreq);
239 if (tevent_req_nterror(req, status)) {
240 return;
243 status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
244 if (tevent_req_nterror(req, status)) {
245 return;
247 tevent_req_done(req);
250 static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
252 struct cli_smb2_create_fnum_state *state = tevent_req_data(
253 req, struct cli_smb2_create_fnum_state);
254 return tevent_req_cancel(state->subreq);
257 NTSTATUS cli_smb2_create_fnum_recv(struct tevent_req *req, uint16_t *pfnum,
258 struct smb_create_returns *cr)
260 struct cli_smb2_create_fnum_state *state = tevent_req_data(
261 req, struct cli_smb2_create_fnum_state);
262 NTSTATUS status;
264 if (tevent_req_is_nterror(req, &status)) {
265 return status;
267 if (pfnum != NULL) {
268 *pfnum = state->fnum;
270 if (cr != NULL) {
271 *cr = state->cr;
273 return NT_STATUS_OK;
276 NTSTATUS cli_smb2_create_fnum(struct cli_state *cli,
277 const char *fname,
278 uint32_t create_flags,
279 uint32_t desired_access,
280 uint32_t file_attributes,
281 uint32_t share_access,
282 uint32_t create_disposition,
283 uint32_t create_options,
284 uint16_t *pfid,
285 struct smb_create_returns *cr)
287 TALLOC_CTX *frame = talloc_stackframe();
288 struct tevent_context *ev;
289 struct tevent_req *req;
290 NTSTATUS status = NT_STATUS_NO_MEMORY;
292 if (smbXcli_conn_has_async_calls(cli->conn)) {
294 * Can't use sync call while an async call is in flight
296 status = NT_STATUS_INVALID_PARAMETER;
297 goto fail;
299 ev = samba_tevent_context_init(frame);
300 if (ev == NULL) {
301 goto fail;
303 req = cli_smb2_create_fnum_send(frame, ev, cli, fname, create_flags,
304 desired_access, file_attributes,
305 share_access, create_disposition,
306 create_options);
307 if (req == NULL) {
308 goto fail;
310 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
311 goto fail;
313 status = cli_smb2_create_fnum_recv(req, pfid, cr);
314 fail:
315 TALLOC_FREE(frame);
316 return status;
319 /***************************************************************
320 Small wrapper that allows SMB2 close to use a uint16_t fnum.
321 ***************************************************************/
323 struct cli_smb2_close_fnum_state {
324 struct cli_state *cli;
325 uint16_t fnum;
326 struct smb2_hnd *ph;
329 static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
331 struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
332 struct tevent_context *ev,
333 struct cli_state *cli,
334 uint16_t fnum)
336 struct tevent_req *req, *subreq;
337 struct cli_smb2_close_fnum_state *state;
338 NTSTATUS status;
340 req = tevent_req_create(mem_ctx, &state,
341 struct cli_smb2_close_fnum_state);
342 if (req == NULL) {
343 return NULL;
345 state->cli = cli;
346 state->fnum = fnum;
348 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
349 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
350 return tevent_req_post(req, ev);
353 status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
354 if (tevent_req_nterror(req, status)) {
355 return tevent_req_post(req, ev);
358 subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout,
359 cli->smb2.session, cli->smb2.tcon,
360 0, state->ph->fid_persistent,
361 state->ph->fid_volatile);
362 if (tevent_req_nomem(subreq, req)) {
363 return tevent_req_post(req, ev);
365 tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
366 return req;
369 static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
371 struct tevent_req *req = tevent_req_callback_data(
372 subreq, struct tevent_req);
373 struct cli_smb2_close_fnum_state *state = tevent_req_data(
374 req, struct cli_smb2_close_fnum_state);
375 NTSTATUS status;
377 status = smb2cli_close_recv(subreq);
378 if (tevent_req_nterror(req, status)) {
379 return;
382 /* Delete the fnum -> handle mapping. */
383 status = delete_smb2_handle_mapping(state->cli, &state->ph,
384 state->fnum);
385 if (tevent_req_nterror(req, status)) {
386 return;
388 tevent_req_done(req);
391 NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
393 return tevent_req_simple_recv_ntstatus(req);
396 NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
398 TALLOC_CTX *frame = talloc_stackframe();
399 struct tevent_context *ev;
400 struct tevent_req *req;
401 NTSTATUS status = NT_STATUS_NO_MEMORY;
403 if (smbXcli_conn_has_async_calls(cli->conn)) {
405 * Can't use sync call while an async call is in flight
407 status = NT_STATUS_INVALID_PARAMETER;
408 goto fail;
410 ev = samba_tevent_context_init(frame);
411 if (ev == NULL) {
412 goto fail;
414 req = cli_smb2_close_fnum_send(frame, ev, cli, fnum);
415 if (req == NULL) {
416 goto fail;
418 if (!tevent_req_poll_ntstatus(req, ev, &status)) {
419 goto fail;
421 status = cli_smb2_close_fnum_recv(req);
422 fail:
423 TALLOC_FREE(frame);
424 return status;
427 /***************************************************************
428 Small wrapper that allows SMB2 to create a directory
429 Synchronous only.
430 ***************************************************************/
432 NTSTATUS cli_smb2_mkdir(struct cli_state *cli, const char *dname)
434 NTSTATUS status;
435 uint16_t fnum;
437 if (smbXcli_conn_has_async_calls(cli->conn)) {
439 * Can't use sync call while an async call is in flight
441 return NT_STATUS_INVALID_PARAMETER;
444 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
445 return NT_STATUS_INVALID_PARAMETER;
448 status = cli_smb2_create_fnum(cli,
449 dname,
450 0, /* create_flags */
451 FILE_READ_ATTRIBUTES, /* desired_access */
452 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
453 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
454 FILE_CREATE, /* create_disposition */
455 FILE_DIRECTORY_FILE, /* create_options */
456 &fnum,
457 NULL);
459 if (!NT_STATUS_IS_OK(status)) {
460 return status;
462 return cli_smb2_close_fnum(cli, fnum);
465 /***************************************************************
466 Small wrapper that allows SMB2 to delete a directory
467 Synchronous only.
468 ***************************************************************/
470 NTSTATUS cli_smb2_rmdir(struct cli_state *cli, const char *dname)
472 NTSTATUS status;
473 uint16_t fnum;
475 if (smbXcli_conn_has_async_calls(cli->conn)) {
477 * Can't use sync call while an async call is in flight
479 return NT_STATUS_INVALID_PARAMETER;
482 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
483 return NT_STATUS_INVALID_PARAMETER;
486 status = cli_smb2_create_fnum(cli,
487 dname,
488 0, /* create_flags */
489 DELETE_ACCESS, /* desired_access */
490 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
491 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
492 FILE_OPEN, /* create_disposition */
493 FILE_DIRECTORY_FILE|FILE_DELETE_ON_CLOSE, /* create_options */
494 &fnum,
495 NULL);
497 if (!NT_STATUS_IS_OK(status)) {
498 return status;
500 return cli_smb2_close_fnum(cli, fnum);
503 /***************************************************************
504 Small wrapper that allows SMB2 to unlink a pathname.
505 Synchronous only.
506 ***************************************************************/
508 NTSTATUS cli_smb2_unlink(struct cli_state *cli, const char *fname)
510 NTSTATUS status;
511 uint16_t fnum;
513 if (smbXcli_conn_has_async_calls(cli->conn)) {
515 * Can't use sync call while an async call is in flight
517 return NT_STATUS_INVALID_PARAMETER;
520 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
521 return NT_STATUS_INVALID_PARAMETER;
524 status = cli_smb2_create_fnum(cli,
525 fname,
526 0, /* create_flags */
527 DELETE_ACCESS, /* desired_access */
528 FILE_ATTRIBUTE_NORMAL, /* file attributes */
529 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
530 FILE_OPEN, /* create_disposition */
531 FILE_DELETE_ON_CLOSE, /* create_options */
532 &fnum,
533 NULL);
535 if (!NT_STATUS_IS_OK(status)) {
536 return status;
538 return cli_smb2_close_fnum(cli, fnum);
541 /***************************************************************
542 Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
543 ***************************************************************/
545 static NTSTATUS parse_finfo_id_both_directory_info(uint8_t *dir_data,
546 uint32_t dir_data_length,
547 struct file_info *finfo,
548 uint32_t *next_offset)
550 size_t namelen = 0;
551 size_t slen = 0;
552 size_t ret = 0;
554 if (dir_data_length < 4) {
555 return NT_STATUS_INFO_LENGTH_MISMATCH;
558 *next_offset = IVAL(dir_data, 0);
560 if (*next_offset > dir_data_length) {
561 return NT_STATUS_INFO_LENGTH_MISMATCH;
564 if (*next_offset != 0) {
565 /* Ensure we only read what in this record. */
566 dir_data_length = *next_offset;
569 if (dir_data_length < 105) {
570 return NT_STATUS_INFO_LENGTH_MISMATCH;
573 finfo->atime_ts = interpret_long_date((const char *)dir_data + 16);
574 finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24);
575 finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32);
576 finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0);
577 finfo->mode = CVAL(dir_data + 56, 0);
578 namelen = IVAL(dir_data + 60,0);
579 if (namelen > (dir_data_length - 104)) {
580 return NT_STATUS_INFO_LENGTH_MISMATCH;
582 slen = CVAL(dir_data + 68, 0);
583 if (slen > 24) {
584 return NT_STATUS_INFO_LENGTH_MISMATCH;
586 ret = pull_string_talloc(finfo,
587 dir_data,
588 FLAGS2_UNICODE_STRINGS,
589 &finfo->short_name,
590 dir_data + 70,
591 slen,
592 STR_UNICODE);
593 if (ret == (size_t)-1) {
594 /* Bad conversion. */
595 return NT_STATUS_INVALID_NETWORK_RESPONSE;
598 ret = pull_string_talloc(finfo,
599 dir_data,
600 FLAGS2_UNICODE_STRINGS,
601 &finfo->name,
602 dir_data + 104,
603 namelen,
604 STR_UNICODE);
605 if (ret == (size_t)-1) {
606 /* Bad conversion. */
607 return NT_STATUS_INVALID_NETWORK_RESPONSE;
609 return NT_STATUS_OK;
612 /*******************************************************************
613 Given a filename - get its directory name
614 ********************************************************************/
616 static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
617 const char *dir,
618 char **parent,
619 const char **name)
621 char *p;
622 ptrdiff_t len;
624 p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
626 if (p == NULL) {
627 if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
628 return false;
630 if (name) {
631 *name = dir;
633 return true;
636 len = p-dir;
638 if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
639 return false;
641 (*parent)[len] = '\0';
643 if (name) {
644 *name = p+1;
646 return true;
649 /***************************************************************
650 Wrapper that allows SMB2 to list a directory.
651 Synchronous only.
652 ***************************************************************/
654 NTSTATUS cli_smb2_list(struct cli_state *cli,
655 const char *pathname,
656 uint16_t attribute,
657 NTSTATUS (*fn)(const char *,
658 struct file_info *,
659 const char *,
660 void *),
661 void *state)
663 NTSTATUS status;
664 uint16_t fnum = 0xffff;
665 char *parent_dir = NULL;
666 const char *mask = NULL;
667 struct smb2_hnd *ph = NULL;
668 bool processed_file = false;
669 TALLOC_CTX *frame = talloc_stackframe();
670 TALLOC_CTX *subframe = NULL;
671 bool mask_has_wild;
673 if (smbXcli_conn_has_async_calls(cli->conn)) {
675 * Can't use sync call while an async call is in flight
677 status = NT_STATUS_INVALID_PARAMETER;
678 goto fail;
681 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
682 status = NT_STATUS_INVALID_PARAMETER;
683 goto fail;
686 /* Get the directory name. */
687 if (!windows_parent_dirname(frame,
688 pathname,
689 &parent_dir,
690 &mask)) {
691 status = NT_STATUS_NO_MEMORY;
692 goto fail;
695 mask_has_wild = ms_has_wild(mask);
697 status = cli_smb2_create_fnum(cli,
698 parent_dir,
699 0, /* create_flags */
700 SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE,/* desired_access */
701 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
702 FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
703 FILE_OPEN, /* create_disposition */
704 FILE_DIRECTORY_FILE, /* create_options */
705 &fnum,
706 NULL);
708 if (!NT_STATUS_IS_OK(status)) {
709 goto fail;
712 status = map_fnum_to_smb2_handle(cli,
713 fnum,
714 &ph);
715 if (!NT_STATUS_IS_OK(status)) {
716 goto fail;
719 do {
720 uint8_t *dir_data = NULL;
721 uint32_t dir_data_length = 0;
722 uint32_t next_offset = 0;
723 subframe = talloc_stackframe();
725 status = smb2cli_query_directory(cli->conn,
726 cli->timeout,
727 cli->smb2.session,
728 cli->smb2.tcon,
729 SMB2_FIND_ID_BOTH_DIRECTORY_INFO,
730 0, /* flags */
731 0, /* file_index */
732 ph->fid_persistent,
733 ph->fid_volatile,
734 mask,
735 0xffff,
736 subframe,
737 &dir_data,
738 &dir_data_length);
740 if (!NT_STATUS_IS_OK(status)) {
741 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
742 break;
744 goto fail;
747 do {
748 struct file_info *finfo = talloc_zero(subframe,
749 struct file_info);
751 if (finfo == NULL) {
752 status = NT_STATUS_NO_MEMORY;
753 goto fail;
756 status = parse_finfo_id_both_directory_info(dir_data,
757 dir_data_length,
758 finfo,
759 &next_offset);
761 if (!NT_STATUS_IS_OK(status)) {
762 goto fail;
765 if (dir_check_ftype((uint32_t)finfo->mode,
766 (uint32_t)attribute)) {
768 * Only process if attributes match.
769 * On SMB1 server does this, so on
770 * SMB2 we need to emulate in the
771 * client.
773 * https://bugzilla.samba.org/show_bug.cgi?id=10260
775 processed_file = true;
777 status = fn(cli->dfs_mountpoint,
778 finfo,
779 pathname,
780 state);
782 if (!NT_STATUS_IS_OK(status)) {
783 break;
787 TALLOC_FREE(finfo);
789 /* Move to next entry. */
790 if (next_offset) {
791 dir_data += next_offset;
792 dir_data_length -= next_offset;
794 } while (next_offset != 0);
796 TALLOC_FREE(subframe);
798 if (!mask_has_wild) {
800 * MacOSX 10 doesn't set STATUS_NO_MORE_FILES
801 * when handed a non-wildcard path. Do it
802 * for the server (with a non-wildcard path
803 * there should only ever be one file returned.
805 status = STATUS_NO_MORE_FILES;
806 break;
809 } while (NT_STATUS_IS_OK(status));
811 if (NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) {
812 status = NT_STATUS_OK;
815 if (NT_STATUS_IS_OK(status) && !processed_file) {
817 * In SMB1 findfirst returns NT_STATUS_NO_SUCH_FILE
818 * if no files match. Emulate this in the client.
820 status = NT_STATUS_NO_SUCH_FILE;
823 fail:
825 if (fnum != 0xffff) {
826 cli_smb2_close_fnum(cli, fnum);
828 TALLOC_FREE(subframe);
829 TALLOC_FREE(frame);
830 return status;
833 /***************************************************************
834 Wrapper that allows SMB2 to query a path info (basic level).
835 Synchronous only.
836 ***************************************************************/
838 NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
839 const char *name,
840 SMB_STRUCT_STAT *sbuf,
841 uint32_t *attributes)
843 NTSTATUS status;
844 struct smb_create_returns cr;
845 uint16_t fnum = 0xffff;
846 size_t namelen = strlen(name);
848 if (smbXcli_conn_has_async_calls(cli->conn)) {
850 * Can't use sync call while an async call is in flight
852 return NT_STATUS_INVALID_PARAMETER;
855 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
856 return NT_STATUS_INVALID_PARAMETER;
859 /* SMB2 is pickier about pathnames. Ensure it doesn't
860 end in a '\' */
861 if (namelen > 0 && name[namelen-1] == '\\') {
862 char *modname = talloc_strdup(talloc_tos(), name);
863 modname[namelen-1] = '\0';
864 name = modname;
867 /* This is commonly used as a 'cd'. Try qpathinfo on
868 a directory handle first. */
870 status = cli_smb2_create_fnum(cli,
871 name,
872 0, /* create_flags */
873 FILE_READ_ATTRIBUTES, /* desired_access */
874 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
875 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
876 FILE_OPEN, /* create_disposition */
877 FILE_DIRECTORY_FILE, /* create_options */
878 &fnum,
879 &cr);
881 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
882 /* Maybe a file ? */
883 status = cli_smb2_create_fnum(cli,
884 name,
885 0, /* create_flags */
886 FILE_READ_ATTRIBUTES, /* desired_access */
887 0, /* file attributes */
888 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
889 FILE_OPEN, /* create_disposition */
890 0, /* create_options */
891 &fnum,
892 &cr);
895 if (!NT_STATUS_IS_OK(status)) {
896 return status;
899 cli_smb2_close_fnum(cli, fnum);
901 ZERO_STRUCTP(sbuf);
903 sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
904 sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
905 sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
906 sbuf->st_ex_size = cr.end_of_file;
907 *attributes = cr.file_attributes;
909 return NT_STATUS_OK;
912 /***************************************************************
913 Helper function for pathname operations.
914 ***************************************************************/
916 static NTSTATUS get_fnum_from_path(struct cli_state *cli,
917 const char *name,
918 uint32_t desired_access,
919 uint16_t *pfnum)
921 NTSTATUS status;
922 size_t namelen = strlen(name);
923 TALLOC_CTX *frame = talloc_stackframe();
925 /* SMB2 is pickier about pathnames. Ensure it doesn't
926 end in a '\' */
927 if (namelen > 0 && name[namelen-1] == '\\') {
928 char *modname = talloc_strdup(frame, name);
929 if (modname == NULL) {
930 status = NT_STATUS_NO_MEMORY;
931 goto fail;
933 modname[namelen-1] = '\0';
934 name = modname;
937 /* Try to open a file handle first. */
938 status = cli_smb2_create_fnum(cli,
939 name,
940 0, /* create_flags */
941 desired_access,
942 0, /* file attributes */
943 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
944 FILE_OPEN, /* create_disposition */
945 0, /* create_options */
946 pfnum,
947 NULL);
949 if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
950 status = cli_smb2_create_fnum(cli,
951 name,
952 0, /* create_flags */
953 desired_access,
954 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
955 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
956 FILE_OPEN, /* create_disposition */
957 FILE_DIRECTORY_FILE, /* create_options */
958 pfnum,
959 NULL);
962 fail:
964 TALLOC_FREE(frame);
965 return status;
968 /***************************************************************
969 Wrapper that allows SMB2 to query a path info (ALTNAME level).
970 Synchronous only.
971 ***************************************************************/
973 NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli,
974 const char *name,
975 fstring alt_name)
977 NTSTATUS status;
978 DATA_BLOB outbuf = data_blob_null;
979 uint16_t fnum = 0xffff;
980 struct smb2_hnd *ph = NULL;
981 uint32_t altnamelen = 0;
982 TALLOC_CTX *frame = talloc_stackframe();
984 if (smbXcli_conn_has_async_calls(cli->conn)) {
986 * Can't use sync call while an async call is in flight
988 status = NT_STATUS_INVALID_PARAMETER;
989 goto fail;
992 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
993 status = NT_STATUS_INVALID_PARAMETER;
994 goto fail;
997 status = get_fnum_from_path(cli,
998 name,
999 FILE_READ_ATTRIBUTES,
1000 &fnum);
1002 if (!NT_STATUS_IS_OK(status)) {
1003 goto fail;
1006 status = map_fnum_to_smb2_handle(cli,
1007 fnum,
1008 &ph);
1009 if (!NT_STATUS_IS_OK(status)) {
1010 goto fail;
1013 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
1014 level SMB_FILE_ALTERNATE_NAME_INFORMATION (1021) == SMB2 21 */
1016 status = smb2cli_query_info(cli->conn,
1017 cli->timeout,
1018 cli->smb2.session,
1019 cli->smb2.tcon,
1020 1, /* in_info_type */
1021 (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */
1022 0xFFFF, /* in_max_output_length */
1023 NULL, /* in_input_buffer */
1024 0, /* in_additional_info */
1025 0, /* in_flags */
1026 ph->fid_persistent,
1027 ph->fid_volatile,
1028 frame,
1029 &outbuf);
1031 if (!NT_STATUS_IS_OK(status)) {
1032 goto fail;
1035 /* Parse the reply. */
1036 if (outbuf.length < 4) {
1037 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1038 goto fail;
1041 altnamelen = IVAL(outbuf.data, 0);
1042 if (altnamelen > outbuf.length - 4) {
1043 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1044 goto fail;
1047 if (altnamelen > 0) {
1048 size_t ret = 0;
1049 char *short_name = NULL;
1050 ret = pull_string_talloc(frame,
1051 outbuf.data,
1052 FLAGS2_UNICODE_STRINGS,
1053 &short_name,
1054 outbuf.data + 4,
1055 altnamelen,
1056 STR_UNICODE);
1057 if (ret == (size_t)-1) {
1058 /* Bad conversion. */
1059 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1060 goto fail;
1063 fstrcpy(alt_name, short_name);
1064 } else {
1065 alt_name[0] = '\0';
1068 status = NT_STATUS_OK;
1070 fail:
1072 if (fnum != 0xffff) {
1073 cli_smb2_close_fnum(cli, fnum);
1075 TALLOC_FREE(frame);
1076 return status;
1080 /***************************************************************
1081 Wrapper that allows SMB2 to query a fnum info (basic level).
1082 Synchronous only.
1083 ***************************************************************/
1085 NTSTATUS cli_smb2_qfileinfo_basic(struct cli_state *cli,
1086 uint16_t fnum,
1087 uint16_t *mode,
1088 off_t *size,
1089 struct timespec *create_time,
1090 struct timespec *access_time,
1091 struct timespec *write_time,
1092 struct timespec *change_time,
1093 SMB_INO_T *ino)
1095 NTSTATUS status;
1096 DATA_BLOB outbuf = data_blob_null;
1097 struct smb2_hnd *ph = NULL;
1098 TALLOC_CTX *frame = talloc_stackframe();
1100 if (smbXcli_conn_has_async_calls(cli->conn)) {
1102 * Can't use sync call while an async call is in flight
1104 status = NT_STATUS_INVALID_PARAMETER;
1105 goto fail;
1108 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1109 status = NT_STATUS_INVALID_PARAMETER;
1110 goto fail;
1113 status = map_fnum_to_smb2_handle(cli,
1114 fnum,
1115 &ph);
1116 if (!NT_STATUS_IS_OK(status)) {
1117 goto fail;
1120 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
1121 level 0x12 (SMB2_FILE_ALL_INFORMATION). */
1123 status = smb2cli_query_info(cli->conn,
1124 cli->timeout,
1125 cli->smb2.session,
1126 cli->smb2.tcon,
1127 1, /* in_info_type */
1128 (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */
1129 0xFFFF, /* in_max_output_length */
1130 NULL, /* in_input_buffer */
1131 0, /* in_additional_info */
1132 0, /* in_flags */
1133 ph->fid_persistent,
1134 ph->fid_volatile,
1135 frame,
1136 &outbuf);
1137 if (!NT_STATUS_IS_OK(status)) {
1138 goto fail;
1141 /* Parse the reply. */
1142 if (outbuf.length < 0x60) {
1143 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1144 goto fail;
1147 if (create_time) {
1148 *create_time = interpret_long_date((const char *)outbuf.data + 0x0);
1150 if (access_time) {
1151 *access_time = interpret_long_date((const char *)outbuf.data + 0x8);
1153 if (write_time) {
1154 *write_time = interpret_long_date((const char *)outbuf.data + 0x10);
1156 if (change_time) {
1157 *change_time = interpret_long_date((const char *)outbuf.data + 0x18);
1159 if (mode) {
1160 uint32_t attr = IVAL(outbuf.data, 0x20);
1161 *mode = (uint16_t)attr;
1163 if (size) {
1164 uint64_t file_size = BVAL(outbuf.data, 0x30);
1165 *size = (off_t)file_size;
1167 if (ino) {
1168 uint64_t file_index = BVAL(outbuf.data, 0x40);
1169 *ino = (SMB_INO_T)file_index;
1172 fail:
1174 TALLOC_FREE(frame);
1175 return status;
1178 /***************************************************************
1179 Wrapper that allows SMB2 to query an fnum.
1180 Implement on top of cli_smb2_qfileinfo_basic().
1181 Synchronous only.
1182 ***************************************************************/
1184 NTSTATUS cli_smb2_getattrE(struct cli_state *cli,
1185 uint16_t fnum,
1186 uint16_t *attr,
1187 off_t *size,
1188 time_t *change_time,
1189 time_t *access_time,
1190 time_t *write_time)
1192 struct timespec access_time_ts;
1193 struct timespec write_time_ts;
1194 struct timespec change_time_ts;
1195 NTSTATUS status = cli_smb2_qfileinfo_basic(cli,
1196 fnum,
1197 attr,
1198 size,
1199 NULL,
1200 &access_time_ts,
1201 &write_time_ts,
1202 &change_time_ts,
1203 NULL);
1205 if (!NT_STATUS_IS_OK(status)) {
1206 return status;
1209 if (change_time) {
1210 *change_time = change_time_ts.tv_sec;
1212 if (access_time) {
1213 *access_time = access_time_ts.tv_sec;
1215 if (write_time) {
1216 *write_time = write_time_ts.tv_sec;
1218 return NT_STATUS_OK;
1221 /***************************************************************
1222 Wrapper that allows SMB2 to get pathname attributes.
1223 Synchronous only.
1224 ***************************************************************/
1226 NTSTATUS cli_smb2_getatr(struct cli_state *cli,
1227 const char *name,
1228 uint16_t *attr,
1229 off_t *size,
1230 time_t *write_time)
1232 NTSTATUS status;
1233 uint16_t fnum = 0xffff;
1234 struct smb2_hnd *ph = NULL;
1235 TALLOC_CTX *frame = talloc_stackframe();
1237 if (smbXcli_conn_has_async_calls(cli->conn)) {
1239 * Can't use sync call while an async call is in flight
1241 status = NT_STATUS_INVALID_PARAMETER;
1242 goto fail;
1245 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1246 status = NT_STATUS_INVALID_PARAMETER;
1247 goto fail;
1250 status = get_fnum_from_path(cli,
1251 name,
1252 FILE_READ_ATTRIBUTES,
1253 &fnum);
1255 if (!NT_STATUS_IS_OK(status)) {
1256 goto fail;
1259 status = map_fnum_to_smb2_handle(cli,
1260 fnum,
1261 &ph);
1262 if (!NT_STATUS_IS_OK(status)) {
1263 goto fail;
1265 status = cli_smb2_getattrE(cli,
1266 fnum,
1267 attr,
1268 size,
1269 NULL,
1270 NULL,
1271 write_time);
1272 if (!NT_STATUS_IS_OK(status)) {
1273 goto fail;
1276 fail:
1278 if (fnum != 0xffff) {
1279 cli_smb2_close_fnum(cli, fnum);
1282 TALLOC_FREE(frame);
1283 return status;
1286 /***************************************************************
1287 Wrapper that allows SMB2 to query a pathname info (basic level).
1288 Implement on top of cli_smb2_qfileinfo_basic().
1289 Synchronous only.
1290 ***************************************************************/
1292 NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli,
1293 const char *name,
1294 struct timespec *create_time,
1295 struct timespec *access_time,
1296 struct timespec *write_time,
1297 struct timespec *change_time,
1298 off_t *size,
1299 uint16_t *mode,
1300 SMB_INO_T *ino)
1302 NTSTATUS status;
1303 struct smb2_hnd *ph = NULL;
1304 uint16_t fnum = 0xffff;
1305 TALLOC_CTX *frame = talloc_stackframe();
1307 if (smbXcli_conn_has_async_calls(cli->conn)) {
1309 * Can't use sync call while an async call is in flight
1311 status = NT_STATUS_INVALID_PARAMETER;
1312 goto fail;
1315 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1316 status = NT_STATUS_INVALID_PARAMETER;
1317 goto fail;
1320 status = get_fnum_from_path(cli,
1321 name,
1322 FILE_READ_ATTRIBUTES,
1323 &fnum);
1325 if (!NT_STATUS_IS_OK(status)) {
1326 goto fail;
1329 status = map_fnum_to_smb2_handle(cli,
1330 fnum,
1331 &ph);
1332 if (!NT_STATUS_IS_OK(status)) {
1333 goto fail;
1336 status = cli_smb2_qfileinfo_basic(cli,
1337 fnum,
1338 mode,
1339 size,
1340 create_time,
1341 access_time,
1342 write_time,
1343 change_time,
1344 ino);
1346 fail:
1348 if (fnum != 0xffff) {
1349 cli_smb2_close_fnum(cli, fnum);
1352 TALLOC_FREE(frame);
1353 return status;
1356 /***************************************************************
1357 Wrapper that allows SMB2 to query pathname streams.
1358 Synchronous only.
1359 ***************************************************************/
1361 NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli,
1362 const char *name,
1363 TALLOC_CTX *mem_ctx,
1364 unsigned int *pnum_streams,
1365 struct stream_struct **pstreams)
1367 NTSTATUS status;
1368 struct smb2_hnd *ph = NULL;
1369 uint16_t fnum = 0xffff;
1370 DATA_BLOB outbuf = data_blob_null;
1371 TALLOC_CTX *frame = talloc_stackframe();
1373 if (smbXcli_conn_has_async_calls(cli->conn)) {
1375 * Can't use sync call while an async call is in flight
1377 status = NT_STATUS_INVALID_PARAMETER;
1378 goto fail;
1381 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1382 status = NT_STATUS_INVALID_PARAMETER;
1383 goto fail;
1386 status = get_fnum_from_path(cli,
1387 name,
1388 FILE_READ_ATTRIBUTES,
1389 &fnum);
1391 if (!NT_STATUS_IS_OK(status)) {
1392 goto fail;
1395 status = map_fnum_to_smb2_handle(cli,
1396 fnum,
1397 &ph);
1398 if (!NT_STATUS_IS_OK(status)) {
1399 goto fail;
1402 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
1403 level 22 (SMB2_FILE_STREAM_INFORMATION). */
1405 status = smb2cli_query_info(cli->conn,
1406 cli->timeout,
1407 cli->smb2.session,
1408 cli->smb2.tcon,
1409 1, /* in_info_type */
1410 (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */
1411 0xFFFF, /* in_max_output_length */
1412 NULL, /* in_input_buffer */
1413 0, /* in_additional_info */
1414 0, /* in_flags */
1415 ph->fid_persistent,
1416 ph->fid_volatile,
1417 frame,
1418 &outbuf);
1420 if (!NT_STATUS_IS_OK(status)) {
1421 goto fail;
1424 /* Parse the reply. */
1425 if (!parse_streams_blob(mem_ctx,
1426 outbuf.data,
1427 outbuf.length,
1428 pnum_streams,
1429 pstreams)) {
1430 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1431 goto fail;
1434 fail:
1436 if (fnum != 0xffff) {
1437 cli_smb2_close_fnum(cli, fnum);
1440 TALLOC_FREE(frame);
1441 return status;
1444 /***************************************************************
1445 Wrapper that allows SMB2 to set pathname attributes.
1446 Synchronous only.
1447 ***************************************************************/
1449 NTSTATUS cli_smb2_setatr(struct cli_state *cli,
1450 const char *name,
1451 uint16_t attr,
1452 time_t mtime)
1454 NTSTATUS status;
1455 uint16_t fnum = 0xffff;
1456 struct smb2_hnd *ph = NULL;
1457 uint8_t inbuf_store[40];
1458 DATA_BLOB inbuf = data_blob_null;
1459 TALLOC_CTX *frame = talloc_stackframe();
1461 if (smbXcli_conn_has_async_calls(cli->conn)) {
1463 * Can't use sync call while an async call is in flight
1465 status = NT_STATUS_INVALID_PARAMETER;
1466 goto fail;
1469 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1470 status = NT_STATUS_INVALID_PARAMETER;
1471 goto fail;
1474 status = get_fnum_from_path(cli,
1475 name,
1476 FILE_WRITE_ATTRIBUTES,
1477 &fnum);
1479 if (!NT_STATUS_IS_OK(status)) {
1480 goto fail;
1483 status = map_fnum_to_smb2_handle(cli,
1484 fnum,
1485 &ph);
1486 if (!NT_STATUS_IS_OK(status)) {
1487 goto fail;
1490 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
1491 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
1493 inbuf.data = inbuf_store;
1494 inbuf.length = sizeof(inbuf_store);
1495 data_blob_clear(&inbuf);
1497 SSVAL(inbuf.data, 32, attr);
1498 if (mtime != 0) {
1499 put_long_date((char *)inbuf.data + 16,mtime);
1501 /* Set all the other times to -1. */
1502 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
1503 SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
1504 SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
1506 status = smb2cli_set_info(cli->conn,
1507 cli->timeout,
1508 cli->smb2.session,
1509 cli->smb2.tcon,
1510 1, /* in_info_type */
1511 SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
1512 &inbuf, /* in_input_buffer */
1513 0, /* in_additional_info */
1514 ph->fid_persistent,
1515 ph->fid_volatile);
1516 fail:
1518 if (fnum != 0xffff) {
1519 cli_smb2_close_fnum(cli, fnum);
1522 TALLOC_FREE(frame);
1523 return status;
1526 /***************************************************************
1527 Wrapper that allows SMB2 to set file handle times.
1528 Synchronous only.
1529 ***************************************************************/
1531 NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
1532 uint16_t fnum,
1533 time_t change_time,
1534 time_t access_time,
1535 time_t write_time)
1537 NTSTATUS status;
1538 struct smb2_hnd *ph = NULL;
1539 uint8_t inbuf_store[40];
1540 DATA_BLOB inbuf = data_blob_null;
1542 if (smbXcli_conn_has_async_calls(cli->conn)) {
1544 * Can't use sync call while an async call is in flight
1546 return NT_STATUS_INVALID_PARAMETER;
1549 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1550 return NT_STATUS_INVALID_PARAMETER;
1553 status = map_fnum_to_smb2_handle(cli,
1554 fnum,
1555 &ph);
1556 if (!NT_STATUS_IS_OK(status)) {
1557 return status;
1560 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
1561 level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
1563 inbuf.data = inbuf_store;
1564 inbuf.length = sizeof(inbuf_store);
1565 data_blob_clear(&inbuf);
1567 SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
1568 if (change_time != 0) {
1569 put_long_date((char *)inbuf.data + 24, change_time);
1571 if (access_time != 0) {
1572 put_long_date((char *)inbuf.data + 8, access_time);
1574 if (write_time != 0) {
1575 put_long_date((char *)inbuf.data + 16, write_time);
1578 return smb2cli_set_info(cli->conn,
1579 cli->timeout,
1580 cli->smb2.session,
1581 cli->smb2.tcon,
1582 1, /* in_info_type */
1583 SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */
1584 &inbuf, /* in_input_buffer */
1585 0, /* in_additional_info */
1586 ph->fid_persistent,
1587 ph->fid_volatile);
1590 /***************************************************************
1591 Wrapper that allows SMB2 to query disk attributes (size).
1592 Synchronous only.
1593 ***************************************************************/
1595 NTSTATUS cli_smb2_dskattr(struct cli_state *cli, uint64_t *bsize, uint64_t *total, uint64_t *avail)
1597 NTSTATUS status;
1598 uint16_t fnum = 0xffff;
1599 DATA_BLOB outbuf = data_blob_null;
1600 struct smb2_hnd *ph = NULL;
1601 uint32_t sectors_per_unit = 0;
1602 uint32_t bytes_per_sector = 0;
1603 uint64_t total_size = 0;
1604 uint64_t size_free = 0;
1605 TALLOC_CTX *frame = talloc_stackframe();
1607 if (smbXcli_conn_has_async_calls(cli->conn)) {
1609 * Can't use sync call while an async call is in flight
1611 status = NT_STATUS_INVALID_PARAMETER;
1612 goto fail;
1615 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1616 status = NT_STATUS_INVALID_PARAMETER;
1617 goto fail;
1620 /* First open the top level directory. */
1621 status = cli_smb2_create_fnum(cli,
1623 0, /* create_flags */
1624 FILE_READ_ATTRIBUTES, /* desired_access */
1625 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1626 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1627 FILE_OPEN, /* create_disposition */
1628 FILE_DIRECTORY_FILE, /* create_options */
1629 &fnum,
1630 NULL);
1632 if (!NT_STATUS_IS_OK(status)) {
1633 goto fail;
1636 status = map_fnum_to_smb2_handle(cli,
1637 fnum,
1638 &ph);
1639 if (!NT_STATUS_IS_OK(status)) {
1640 goto fail;
1643 /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
1644 level 3 (SMB_FS_SIZE_INFORMATION). */
1646 status = smb2cli_query_info(cli->conn,
1647 cli->timeout,
1648 cli->smb2.session,
1649 cli->smb2.tcon,
1650 2, /* in_info_type */
1651 3, /* in_file_info_class */
1652 0xFFFF, /* in_max_output_length */
1653 NULL, /* in_input_buffer */
1654 0, /* in_additional_info */
1655 0, /* in_flags */
1656 ph->fid_persistent,
1657 ph->fid_volatile,
1658 frame,
1659 &outbuf);
1660 if (!NT_STATUS_IS_OK(status)) {
1661 goto fail;
1664 /* Parse the reply. */
1665 if (outbuf.length != 24) {
1666 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
1667 goto fail;
1670 total_size = BVAL(outbuf.data, 0);
1671 size_free = BVAL(outbuf.data, 8);
1672 sectors_per_unit = IVAL(outbuf.data, 16);
1673 bytes_per_sector = IVAL(outbuf.data, 20);
1675 if (bsize) {
1676 *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
1678 if (total) {
1679 *total = total_size;
1681 if (avail) {
1682 *avail = size_free;
1685 status = NT_STATUS_OK;
1687 fail:
1689 if (fnum != 0xffff) {
1690 cli_smb2_close_fnum(cli, fnum);
1693 TALLOC_FREE(frame);
1694 return status;
1697 /***************************************************************
1698 Wrapper that allows SMB2 to query a security descriptor.
1699 Synchronous only.
1700 ***************************************************************/
1702 NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
1703 uint16_t fnum,
1704 uint32_t sec_info,
1705 TALLOC_CTX *mem_ctx,
1706 struct security_descriptor **ppsd)
1708 NTSTATUS status;
1709 DATA_BLOB outbuf = data_blob_null;
1710 struct smb2_hnd *ph = NULL;
1711 struct security_descriptor *lsd = NULL;
1712 TALLOC_CTX *frame = talloc_stackframe();
1714 if (smbXcli_conn_has_async_calls(cli->conn)) {
1716 * Can't use sync call while an async call is in flight
1718 status = NT_STATUS_INVALID_PARAMETER;
1719 goto fail;
1722 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1723 status = NT_STATUS_INVALID_PARAMETER;
1724 goto fail;
1727 status = map_fnum_to_smb2_handle(cli,
1728 fnum,
1729 &ph);
1730 if (!NT_STATUS_IS_OK(status)) {
1731 goto fail;
1734 /* getinfo on the returned handle with info_type SMB2_GETINFO_SEC (3) */
1736 status = smb2cli_query_info(cli->conn,
1737 cli->timeout,
1738 cli->smb2.session,
1739 cli->smb2.tcon,
1740 3, /* in_info_type */
1741 0, /* in_file_info_class */
1742 0xFFFF, /* in_max_output_length */
1743 NULL, /* in_input_buffer */
1744 sec_info, /* in_additional_info */
1745 0, /* in_flags */
1746 ph->fid_persistent,
1747 ph->fid_volatile,
1748 frame,
1749 &outbuf);
1751 if (!NT_STATUS_IS_OK(status)) {
1752 goto fail;
1755 /* Parse the reply. */
1756 status = unmarshall_sec_desc(mem_ctx,
1757 outbuf.data,
1758 outbuf.length,
1759 &lsd);
1761 if (!NT_STATUS_IS_OK(status)) {
1762 goto fail;
1765 if (ppsd != NULL) {
1766 *ppsd = lsd;
1767 } else {
1768 TALLOC_FREE(lsd);
1771 fail:
1773 TALLOC_FREE(frame);
1774 return status;
1777 /***************************************************************
1778 Wrapper that allows SMB2 to set a security descriptor.
1779 Synchronous only.
1780 ***************************************************************/
1782 NTSTATUS cli_smb2_set_security_descriptor(struct cli_state *cli,
1783 uint16_t fnum,
1784 uint32_t sec_info,
1785 const struct security_descriptor *sd)
1787 NTSTATUS status;
1788 DATA_BLOB inbuf = data_blob_null;
1789 struct smb2_hnd *ph = NULL;
1790 TALLOC_CTX *frame = talloc_stackframe();
1792 if (smbXcli_conn_has_async_calls(cli->conn)) {
1794 * Can't use sync call while an async call is in flight
1796 status = NT_STATUS_INVALID_PARAMETER;
1797 goto fail;
1800 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1801 status = NT_STATUS_INVALID_PARAMETER;
1802 goto fail;
1805 status = map_fnum_to_smb2_handle(cli,
1806 fnum,
1807 &ph);
1808 if (!NT_STATUS_IS_OK(status)) {
1809 goto fail;
1812 status = marshall_sec_desc(frame,
1814 &inbuf.data,
1815 &inbuf.length);
1817 if (!NT_STATUS_IS_OK(status)) {
1818 goto fail;
1821 /* setinfo on the returned handle with info_type SMB2_SETINFO_SEC (3) */
1823 status = smb2cli_set_info(cli->conn,
1824 cli->timeout,
1825 cli->smb2.session,
1826 cli->smb2.tcon,
1827 3, /* in_info_type */
1828 0, /* in_file_info_class */
1829 &inbuf, /* in_input_buffer */
1830 sec_info, /* in_additional_info */
1831 ph->fid_persistent,
1832 ph->fid_volatile);
1834 fail:
1836 TALLOC_FREE(frame);
1837 return status;
1840 /***************************************************************
1841 Wrapper that allows SMB2 to rename a file.
1842 Synchronous only.
1843 ***************************************************************/
1845 NTSTATUS cli_smb2_rename(struct cli_state *cli,
1846 const char *fname_src,
1847 const char *fname_dst)
1849 NTSTATUS status;
1850 DATA_BLOB inbuf = data_blob_null;
1851 uint16_t fnum = 0xffff;
1852 struct smb2_hnd *ph = NULL;
1853 smb_ucs2_t *converted_str = NULL;
1854 size_t converted_size_bytes = 0;
1855 size_t namelen = 0;
1856 TALLOC_CTX *frame = talloc_stackframe();
1858 if (smbXcli_conn_has_async_calls(cli->conn)) {
1860 * Can't use sync call while an async call is in flight
1862 status = NT_STATUS_INVALID_PARAMETER;
1863 goto fail;
1866 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1867 status = NT_STATUS_INVALID_PARAMETER;
1868 goto fail;
1871 status = get_fnum_from_path(cli,
1872 fname_src,
1873 DELETE_ACCESS,
1874 &fnum);
1876 if (!NT_STATUS_IS_OK(status)) {
1877 goto fail;
1880 status = map_fnum_to_smb2_handle(cli,
1881 fnum,
1882 &ph);
1883 if (!NT_STATUS_IS_OK(status)) {
1884 goto fail;
1887 /* SMB2 is pickier about pathnames. Ensure it doesn't
1888 start in a '\' */
1889 if (*fname_dst == '\\') {
1890 fname_dst++;
1893 /* SMB2 is pickier about pathnames. Ensure it doesn't
1894 end in a '\' */
1895 namelen = strlen(fname_dst);
1896 if (namelen > 0 && fname_dst[namelen-1] == '\\') {
1897 char *modname = talloc_strdup(frame, fname_dst);
1898 modname[namelen-1] = '\0';
1899 fname_dst = modname;
1902 if (!push_ucs2_talloc(frame,
1903 &converted_str,
1904 fname_dst,
1905 &converted_size_bytes)) {
1906 status = NT_STATUS_INVALID_PARAMETER;
1907 goto fail;
1910 /* W2K8 insists the dest name is not null
1911 terminated. Remove the last 2 zero bytes
1912 and reduce the name length. */
1914 if (converted_size_bytes < 2) {
1915 status = NT_STATUS_INVALID_PARAMETER;
1916 goto fail;
1918 converted_size_bytes -= 2;
1920 inbuf = data_blob_talloc_zero(frame,
1921 20 + converted_size_bytes);
1922 if (inbuf.data == NULL) {
1923 status = NT_STATUS_NO_MEMORY;
1924 goto fail;
1927 SIVAL(inbuf.data, 16, converted_size_bytes);
1928 memcpy(inbuf.data + 20, converted_str, converted_size_bytes);
1930 /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
1931 level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
1933 status = smb2cli_set_info(cli->conn,
1934 cli->timeout,
1935 cli->smb2.session,
1936 cli->smb2.tcon,
1937 1, /* in_info_type */
1938 SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */
1939 &inbuf, /* in_input_buffer */
1940 0, /* in_additional_info */
1941 ph->fid_persistent,
1942 ph->fid_volatile);
1944 fail:
1946 if (fnum != 0xffff) {
1947 cli_smb2_close_fnum(cli, fnum);
1950 TALLOC_FREE(frame);
1951 return status;
1954 /***************************************************************
1955 Wrapper that allows SMB2 to set an EA on a fnum.
1956 Synchronous only.
1957 ***************************************************************/
1959 NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
1960 uint16_t fnum,
1961 const char *ea_name,
1962 const char *ea_val,
1963 size_t ea_len)
1965 NTSTATUS status;
1966 DATA_BLOB inbuf = data_blob_null;
1967 size_t bloblen = 0;
1968 char *ea_name_ascii = NULL;
1969 size_t namelen = 0;
1970 struct smb2_hnd *ph = NULL;
1971 TALLOC_CTX *frame = talloc_stackframe();
1973 if (smbXcli_conn_has_async_calls(cli->conn)) {
1975 * Can't use sync call while an async call is in flight
1977 status = NT_STATUS_INVALID_PARAMETER;
1978 goto fail;
1981 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
1982 status = NT_STATUS_INVALID_PARAMETER;
1983 goto fail;
1986 status = map_fnum_to_smb2_handle(cli,
1987 fnum,
1988 &ph);
1989 if (!NT_STATUS_IS_OK(status)) {
1990 goto fail;
1993 /* Marshall the SMB2 EA data. */
1994 if (ea_len > 0xFFFF) {
1995 status = NT_STATUS_INVALID_PARAMETER;
1996 goto fail;
1999 if (!push_ascii_talloc(frame,
2000 &ea_name_ascii,
2001 ea_name,
2002 &namelen)) {
2003 status = NT_STATUS_INVALID_PARAMETER;
2004 goto fail;
2007 if (namelen < 2 || namelen > 0xFF) {
2008 status = NT_STATUS_INVALID_PARAMETER;
2009 goto fail;
2012 bloblen = 8 + ea_len + namelen;
2013 /* Round up to a 4 byte boundary. */
2014 bloblen = ((bloblen + 3)&~3);
2016 inbuf = data_blob_talloc_zero(frame, bloblen);
2017 if (inbuf.data == NULL) {
2018 status = NT_STATUS_NO_MEMORY;
2019 goto fail;
2021 /* namelen doesn't include the NULL byte. */
2022 SCVAL(inbuf.data, 5, namelen - 1);
2023 SSVAL(inbuf.data, 6, ea_len);
2024 memcpy(inbuf.data + 8, ea_name_ascii, namelen);
2025 memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
2027 /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2028 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
2030 status = smb2cli_set_info(cli->conn,
2031 cli->timeout,
2032 cli->smb2.session,
2033 cli->smb2.tcon,
2034 1, /* in_info_type */
2035 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
2036 &inbuf, /* in_input_buffer */
2037 0, /* in_additional_info */
2038 ph->fid_persistent,
2039 ph->fid_volatile);
2041 fail:
2043 TALLOC_FREE(frame);
2044 return status;
2047 /***************************************************************
2048 Wrapper that allows SMB2 to set an EA on a pathname.
2049 Synchronous only.
2050 ***************************************************************/
2052 NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
2053 const char *name,
2054 const char *ea_name,
2055 const char *ea_val,
2056 size_t ea_len)
2058 NTSTATUS status;
2059 uint16_t fnum = 0xffff;
2061 if (smbXcli_conn_has_async_calls(cli->conn)) {
2063 * Can't use sync call while an async call is in flight
2065 status = NT_STATUS_INVALID_PARAMETER;
2066 goto fail;
2069 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
2070 status = NT_STATUS_INVALID_PARAMETER;
2071 goto fail;
2074 status = get_fnum_from_path(cli,
2075 name,
2076 FILE_WRITE_EA,
2077 &fnum);
2079 if (!NT_STATUS_IS_OK(status)) {
2080 goto fail;
2083 status = cli_set_ea_fnum(cli,
2084 fnum,
2085 ea_name,
2086 ea_val,
2087 ea_len);
2088 if (!NT_STATUS_IS_OK(status)) {
2089 goto fail;
2092 fail:
2094 if (fnum != 0xffff) {
2095 cli_smb2_close_fnum(cli, fnum);
2098 return status;
2101 /***************************************************************
2102 Wrapper that allows SMB2 to get an EA list on a pathname.
2103 Synchronous only.
2104 ***************************************************************/
2106 NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
2107 const char *name,
2108 TALLOC_CTX *ctx,
2109 size_t *pnum_eas,
2110 struct ea_struct **pea_array)
2112 NTSTATUS status;
2113 uint16_t fnum = 0xffff;
2114 DATA_BLOB outbuf = data_blob_null;
2115 struct smb2_hnd *ph = NULL;
2116 struct ea_list *ea_list = NULL;
2117 struct ea_list *eal = NULL;
2118 size_t ea_count = 0;
2119 TALLOC_CTX *frame = talloc_stackframe();
2121 *pnum_eas = 0;
2122 *pea_array = NULL;
2124 if (smbXcli_conn_has_async_calls(cli->conn)) {
2126 * Can't use sync call while an async call is in flight
2128 status = NT_STATUS_INVALID_PARAMETER;
2129 goto fail;
2132 if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
2133 status = NT_STATUS_INVALID_PARAMETER;
2134 goto fail;
2137 status = get_fnum_from_path(cli,
2138 name,
2139 FILE_READ_EA,
2140 &fnum);
2142 if (!NT_STATUS_IS_OK(status)) {
2143 goto fail;
2146 status = map_fnum_to_smb2_handle(cli,
2147 fnum,
2148 &ph);
2149 if (!NT_STATUS_IS_OK(status)) {
2150 goto fail;
2153 /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
2154 level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
2156 status = smb2cli_query_info(cli->conn,
2157 cli->timeout,
2158 cli->smb2.session,
2159 cli->smb2.tcon,
2160 1, /* in_info_type */
2161 SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */
2162 0xFFFF, /* in_max_output_length */
2163 NULL, /* in_input_buffer */
2164 0, /* in_additional_info */
2165 0, /* in_flags */
2166 ph->fid_persistent,
2167 ph->fid_volatile,
2168 frame,
2169 &outbuf);
2171 if (!NT_STATUS_IS_OK(status)) {
2172 goto fail;
2175 /* Parse the reply. */
2176 ea_list = read_nttrans_ea_list(ctx,
2177 (const char *)outbuf.data,
2178 outbuf.length);
2179 if (ea_list == NULL) {
2180 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2181 goto fail;
2184 /* Convert to an array. */
2185 for (eal = ea_list; eal; eal = eal->next) {
2186 ea_count++;
2189 if (ea_count) {
2190 *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
2191 if (*pea_array == NULL) {
2192 status = NT_STATUS_NO_MEMORY;
2193 goto fail;
2195 ea_count = 0;
2196 for (eal = ea_list; eal; eal = eal->next) {
2197 (*pea_array)[ea_count++] = eal->ea;
2199 *pnum_eas = ea_count;
2202 fail:
2204 if (fnum != 0xffff) {
2205 cli_smb2_close_fnum(cli, fnum);
2208 TALLOC_FREE(frame);
2209 return status;
2212 struct cli_smb2_read_state {
2213 struct tevent_context *ev;
2214 struct cli_state *cli;
2215 struct smb2_hnd *ph;
2216 uint64_t start_offset;
2217 uint32_t size;
2218 uint32_t received;
2219 uint8_t *buf;
2222 static void cli_smb2_read_done(struct tevent_req *subreq);
2224 struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
2225 struct tevent_context *ev,
2226 struct cli_state *cli,
2227 uint16_t fnum,
2228 off_t offset,
2229 size_t size)
2231 NTSTATUS status;
2232 struct tevent_req *req, *subreq;
2233 struct cli_smb2_read_state *state;
2235 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
2236 if (req == NULL) {
2237 return NULL;
2239 state->ev = ev;
2240 state->cli = cli;
2241 state->start_offset = (uint64_t)offset;
2242 state->size = (uint32_t)size;
2243 state->received = 0;
2244 state->buf = NULL;
2246 status = map_fnum_to_smb2_handle(cli,
2247 fnum,
2248 &state->ph);
2249 if (tevent_req_nterror(req, status)) {
2250 return tevent_req_post(req, ev);
2253 subreq = smb2cli_read_send(state,
2254 state->ev,
2255 state->cli->conn,
2256 state->cli->timeout,
2257 state->cli->smb2.session,
2258 state->cli->smb2.tcon,
2259 state->size,
2260 state->start_offset,
2261 state->ph->fid_persistent,
2262 state->ph->fid_volatile,
2263 0, /* minimum_count */
2264 0); /* remaining_bytes */
2266 if (tevent_req_nomem(subreq, req)) {
2267 return tevent_req_post(req, ev);
2269 tevent_req_set_callback(subreq, cli_smb2_read_done, req);
2270 return req;
2273 static void cli_smb2_read_done(struct tevent_req *subreq)
2275 struct tevent_req *req = tevent_req_callback_data(
2276 subreq, struct tevent_req);
2277 struct cli_smb2_read_state *state = tevent_req_data(
2278 req, struct cli_smb2_read_state);
2279 NTSTATUS status;
2281 status = smb2cli_read_recv(subreq, state,
2282 &state->buf, &state->received);
2283 if (tevent_req_nterror(req, status)) {
2284 return;
2287 if (state->received > state->size) {
2288 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
2289 return;
2292 tevent_req_done(req);
2295 NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
2296 ssize_t *received,
2297 uint8_t **rcvbuf)
2299 NTSTATUS status;
2300 struct cli_smb2_read_state *state = tevent_req_data(
2301 req, struct cli_smb2_read_state);
2303 if (tevent_req_is_nterror(req, &status)) {
2304 return status;
2307 * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
2308 * better make sure that you copy it away before you talloc_free(req).
2309 * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
2311 *received = (ssize_t)state->received;
2312 *rcvbuf = state->buf;
2313 return NT_STATUS_OK;
2316 struct cli_smb2_write_state {
2317 struct tevent_context *ev;
2318 struct cli_state *cli;
2319 struct smb2_hnd *ph;
2320 uint32_t flags;
2321 const uint8_t *buf;
2322 uint64_t offset;
2323 uint32_t size;
2324 uint32_t written;
2327 static void cli_smb2_write_written(struct tevent_req *req);
2329 struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
2330 struct tevent_context *ev,
2331 struct cli_state *cli,
2332 uint16_t fnum,
2333 uint16_t mode,
2334 const uint8_t *buf,
2335 off_t offset,
2336 size_t size)
2338 NTSTATUS status;
2339 struct tevent_req *req, *subreq = NULL;
2340 struct cli_smb2_write_state *state = NULL;
2342 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
2343 if (req == NULL) {
2344 return NULL;
2346 state->ev = ev;
2347 state->cli = cli;
2348 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
2349 state->flags = (uint32_t)mode;
2350 state->buf = buf;
2351 state->offset = (uint64_t)offset;
2352 state->size = (uint32_t)size;
2353 state->written = 0;
2355 status = map_fnum_to_smb2_handle(cli,
2356 fnum,
2357 &state->ph);
2358 if (tevent_req_nterror(req, status)) {
2359 return tevent_req_post(req, ev);
2362 subreq = smb2cli_write_send(state,
2363 state->ev,
2364 state->cli->conn,
2365 state->cli->timeout,
2366 state->cli->smb2.session,
2367 state->cli->smb2.tcon,
2368 state->size,
2369 state->offset,
2370 state->ph->fid_persistent,
2371 state->ph->fid_volatile,
2372 0, /* remaining_bytes */
2373 state->flags, /* flags */
2374 state->buf);
2376 if (tevent_req_nomem(subreq, req)) {
2377 return tevent_req_post(req, ev);
2379 tevent_req_set_callback(subreq, cli_smb2_write_written, req);
2380 return req;
2383 static void cli_smb2_write_written(struct tevent_req *subreq)
2385 struct tevent_req *req = tevent_req_callback_data(
2386 subreq, struct tevent_req);
2387 struct cli_smb2_write_state *state = tevent_req_data(
2388 req, struct cli_smb2_write_state);
2389 NTSTATUS status;
2390 uint32_t written;
2392 status = smb2cli_write_recv(subreq, &written);
2393 TALLOC_FREE(subreq);
2394 if (tevent_req_nterror(req, status)) {
2395 return;
2398 state->written = written;
2400 tevent_req_done(req);
2403 NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
2404 size_t *pwritten)
2406 struct cli_smb2_write_state *state = tevent_req_data(
2407 req, struct cli_smb2_write_state);
2408 NTSTATUS status;
2410 if (tevent_req_is_nterror(req, &status)) {
2411 tevent_req_received(req);
2412 return status;
2415 if (pwritten != NULL) {
2416 *pwritten = (size_t)state->written;
2418 tevent_req_received(req);
2419 return NT_STATUS_OK;
2422 /***************************************************************
2423 Wrapper that allows SMB2 async write using an fnum.
2424 This is mostly cut-and-paste from Volker's code inside
2425 source3/libsmb/clireadwrite.c, adapted for SMB2.
2427 Done this way so I can reuse all the logic inside cli_push()
2428 for free :-).
2429 ***************************************************************/
2431 struct cli_smb2_writeall_state {
2432 struct tevent_context *ev;
2433 struct cli_state *cli;
2434 struct smb2_hnd *ph;
2435 uint32_t flags;
2436 const uint8_t *buf;
2437 uint64_t offset;
2438 uint32_t size;
2439 uint32_t written;
2442 static void cli_smb2_writeall_written(struct tevent_req *req);
2444 struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
2445 struct tevent_context *ev,
2446 struct cli_state *cli,
2447 uint16_t fnum,
2448 uint16_t mode,
2449 const uint8_t *buf,
2450 off_t offset,
2451 size_t size)
2453 NTSTATUS status;
2454 struct tevent_req *req, *subreq = NULL;
2455 struct cli_smb2_writeall_state *state = NULL;
2456 uint32_t to_write;
2457 uint32_t max_size;
2458 bool ok;
2460 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
2461 if (req == NULL) {
2462 return NULL;
2464 state->ev = ev;
2465 state->cli = cli;
2466 /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
2467 state->flags = (uint32_t)mode;
2468 state->buf = buf;
2469 state->offset = (uint64_t)offset;
2470 state->size = (uint32_t)size;
2471 state->written = 0;
2473 status = map_fnum_to_smb2_handle(cli,
2474 fnum,
2475 &state->ph);
2476 if (tevent_req_nterror(req, status)) {
2477 return tevent_req_post(req, ev);
2480 to_write = state->size;
2481 max_size = smb2cli_conn_max_write_size(state->cli->conn);
2482 to_write = MIN(max_size, to_write);
2483 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
2484 if (ok) {
2485 to_write = MIN(max_size, to_write);
2488 subreq = smb2cli_write_send(state,
2489 state->ev,
2490 state->cli->conn,
2491 state->cli->timeout,
2492 state->cli->smb2.session,
2493 state->cli->smb2.tcon,
2494 to_write,
2495 state->offset,
2496 state->ph->fid_persistent,
2497 state->ph->fid_volatile,
2498 0, /* remaining_bytes */
2499 state->flags, /* flags */
2500 state->buf + state->written);
2502 if (tevent_req_nomem(subreq, req)) {
2503 return tevent_req_post(req, ev);
2505 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
2506 return req;
2509 static void cli_smb2_writeall_written(struct tevent_req *subreq)
2511 struct tevent_req *req = tevent_req_callback_data(
2512 subreq, struct tevent_req);
2513 struct cli_smb2_writeall_state *state = tevent_req_data(
2514 req, struct cli_smb2_writeall_state);
2515 NTSTATUS status;
2516 uint32_t written, to_write;
2517 uint32_t max_size;
2518 bool ok;
2520 status = smb2cli_write_recv(subreq, &written);
2521 TALLOC_FREE(subreq);
2522 if (tevent_req_nterror(req, status)) {
2523 return;
2526 state->written += written;
2528 if (state->written > state->size) {
2529 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
2530 return;
2533 to_write = state->size - state->written;
2535 if (to_write == 0) {
2536 tevent_req_done(req);
2537 return;
2540 max_size = smb2cli_conn_max_write_size(state->cli->conn);
2541 to_write = MIN(max_size, to_write);
2542 ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
2543 if (ok) {
2544 to_write = MIN(max_size, to_write);
2547 subreq = smb2cli_write_send(state,
2548 state->ev,
2549 state->cli->conn,
2550 state->cli->timeout,
2551 state->cli->smb2.session,
2552 state->cli->smb2.tcon,
2553 to_write,
2554 state->offset + state->written,
2555 state->ph->fid_persistent,
2556 state->ph->fid_volatile,
2557 0, /* remaining_bytes */
2558 state->flags, /* flags */
2559 state->buf + state->written);
2561 if (tevent_req_nomem(subreq, req)) {
2562 return;
2564 tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
2567 NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
2568 size_t *pwritten)
2570 struct cli_smb2_writeall_state *state = tevent_req_data(
2571 req, struct cli_smb2_writeall_state);
2572 NTSTATUS status;
2574 if (tevent_req_is_nterror(req, &status)) {
2575 return status;
2577 if (pwritten != NULL) {
2578 *pwritten = (size_t)state->written;
2580 return NT_STATUS_OK;
2583 struct cli_smb2_splice_state {
2584 struct tevent_context *ev;
2585 struct cli_state *cli;
2586 struct smb2_hnd *src_ph;
2587 struct smb2_hnd *dst_ph;
2588 int (*splice_cb)(off_t n, void *priv);
2589 void *priv;
2590 off_t written;
2591 off_t size;
2592 off_t src_offset;
2593 off_t dst_offset;
2594 bool resized;
2595 struct req_resume_key_rsp resume_rsp;
2596 struct srv_copychunk_copy cc_copy;
2599 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
2600 struct tevent_req *req);
2602 static void cli_splice_copychunk_done(struct tevent_req *subreq)
2604 struct tevent_req *req = tevent_req_callback_data(
2605 subreq, struct tevent_req);
2606 struct cli_smb2_splice_state *state =
2607 tevent_req_data(req,
2608 struct cli_smb2_splice_state);
2609 struct smbXcli_conn *conn = state->cli->conn;
2610 DATA_BLOB out_input_buffer = data_blob_null;
2611 DATA_BLOB out_output_buffer = data_blob_null;
2612 struct srv_copychunk_rsp cc_copy_rsp;
2613 enum ndr_err_code ndr_ret;
2614 NTSTATUS status;
2616 status = smb2cli_ioctl_recv(subreq, state,
2617 &out_input_buffer,
2618 &out_output_buffer);
2619 TALLOC_FREE(subreq);
2620 if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
2621 state->resized) && tevent_req_nterror(req, status)) {
2622 return;
2625 ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
2626 (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
2627 if (ndr_ret != NDR_ERR_SUCCESS) {
2628 DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
2629 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
2630 return;
2633 if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
2634 uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
2635 cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
2636 if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
2637 max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
2638 tevent_req_nterror(req, status)) {
2639 return;
2642 state->resized = true;
2643 smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
2644 smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
2645 } else {
2646 if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
2647 (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
2648 (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
2649 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
2650 return;
2652 state->src_offset += cc_copy_rsp.total_bytes_written;
2653 state->dst_offset += cc_copy_rsp.total_bytes_written;
2654 state->written += cc_copy_rsp.total_bytes_written;
2655 if (!state->splice_cb(state->written, state->priv)) {
2656 tevent_req_nterror(req, NT_STATUS_CANCELLED);
2657 return;
2661 cli_splice_copychunk_send(state, req);
2664 static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
2665 struct tevent_req *req)
2667 struct tevent_req *subreq;
2668 enum ndr_err_code ndr_ret;
2669 struct smbXcli_conn *conn = state->cli->conn;
2670 struct srv_copychunk_copy *cc_copy = &state->cc_copy;
2671 off_t src_offset = state->src_offset;
2672 off_t dst_offset = state->dst_offset;
2673 uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
2674 state->size - state->written);
2675 DATA_BLOB in_input_buffer = data_blob_null;
2676 DATA_BLOB in_output_buffer = data_blob_null;
2678 if (state->size - state->written == 0) {
2679 tevent_req_done(req);
2680 return;
2683 cc_copy->chunk_count = 0;
2684 while (req_len) {
2685 cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
2686 cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
2687 cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
2688 smb2cli_conn_cc_chunk_len(conn));
2689 if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
2690 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
2691 return;
2693 req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
2694 if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
2695 (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
2696 tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
2697 return;
2699 src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
2700 dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
2701 cc_copy->chunk_count++;
2704 ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
2705 (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
2706 if (ndr_ret != NDR_ERR_SUCCESS) {
2707 DEBUG(0, ("failed to marshall copy chunk req\n"));
2708 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
2709 return;
2712 subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
2713 state->cli->timeout,
2714 state->cli->smb2.session,
2715 state->cli->smb2.tcon,
2716 state->dst_ph->fid_persistent, /* in_fid_persistent */
2717 state->dst_ph->fid_volatile, /* in_fid_volatile */
2718 FSCTL_SRV_COPYCHUNK_WRITE,
2719 0, /* in_max_input_length */
2720 &in_input_buffer,
2721 12, /* in_max_output_length */
2722 &in_output_buffer,
2723 SMB2_IOCTL_FLAG_IS_FSCTL);
2724 if (tevent_req_nomem(subreq, req)) {
2725 return;
2727 tevent_req_set_callback(subreq,
2728 cli_splice_copychunk_done,
2729 req);
2732 static void cli_splice_key_done(struct tevent_req *subreq)
2734 struct tevent_req *req = tevent_req_callback_data(
2735 subreq, struct tevent_req);
2736 struct cli_smb2_splice_state *state =
2737 tevent_req_data(req,
2738 struct cli_smb2_splice_state);
2739 enum ndr_err_code ndr_ret;
2740 NTSTATUS status;
2742 DATA_BLOB out_input_buffer = data_blob_null;
2743 DATA_BLOB out_output_buffer = data_blob_null;
2745 status = smb2cli_ioctl_recv(subreq, state,
2746 &out_input_buffer,
2747 &out_output_buffer);
2748 TALLOC_FREE(subreq);
2749 if (tevent_req_nterror(req, status)) {
2750 return;
2753 ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
2754 state, &state->resume_rsp,
2755 (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
2756 if (ndr_ret != NDR_ERR_SUCCESS) {
2757 DEBUG(0, ("failed to unmarshall resume key rsp\n"));
2758 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
2759 return;
2762 memcpy(&state->cc_copy.source_key,
2763 &state->resume_rsp.resume_key,
2764 sizeof state->resume_rsp.resume_key);
2766 cli_splice_copychunk_send(state, req);
2769 struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
2770 struct tevent_context *ev,
2771 struct cli_state *cli,
2772 uint16_t src_fnum, uint16_t dst_fnum,
2773 off_t size, off_t src_offset, off_t dst_offset,
2774 int (*splice_cb)(off_t n, void *priv),
2775 void *priv)
2777 struct tevent_req *req;
2778 struct tevent_req *subreq;
2779 struct cli_smb2_splice_state *state;
2780 NTSTATUS status;
2781 DATA_BLOB in_input_buffer = data_blob_null;
2782 DATA_BLOB in_output_buffer = data_blob_null;
2784 req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
2785 if (req == NULL) {
2786 return NULL;
2788 state->cli = cli;
2789 state->ev = ev;
2790 state->splice_cb = splice_cb;
2791 state->priv = priv;
2792 state->size = size;
2793 state->written = 0;
2794 state->src_offset = src_offset;
2795 state->dst_offset = dst_offset;
2796 state->cc_copy.chunks = talloc_array(state,
2797 struct srv_copychunk,
2798 smb2cli_conn_cc_max_chunks(cli->conn));
2799 if (state->cc_copy.chunks == NULL) {
2800 return NULL;
2803 status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
2804 if (tevent_req_nterror(req, status))
2805 return tevent_req_post(req, ev);
2807 status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
2808 if (tevent_req_nterror(req, status))
2809 return tevent_req_post(req, ev);
2811 subreq = smb2cli_ioctl_send(state, ev, cli->conn,
2812 cli->timeout,
2813 cli->smb2.session,
2814 cli->smb2.tcon,
2815 state->src_ph->fid_persistent, /* in_fid_persistent */
2816 state->src_ph->fid_volatile, /* in_fid_volatile */
2817 FSCTL_SRV_REQUEST_RESUME_KEY,
2818 0, /* in_max_input_length */
2819 &in_input_buffer,
2820 32, /* in_max_output_length */
2821 &in_output_buffer,
2822 SMB2_IOCTL_FLAG_IS_FSCTL);
2823 if (tevent_req_nomem(subreq, req)) {
2824 return NULL;
2826 tevent_req_set_callback(subreq,
2827 cli_splice_key_done,
2828 req);
2830 return req;
2833 NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
2835 struct cli_smb2_splice_state *state = tevent_req_data(
2836 req, struct cli_smb2_splice_state);
2837 NTSTATUS status;
2839 if (tevent_req_is_nterror(req, &status)) {
2840 tevent_req_received(req);
2841 return status;
2843 if (written != NULL) {
2844 *written = state->written;
2846 tevent_req_received(req);
2847 return NT_STATUS_OK;