Line data Source code
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
7 :
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.
12 :
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.
17 :
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/>.
20 : */
21 :
22 : /*
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.
27 : */
28 :
29 : #include "includes.h"
30 : #include "client.h"
31 : #include "async_smb.h"
32 : #include "../libcli/smb/smbXcli_base.h"
33 : #include "cli_smb2_fnum.h"
34 : #include "trans2.h"
35 : #include "clirap.h"
36 : #include "../libcli/smb/smb2_create_blob.h"
37 : #include "libsmb/proto.h"
38 : #include "lib/util/tevent_ntstatus.h"
39 : #include "../libcli/security/security.h"
40 : #include "../librpc/gen_ndr/ndr_security.h"
41 : #include "lib/util_ea.h"
42 : #include "librpc/gen_ndr/ndr_ioctl.h"
43 : #include "ntioctl.h"
44 : #include "librpc/gen_ndr/ndr_quota.h"
45 : #include "librpc/gen_ndr/ndr_smb3posix.h"
46 : #include "lib/util/string_wrappers.h"
47 : #include "lib/util/idtree.h"
48 :
49 : struct smb2_hnd {
50 : uint64_t fid_persistent;
51 : uint64_t fid_volatile;
52 : };
53 :
54 : /*
55 : * Handle mapping code.
56 : */
57 :
58 : /***************************************************************
59 : Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd.
60 : Ensures handle is owned by cli struct.
61 : ***************************************************************/
62 :
63 47494 : static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli,
64 : const struct smb2_hnd *ph, /* In */
65 : uint16_t *pfnum) /* Out */
66 : {
67 0 : int ret;
68 47494 : struct idr_context *idp = cli->smb2.open_handles;
69 47494 : struct smb2_hnd *owned_h = talloc_memdup(cli,
70 : ph,
71 : sizeof(struct smb2_hnd));
72 :
73 47494 : if (owned_h == NULL) {
74 0 : return NT_STATUS_NO_MEMORY;
75 : }
76 :
77 47494 : if (idp == NULL) {
78 : /* Lazy init */
79 9345 : cli->smb2.open_handles = idr_init(cli);
80 9345 : if (cli->smb2.open_handles == NULL) {
81 0 : TALLOC_FREE(owned_h);
82 0 : return NT_STATUS_NO_MEMORY;
83 : }
84 9345 : idp = cli->smb2.open_handles;
85 : }
86 :
87 47494 : ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE);
88 47494 : if (ret == -1) {
89 0 : TALLOC_FREE(owned_h);
90 0 : return NT_STATUS_NO_MEMORY;
91 : }
92 :
93 47494 : *pfnum = (uint16_t)ret;
94 47494 : return NT_STATUS_OK;
95 : }
96 :
97 : /***************************************************************
98 : Return the smb2_hnd pointer associated with the given fnum.
99 : ***************************************************************/
100 :
101 91273 : static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli,
102 : uint16_t fnum, /* In */
103 : struct smb2_hnd **pph) /* Out */
104 : {
105 91273 : struct idr_context *idp = cli->smb2.open_handles;
106 :
107 91273 : if (idp == NULL) {
108 0 : return NT_STATUS_INVALID_PARAMETER;
109 : }
110 91273 : *pph = (struct smb2_hnd *)idr_find(idp, fnum);
111 91273 : if (*pph == NULL) {
112 12 : return NT_STATUS_INVALID_HANDLE;
113 : }
114 91261 : return NT_STATUS_OK;
115 : }
116 :
117 : /***************************************************************
118 : Delete the fnum to smb2_hnd mapping. Zeros out handle on
119 : successful return.
120 : ***************************************************************/
121 :
122 47468 : static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli,
123 : struct smb2_hnd **pph, /* In */
124 : uint16_t fnum) /* In */
125 : {
126 47468 : struct idr_context *idp = cli->smb2.open_handles;
127 0 : struct smb2_hnd *ph;
128 :
129 47468 : if (idp == NULL) {
130 0 : return NT_STATUS_INVALID_PARAMETER;
131 : }
132 :
133 47468 : ph = (struct smb2_hnd *)idr_find(idp, fnum);
134 47468 : if (ph != *pph) {
135 0 : return NT_STATUS_INVALID_PARAMETER;
136 : }
137 47468 : idr_remove(idp, fnum);
138 47468 : TALLOC_FREE(*pph);
139 47468 : return NT_STATUS_OK;
140 : }
141 :
142 : /***************************************************************
143 : Oplock mapping code.
144 : ***************************************************************/
145 :
146 57631 : static uint8_t flags_to_smb2_oplock(struct cli_smb2_create_flags create_flags)
147 : {
148 57631 : if (create_flags.batch_oplock) {
149 18 : return SMB2_OPLOCK_LEVEL_BATCH;
150 57613 : } else if (create_flags.exclusive_oplock) {
151 0 : return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
152 : }
153 :
154 : /* create_flags doesn't do a level2 request. */
155 57613 : return SMB2_OPLOCK_LEVEL_NONE;
156 : }
157 :
158 : /***************************************************************
159 : If we're on a DFS share, ensure we convert to a full DFS path
160 : if this hasn't already been done.
161 : ***************************************************************/
162 :
163 57631 : static char *smb2_dfs_share_path(TALLOC_CTX *ctx,
164 : struct cli_state *cli,
165 : char *path)
166 : {
167 112997 : bool is_dfs = smbXcli_conn_dfs_supported(cli->conn) &&
168 55366 : smbXcli_tcon_is_dfs_share(cli->smb2.tcon);
169 57631 : bool is_already_dfs_path = false;
170 :
171 57631 : if (!is_dfs) {
172 44209 : return path;
173 : }
174 13422 : is_already_dfs_path = cli_dfs_is_already_full_path(cli, path);
175 13422 : if (is_already_dfs_path) {
176 13372 : return path;
177 : }
178 50 : if (path[0] == '\0') {
179 6 : return talloc_asprintf(ctx,
180 : "%s\\%s",
181 : smbXcli_conn_remote_name(cli->conn),
182 : cli->share);
183 : }
184 76 : while (*path == '\\') {
185 32 : path++;
186 : }
187 44 : return talloc_asprintf(ctx,
188 : "%s\\%s\\%s",
189 : smbXcli_conn_remote_name(cli->conn),
190 : cli->share,
191 : path);
192 : }
193 :
194 : /***************************************************************
195 : Small wrapper that allows SMB2 create to return a uint16_t fnum.
196 : ***************************************************************/
197 :
198 : struct cli_smb2_create_fnum_state {
199 : struct cli_state *cli;
200 : struct smb2_create_blobs in_cblobs;
201 : struct smb2_create_blobs out_cblobs;
202 : struct smb_create_returns cr;
203 : struct symlink_reparse_struct *symlink;
204 : uint16_t fnum;
205 : struct tevent_req *subreq;
206 : };
207 :
208 : static void cli_smb2_create_fnum_done(struct tevent_req *subreq);
209 : static bool cli_smb2_create_fnum_cancel(struct tevent_req *req);
210 :
211 57631 : struct tevent_req *cli_smb2_create_fnum_send(
212 : TALLOC_CTX *mem_ctx,
213 : struct tevent_context *ev,
214 : struct cli_state *cli,
215 : const char *fname_in,
216 : struct cli_smb2_create_flags create_flags,
217 : uint32_t impersonation_level,
218 : uint32_t desired_access,
219 : uint32_t file_attributes,
220 : uint32_t share_access,
221 : uint32_t create_disposition,
222 : uint32_t create_options,
223 : const struct smb2_create_blobs *in_cblobs)
224 : {
225 0 : struct tevent_req *req, *subreq;
226 0 : struct cli_smb2_create_fnum_state *state;
227 57631 : char *fname = NULL;
228 57631 : size_t fname_len = 0;
229 0 : bool have_twrp;
230 0 : NTTIME ntt;
231 0 : NTSTATUS status;
232 :
233 57631 : req = tevent_req_create(mem_ctx, &state,
234 : struct cli_smb2_create_fnum_state);
235 57631 : if (req == NULL) {
236 0 : return NULL;
237 : }
238 57631 : state->cli = cli;
239 :
240 57631 : fname = talloc_strdup(state, fname_in);
241 57631 : if (tevent_req_nomem(fname, req)) {
242 0 : return tevent_req_post(req, ev);
243 : }
244 :
245 57631 : if (cli->backup_intent) {
246 28 : create_options |= FILE_OPEN_FOR_BACKUP_INTENT;
247 : }
248 :
249 57631 : if (cli->smb2.client_smb311_posix) {
250 12 : uint8_t modebuf[4] = {
251 : 0,
252 : };
253 :
254 0 : status =
255 12 : smb2_create_blob_add(state,
256 12 : &state->in_cblobs,
257 : SMB2_CREATE_TAG_POSIX,
258 12 : (DATA_BLOB){
259 : .data = modebuf,
260 : .length = sizeof(modebuf),
261 : });
262 12 : if (tevent_req_nterror(req, status)) {
263 0 : return tevent_req_post(req, ev);
264 : }
265 : }
266 :
267 : /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */
268 57631 : have_twrp = clistr_smb2_extract_snapshot_token(fname, &ntt);
269 57631 : if (have_twrp) {
270 2655 : status = smb2_create_blob_add(
271 : state,
272 2655 : &state->in_cblobs,
273 : SMB2_CREATE_TAG_TWRP,
274 2655 : (DATA_BLOB) {
275 : .data = (uint8_t *)&ntt,
276 : .length = sizeof(ntt),
277 : });
278 2655 : if (tevent_req_nterror(req, status)) {
279 0 : return tevent_req_post(req, ev);
280 : }
281 : }
282 :
283 57631 : if (in_cblobs != NULL) {
284 : uint32_t i;
285 4292 : for (i=0; i<in_cblobs->num_blobs; i++) {
286 2146 : struct smb2_create_blob *b = &in_cblobs->blobs[i];
287 2146 : status = smb2_create_blob_add(
288 2146 : state, &state->in_cblobs, b->tag, b->data);
289 2146 : if (!NT_STATUS_IS_OK(status)) {
290 0 : tevent_req_nterror(req, status);
291 0 : return tevent_req_post(req, ev);
292 : }
293 : }
294 : }
295 :
296 57631 : fname = smb2_dfs_share_path(state, cli, fname);
297 57631 : if (tevent_req_nomem(fname, req)) {
298 0 : return tevent_req_post(req, ev);
299 : }
300 57631 : fname_len = strlen(fname);
301 :
302 : /* SMB2 is pickier about pathnames. Ensure it doesn't
303 : start in a '\' */
304 57631 : if (*fname == '\\') {
305 39911 : fname++;
306 39911 : fname_len--;
307 : }
308 :
309 : /* Or end in a '\' */
310 57631 : if (fname_len > 0 && fname[fname_len-1] == '\\') {
311 1340 : fname[fname_len-1] = '\0';
312 : }
313 :
314 115262 : subreq = smb2cli_create_send(state, ev,
315 : cli->conn,
316 57631 : cli->timeout,
317 : cli->smb2.session,
318 : cli->smb2.tcon,
319 : fname,
320 57631 : flags_to_smb2_oplock(create_flags),
321 : impersonation_level,
322 : desired_access,
323 : file_attributes,
324 : share_access,
325 : create_disposition,
326 : create_options,
327 57631 : &state->in_cblobs);
328 57631 : if (tevent_req_nomem(subreq, req)) {
329 0 : return tevent_req_post(req, ev);
330 : }
331 57631 : tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req);
332 :
333 57631 : state->subreq = subreq;
334 57631 : tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel);
335 :
336 57631 : return req;
337 : }
338 :
339 57631 : static void cli_smb2_create_fnum_done(struct tevent_req *subreq)
340 : {
341 57631 : struct tevent_req *req = tevent_req_callback_data(
342 : subreq, struct tevent_req);
343 57631 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
344 : req, struct cli_smb2_create_fnum_state);
345 0 : struct smb2_hnd h;
346 0 : NTSTATUS status;
347 :
348 57631 : status = smb2cli_create_recv(
349 : subreq,
350 : &h.fid_persistent,
351 : &h.fid_volatile, &state->cr,
352 : state,
353 : &state->out_cblobs,
354 : &state->symlink);
355 57631 : TALLOC_FREE(subreq);
356 57631 : if (tevent_req_nterror(req, status)) {
357 10137 : return;
358 : }
359 :
360 47494 : status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum);
361 47494 : if (tevent_req_nterror(req, status)) {
362 0 : return;
363 : }
364 47494 : tevent_req_done(req);
365 : }
366 :
367 2 : static bool cli_smb2_create_fnum_cancel(struct tevent_req *req)
368 : {
369 2 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
370 : req, struct cli_smb2_create_fnum_state);
371 2 : return tevent_req_cancel(state->subreq);
372 : }
373 :
374 57631 : NTSTATUS cli_smb2_create_fnum_recv(
375 : struct tevent_req *req,
376 : uint16_t *pfnum,
377 : struct smb_create_returns *cr,
378 : TALLOC_CTX *mem_ctx,
379 : struct smb2_create_blobs *out_cblobs,
380 : struct symlink_reparse_struct **symlink)
381 : {
382 57631 : struct cli_smb2_create_fnum_state *state = tevent_req_data(
383 : req, struct cli_smb2_create_fnum_state);
384 0 : NTSTATUS status;
385 :
386 57631 : if (tevent_req_is_nterror(req, &status)) {
387 10137 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) &&
388 : (symlink != NULL)) {
389 0 : *symlink = talloc_move(mem_ctx, &state->symlink);
390 : }
391 10137 : state->cli->raw_status = status;
392 10137 : return status;
393 : }
394 47494 : if (pfnum != NULL) {
395 47494 : *pfnum = state->fnum;
396 : }
397 47494 : if (cr != NULL) {
398 25079 : *cr = state->cr;
399 : }
400 47494 : if (out_cblobs != NULL) {
401 2142 : *out_cblobs = (struct smb2_create_blobs) {
402 2142 : .num_blobs = state->out_cblobs.num_blobs,
403 2142 : .blobs = talloc_move(
404 : mem_ctx, &state->out_cblobs.blobs),
405 : };
406 : }
407 47494 : state->cli->raw_status = NT_STATUS_OK;
408 47494 : return NT_STATUS_OK;
409 : }
410 :
411 11141 : NTSTATUS cli_smb2_create_fnum(
412 : struct cli_state *cli,
413 : const char *fname,
414 : struct cli_smb2_create_flags create_flags,
415 : uint32_t impersonation_level,
416 : uint32_t desired_access,
417 : uint32_t file_attributes,
418 : uint32_t share_access,
419 : uint32_t create_disposition,
420 : uint32_t create_options,
421 : const struct smb2_create_blobs *in_cblobs,
422 : uint16_t *pfid,
423 : struct smb_create_returns *cr,
424 : TALLOC_CTX *mem_ctx,
425 : struct smb2_create_blobs *out_cblobs)
426 : {
427 11141 : TALLOC_CTX *frame = talloc_stackframe();
428 0 : struct tevent_context *ev;
429 0 : struct tevent_req *req;
430 11141 : NTSTATUS status = NT_STATUS_NO_MEMORY;
431 :
432 11141 : if (smbXcli_conn_has_async_calls(cli->conn)) {
433 : /*
434 : * Can't use sync call while an async call is in flight
435 : */
436 0 : status = NT_STATUS_INVALID_PARAMETER;
437 0 : goto fail;
438 : }
439 11141 : ev = samba_tevent_context_init(frame);
440 11141 : if (ev == NULL) {
441 0 : goto fail;
442 : }
443 11141 : req = cli_smb2_create_fnum_send(
444 : frame,
445 : ev,
446 : cli,
447 : fname,
448 : create_flags,
449 : impersonation_level,
450 : desired_access,
451 : file_attributes,
452 : share_access,
453 : create_disposition,
454 : create_options,
455 : in_cblobs);
456 11141 : if (req == NULL) {
457 0 : goto fail;
458 : }
459 11141 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
460 0 : goto fail;
461 : }
462 11141 : status = cli_smb2_create_fnum_recv(
463 : req, pfid, cr, mem_ctx, out_cblobs, NULL);
464 11141 : fail:
465 11141 : TALLOC_FREE(frame);
466 11141 : return status;
467 : }
468 :
469 : /***************************************************************
470 : Small wrapper that allows SMB2 close to use a uint16_t fnum.
471 : ***************************************************************/
472 :
473 : struct cli_smb2_close_fnum_state {
474 : struct cli_state *cli;
475 : uint16_t fnum;
476 : struct smb2_hnd *ph;
477 : };
478 :
479 : static void cli_smb2_close_fnum_done(struct tevent_req *subreq);
480 :
481 47482 : struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx,
482 : struct tevent_context *ev,
483 : struct cli_state *cli,
484 : uint16_t fnum,
485 : uint16_t flags)
486 : {
487 0 : struct tevent_req *req, *subreq;
488 0 : struct cli_smb2_close_fnum_state *state;
489 0 : NTSTATUS status;
490 :
491 47482 : req = tevent_req_create(mem_ctx, &state,
492 : struct cli_smb2_close_fnum_state);
493 47482 : if (req == NULL) {
494 0 : return NULL;
495 : }
496 47482 : state->cli = cli;
497 47482 : state->fnum = fnum;
498 :
499 47482 : status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
500 47482 : if (tevent_req_nterror(req, status)) {
501 6 : return tevent_req_post(req, ev);
502 : }
503 :
504 47476 : subreq = smb2cli_close_send(state,
505 : ev,
506 : cli->conn,
507 47476 : cli->timeout,
508 : cli->smb2.session,
509 : cli->smb2.tcon,
510 : flags,
511 47476 : state->ph->fid_persistent,
512 47476 : state->ph->fid_volatile);
513 47476 : if (tevent_req_nomem(subreq, req)) {
514 0 : return tevent_req_post(req, ev);
515 : }
516 47476 : tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req);
517 47476 : return req;
518 : }
519 :
520 47476 : static void cli_smb2_close_fnum_done(struct tevent_req *subreq)
521 : {
522 47476 : struct tevent_req *req = tevent_req_callback_data(
523 : subreq, struct tevent_req);
524 47476 : struct cli_smb2_close_fnum_state *state = tevent_req_data(
525 : req, struct cli_smb2_close_fnum_state);
526 0 : NTSTATUS status;
527 :
528 47476 : status = smb2cli_close_recv(subreq);
529 47476 : if (tevent_req_nterror(req, status)) {
530 8 : return;
531 : }
532 :
533 : /* Delete the fnum -> handle mapping. */
534 47468 : status = delete_smb2_handle_mapping(state->cli, &state->ph,
535 47468 : state->fnum);
536 47468 : if (tevent_req_nterror(req, status)) {
537 0 : return;
538 : }
539 47468 : tevent_req_done(req);
540 : }
541 :
542 26472 : NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req)
543 : {
544 26472 : struct cli_smb2_close_fnum_state *state = tevent_req_data(
545 : req, struct cli_smb2_close_fnum_state);
546 26472 : NTSTATUS status = NT_STATUS_OK;
547 :
548 26472 : if (tevent_req_is_nterror(req, &status)) {
549 2 : state->cli->raw_status = status;
550 : }
551 26472 : tevent_req_received(req);
552 26472 : return status;
553 : }
554 :
555 6555 : NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum)
556 : {
557 6555 : TALLOC_CTX *frame = talloc_stackframe();
558 0 : struct tevent_context *ev;
559 0 : struct tevent_req *req;
560 6555 : NTSTATUS status = NT_STATUS_NO_MEMORY;
561 :
562 6555 : if (smbXcli_conn_has_async_calls(cli->conn)) {
563 : /*
564 : * Can't use sync call while an async call is in flight
565 : */
566 0 : status = NT_STATUS_INVALID_PARAMETER;
567 0 : goto fail;
568 : }
569 6555 : ev = samba_tevent_context_init(frame);
570 6555 : if (ev == NULL) {
571 0 : goto fail;
572 : }
573 6555 : req = cli_smb2_close_fnum_send(frame, ev, cli, fnum, 0);
574 6555 : if (req == NULL) {
575 0 : goto fail;
576 : }
577 6555 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
578 0 : goto fail;
579 : }
580 6555 : status = cli_smb2_close_fnum_recv(req);
581 6555 : fail:
582 6555 : TALLOC_FREE(frame);
583 6555 : return status;
584 : }
585 :
586 : struct cli_smb2_set_info_fnum_state {
587 : uint8_t dummy;
588 : };
589 :
590 : static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq);
591 :
592 7768 : struct tevent_req *cli_smb2_set_info_fnum_send(
593 : TALLOC_CTX *mem_ctx,
594 : struct tevent_context *ev,
595 : struct cli_state *cli,
596 : uint16_t fnum,
597 : uint8_t in_info_type,
598 : uint8_t in_info_class,
599 : const DATA_BLOB *in_input_buffer,
600 : uint32_t in_additional_info)
601 : {
602 7768 : struct tevent_req *req = NULL, *subreq = NULL;
603 7768 : struct cli_smb2_set_info_fnum_state *state = NULL;
604 7768 : struct smb2_hnd *ph = NULL;
605 0 : NTSTATUS status;
606 :
607 7768 : req = tevent_req_create(
608 : mem_ctx, &state, struct cli_smb2_set_info_fnum_state);
609 7768 : if (req == NULL) {
610 0 : return NULL;
611 : }
612 :
613 7768 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
614 7768 : if (tevent_req_nterror(req, status)) {
615 0 : return tevent_req_post(req, ev);
616 : }
617 :
618 7768 : subreq = smb2cli_set_info_send(
619 : state,
620 : ev,
621 : cli->conn,
622 7768 : cli->timeout,
623 : cli->smb2.session,
624 : cli->smb2.tcon,
625 : in_info_type,
626 : in_info_class,
627 : in_input_buffer,
628 : in_additional_info,
629 7768 : ph->fid_persistent,
630 7768 : ph->fid_volatile);
631 7768 : if (tevent_req_nomem(subreq, req)) {
632 0 : return tevent_req_post(req, ev);
633 : }
634 7768 : tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req);
635 7768 : return req;
636 : }
637 :
638 7768 : static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq)
639 : {
640 7768 : NTSTATUS status = smb2cli_set_info_recv(subreq);
641 7768 : tevent_req_simple_finish_ntstatus(subreq, status);
642 7768 : }
643 :
644 7768 : NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req)
645 : {
646 7768 : return tevent_req_simple_recv_ntstatus(req);
647 : }
648 :
649 1345 : NTSTATUS cli_smb2_set_info_fnum(
650 : struct cli_state *cli,
651 : uint16_t fnum,
652 : uint8_t in_info_type,
653 : uint8_t in_info_class,
654 : const DATA_BLOB *in_input_buffer,
655 : uint32_t in_additional_info)
656 : {
657 1345 : TALLOC_CTX *frame = talloc_stackframe();
658 1345 : struct tevent_context *ev = NULL;
659 1345 : struct tevent_req *req = NULL;
660 1345 : NTSTATUS status = NT_STATUS_NO_MEMORY;
661 0 : bool ok;
662 :
663 1345 : if (smbXcli_conn_has_async_calls(cli->conn)) {
664 : /*
665 : * Can't use sync call while an async call is in flight
666 : */
667 0 : status = NT_STATUS_INVALID_PARAMETER;
668 0 : goto fail;
669 : }
670 1345 : ev = samba_tevent_context_init(frame);
671 1345 : if (ev == NULL) {
672 0 : goto fail;
673 : }
674 1345 : req = cli_smb2_set_info_fnum_send(
675 : frame,
676 : ev,
677 : cli,
678 : fnum,
679 : in_info_type,
680 : in_info_class,
681 : in_input_buffer,
682 : in_additional_info);
683 1345 : if (req == NULL) {
684 0 : goto fail;
685 : }
686 1345 : ok = tevent_req_poll_ntstatus(req, ev, &status);
687 1345 : if (!ok) {
688 0 : goto fail;
689 : }
690 1345 : status = cli_smb2_set_info_fnum_recv(req);
691 1345 : fail:
692 1345 : TALLOC_FREE(frame);
693 1345 : return status;
694 : }
695 :
696 : struct cli_smb2_delete_on_close_state {
697 : struct cli_state *cli;
698 : uint8_t data[1];
699 : DATA_BLOB inbuf;
700 : };
701 :
702 : static void cli_smb2_delete_on_close_done(struct tevent_req *subreq);
703 :
704 3576 : struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx,
705 : struct tevent_context *ev,
706 : struct cli_state *cli,
707 : uint16_t fnum,
708 : bool flag)
709 : {
710 3576 : struct tevent_req *req = NULL;
711 3576 : struct cli_smb2_delete_on_close_state *state = NULL;
712 3576 : struct tevent_req *subreq = NULL;
713 :
714 3576 : req = tevent_req_create(mem_ctx, &state,
715 : struct cli_smb2_delete_on_close_state);
716 3576 : if (req == NULL) {
717 0 : return NULL;
718 : }
719 3576 : state->cli = cli;
720 :
721 : /* Setup data array. */
722 3576 : SCVAL(&state->data[0], 0, flag ? 1 : 0);
723 3576 : state->inbuf.data = &state->data[0];
724 3576 : state->inbuf.length = 1;
725 :
726 3576 : subreq = cli_smb2_set_info_fnum_send(state,
727 : ev,
728 : cli,
729 : fnum,
730 : SMB2_0_INFO_FILE,
731 : FSCC_FILE_DISPOSITION_INFORMATION,
732 3576 : &state->inbuf,
733 : 0);
734 3576 : if (tevent_req_nomem(subreq, req)) {
735 0 : return tevent_req_post(req, ev);
736 : }
737 3576 : tevent_req_set_callback(subreq,
738 : cli_smb2_delete_on_close_done,
739 : req);
740 3576 : return req;
741 : }
742 :
743 3576 : static void cli_smb2_delete_on_close_done(struct tevent_req *subreq)
744 : {
745 3576 : NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
746 3576 : tevent_req_simple_finish_ntstatus(subreq, status);
747 3576 : }
748 :
749 3576 : NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req)
750 : {
751 0 : struct cli_smb2_delete_on_close_state *state =
752 3576 : tevent_req_data(req,
753 : struct cli_smb2_delete_on_close_state);
754 0 : NTSTATUS status;
755 :
756 3576 : if (tevent_req_is_nterror(req, &status)) {
757 62 : state->cli->raw_status = status;
758 62 : tevent_req_received(req);
759 62 : return status;
760 : }
761 :
762 3514 : state->cli->raw_status = NT_STATUS_OK;
763 3514 : tevent_req_received(req);
764 3514 : return NT_STATUS_OK;
765 : }
766 :
767 0 : NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag)
768 : {
769 0 : TALLOC_CTX *frame = talloc_stackframe();
770 0 : struct tevent_context *ev;
771 0 : struct tevent_req *req;
772 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
773 :
774 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
775 : /*
776 : * Can't use sync call while an async call is in flight
777 : */
778 0 : status = NT_STATUS_INVALID_PARAMETER;
779 0 : goto fail;
780 : }
781 0 : ev = samba_tevent_context_init(frame);
782 0 : if (ev == NULL) {
783 0 : goto fail;
784 : }
785 0 : req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag);
786 0 : if (req == NULL) {
787 0 : goto fail;
788 : }
789 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
790 0 : goto fail;
791 : }
792 0 : status = cli_smb2_delete_on_close_recv(req);
793 0 : fail:
794 0 : TALLOC_FREE(frame);
795 0 : return status;
796 : }
797 :
798 : struct cli_smb2_mkdir_state {
799 : struct tevent_context *ev;
800 : struct cli_state *cli;
801 : };
802 :
803 : static void cli_smb2_mkdir_opened(struct tevent_req *subreq);
804 : static void cli_smb2_mkdir_closed(struct tevent_req *subreq);
805 :
806 3328 : struct tevent_req *cli_smb2_mkdir_send(
807 : TALLOC_CTX *mem_ctx,
808 : struct tevent_context *ev,
809 : struct cli_state *cli,
810 : const char *dname)
811 : {
812 3328 : struct tevent_req *req = NULL, *subreq = NULL;
813 3328 : struct cli_smb2_mkdir_state *state = NULL;
814 :
815 3328 : req = tevent_req_create(
816 : mem_ctx, &state, struct cli_smb2_mkdir_state);
817 3328 : if (req == NULL) {
818 0 : return NULL;
819 : }
820 3328 : state->ev = ev;
821 3328 : state->cli = cli;
822 :
823 : /* Ensure this is a directory. */
824 3328 : subreq = cli_smb2_create_fnum_send(
825 : state, /* mem_ctx */
826 : ev, /* ev */
827 : cli, /* cli */
828 : dname, /* fname */
829 3328 : (struct cli_smb2_create_flags){0}, /* create_flags */
830 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
831 : FILE_READ_ATTRIBUTES, /* desired_access */
832 : FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
833 : FILE_SHARE_READ|
834 : FILE_SHARE_WRITE, /* share_access */
835 : FILE_CREATE, /* create_disposition */
836 : FILE_DIRECTORY_FILE, /* create_options */
837 : NULL); /* in_cblobs */
838 3328 : if (tevent_req_nomem(subreq, req)) {
839 0 : return tevent_req_post(req, ev);
840 : }
841 3328 : tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req);
842 3328 : return req;
843 : }
844 :
845 3328 : static void cli_smb2_mkdir_opened(struct tevent_req *subreq)
846 : {
847 3328 : struct tevent_req *req = tevent_req_callback_data(
848 : subreq, struct tevent_req);
849 3328 : struct cli_smb2_mkdir_state *state = tevent_req_data(
850 : req, struct cli_smb2_mkdir_state);
851 0 : NTSTATUS status;
852 3328 : uint16_t fnum = 0xffff;
853 :
854 3328 : status = cli_smb2_create_fnum_recv(
855 : subreq, &fnum, NULL, NULL, NULL, NULL);
856 3328 : TALLOC_FREE(subreq);
857 3328 : if (tevent_req_nterror(req, status)) {
858 1026 : return;
859 : }
860 :
861 0 : subreq =
862 2302 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
863 2302 : if (tevent_req_nomem(subreq, req)) {
864 0 : return;
865 : }
866 2302 : tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req);
867 : }
868 :
869 2302 : static void cli_smb2_mkdir_closed(struct tevent_req *subreq)
870 : {
871 2302 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
872 2302 : tevent_req_simple_finish_ntstatus(subreq, status);
873 2302 : }
874 :
875 3328 : NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req)
876 : {
877 3328 : return tevent_req_simple_recv_ntstatus(req);
878 : }
879 :
880 : struct cli_smb2_rmdir_state {
881 : struct tevent_context *ev;
882 : struct cli_state *cli;
883 : const char *dname;
884 : const struct smb2_create_blobs *in_cblobs;
885 : uint16_t fnum;
886 : NTSTATUS status;
887 : };
888 :
889 : static void cli_smb2_rmdir_opened1(struct tevent_req *subreq);
890 : static void cli_smb2_rmdir_opened2(struct tevent_req *subreq);
891 : static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq);
892 : static void cli_smb2_rmdir_closed(struct tevent_req *subreq);
893 :
894 2500 : struct tevent_req *cli_smb2_rmdir_send(
895 : TALLOC_CTX *mem_ctx,
896 : struct tevent_context *ev,
897 : struct cli_state *cli,
898 : const char *dname,
899 : const struct smb2_create_blobs *in_cblobs)
900 : {
901 2500 : struct tevent_req *req = NULL, *subreq = NULL;
902 2500 : struct cli_smb2_rmdir_state *state = NULL;
903 :
904 2500 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state);
905 2500 : if (req == NULL) {
906 0 : return NULL;
907 : }
908 2500 : state->ev = ev;
909 2500 : state->cli = cli;
910 2500 : state->dname = dname;
911 2500 : state->in_cblobs = in_cblobs;
912 :
913 2500 : subreq = cli_smb2_create_fnum_send(
914 : state,
915 2500 : state->ev,
916 2500 : state->cli,
917 2500 : state->dname,
918 2500 : (struct cli_smb2_create_flags){0},
919 : SMB2_IMPERSONATION_IMPERSONATION,
920 : DELETE_ACCESS, /* desired_access */
921 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
922 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
923 : FILE_OPEN, /* create_disposition */
924 : FILE_DIRECTORY_FILE, /* create_options */
925 2500 : state->in_cblobs); /* in_cblobs */
926 2500 : if (tevent_req_nomem(subreq, req)) {
927 0 : return tevent_req_post(req, ev);
928 : }
929 2500 : tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req);
930 2500 : return req;
931 : }
932 :
933 2500 : static void cli_smb2_rmdir_opened1(struct tevent_req *subreq)
934 : {
935 2500 : struct tevent_req *req = tevent_req_callback_data(
936 : subreq, struct tevent_req);
937 2500 : struct cli_smb2_rmdir_state *state = tevent_req_data(
938 : req, struct cli_smb2_rmdir_state);
939 0 : NTSTATUS status;
940 :
941 2500 : status = cli_smb2_create_fnum_recv(
942 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
943 2500 : TALLOC_FREE(subreq);
944 :
945 2500 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
946 : /*
947 : * Naive option to match our SMB1 code. Assume the
948 : * symlink path that tripped us up was the last
949 : * component and try again. Eventually we will have to
950 : * deal with the returned path unprocessed component. JRA.
951 : */
952 0 : subreq = cli_smb2_create_fnum_send(
953 : state,
954 : state->ev,
955 : state->cli,
956 : state->dname,
957 0 : (struct cli_smb2_create_flags){0},
958 : SMB2_IMPERSONATION_IMPERSONATION,
959 : DELETE_ACCESS, /* desired_access */
960 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
961 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
962 : FILE_OPEN, /* create_disposition */
963 : FILE_DIRECTORY_FILE|
964 : FILE_DELETE_ON_CLOSE|
965 : FILE_OPEN_REPARSE_POINT, /* create_options */
966 : state->in_cblobs); /* in_cblobs */
967 0 : if (tevent_req_nomem(subreq, req)) {
968 0 : return;
969 : }
970 0 : tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req);
971 0 : return;
972 : }
973 :
974 2500 : if (tevent_req_nterror(req, status)) {
975 40 : return;
976 : }
977 :
978 2460 : subreq = cli_smb2_delete_on_close_send(
979 2460 : state, state->ev, state->cli, state->fnum, true);
980 2460 : if (tevent_req_nomem(subreq, req)) {
981 0 : return;
982 : }
983 2460 : tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
984 : }
985 :
986 0 : static void cli_smb2_rmdir_opened2(struct tevent_req *subreq)
987 : {
988 0 : struct tevent_req *req = tevent_req_callback_data(
989 : subreq, struct tevent_req);
990 0 : struct cli_smb2_rmdir_state *state = tevent_req_data(
991 : req, struct cli_smb2_rmdir_state);
992 0 : NTSTATUS status;
993 :
994 0 : status = cli_smb2_create_fnum_recv(
995 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
996 0 : TALLOC_FREE(subreq);
997 0 : if (tevent_req_nterror(req, status)) {
998 0 : return;
999 : }
1000 :
1001 0 : subreq = cli_smb2_delete_on_close_send(
1002 0 : state, state->ev, state->cli, state->fnum, true);
1003 0 : if (tevent_req_nomem(subreq, req)) {
1004 0 : return;
1005 : }
1006 0 : tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req);
1007 : }
1008 :
1009 2460 : static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq)
1010 : {
1011 2460 : struct tevent_req *req = tevent_req_callback_data(
1012 : subreq, struct tevent_req);
1013 2460 : struct cli_smb2_rmdir_state *state = tevent_req_data(
1014 : req, struct cli_smb2_rmdir_state);
1015 :
1016 2460 : state->status = cli_smb2_delete_on_close_recv(subreq);
1017 2460 : TALLOC_FREE(subreq);
1018 :
1019 : /*
1020 : * Close the fd even if the set_disp failed
1021 : */
1022 :
1023 2460 : subreq = cli_smb2_close_fnum_send(state,
1024 : state->ev,
1025 : state->cli,
1026 2460 : state->fnum,
1027 : 0);
1028 2460 : if (tevent_req_nomem(subreq, req)) {
1029 0 : return;
1030 : }
1031 2460 : tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req);
1032 : }
1033 :
1034 2460 : static void cli_smb2_rmdir_closed(struct tevent_req *subreq)
1035 : {
1036 2460 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1037 2460 : tevent_req_simple_finish_ntstatus(subreq, status);
1038 2460 : }
1039 :
1040 2500 : NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req)
1041 : {
1042 2500 : struct cli_smb2_rmdir_state *state = tevent_req_data(
1043 : req, struct cli_smb2_rmdir_state);
1044 0 : NTSTATUS status;
1045 :
1046 2500 : if (tevent_req_is_nterror(req, &status)) {
1047 42 : return status;
1048 : }
1049 2458 : return state->status;
1050 : }
1051 :
1052 : /***************************************************************
1053 : Small wrapper that allows SMB2 to unlink a pathname.
1054 : ***************************************************************/
1055 :
1056 : struct cli_smb2_unlink_state {
1057 : struct tevent_context *ev;
1058 : struct cli_state *cli;
1059 : const char *fname;
1060 : const struct smb2_create_blobs *in_cblobs;
1061 : };
1062 :
1063 : static void cli_smb2_unlink_opened1(struct tevent_req *subreq);
1064 : static void cli_smb2_unlink_opened2(struct tevent_req *subreq);
1065 : static void cli_smb2_unlink_closed(struct tevent_req *subreq);
1066 :
1067 2393 : struct tevent_req *cli_smb2_unlink_send(
1068 : TALLOC_CTX *mem_ctx,
1069 : struct tevent_context *ev,
1070 : struct cli_state *cli,
1071 : const char *fname,
1072 : const struct smb2_create_blobs *in_cblobs)
1073 : {
1074 2393 : struct tevent_req *req = NULL, *subreq = NULL;
1075 2393 : struct cli_smb2_unlink_state *state = NULL;
1076 :
1077 2393 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state);
1078 2393 : if (req == NULL) {
1079 0 : return NULL;
1080 : }
1081 2393 : state->ev = ev;
1082 2393 : state->cli = cli;
1083 2393 : state->fname = fname;
1084 2393 : state->in_cblobs = in_cblobs;
1085 :
1086 2393 : subreq = cli_smb2_create_fnum_send(
1087 : state, /* mem_ctx */
1088 2393 : state->ev, /* tevent_context */
1089 2393 : state->cli, /* cli_struct */
1090 2393 : state->fname, /* filename */
1091 2393 : (struct cli_smb2_create_flags){0},
1092 : SMB2_IMPERSONATION_IMPERSONATION,
1093 : DELETE_ACCESS, /* desired_access */
1094 : FILE_ATTRIBUTE_NORMAL, /* file attributes */
1095 : FILE_SHARE_READ|
1096 : FILE_SHARE_WRITE|
1097 : FILE_SHARE_DELETE, /* share_access */
1098 : FILE_OPEN, /* create_disposition */
1099 : FILE_DELETE_ON_CLOSE, /* create_options */
1100 2393 : state->in_cblobs); /* in_cblobs */
1101 2393 : if (tevent_req_nomem(subreq, req)) {
1102 0 : return tevent_req_post(req, ev);
1103 : }
1104 2393 : tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req);
1105 2393 : return req;
1106 : }
1107 :
1108 2393 : static void cli_smb2_unlink_opened1(struct tevent_req *subreq)
1109 : {
1110 2393 : struct tevent_req *req = tevent_req_callback_data(
1111 : subreq, struct tevent_req);
1112 2393 : struct cli_smb2_unlink_state *state = tevent_req_data(
1113 : req, struct cli_smb2_unlink_state);
1114 2393 : uint16_t fnum = 0xffff;
1115 0 : NTSTATUS status;
1116 :
1117 2393 : status = cli_smb2_create_fnum_recv(
1118 : subreq, &fnum, NULL, NULL, NULL, NULL);
1119 2393 : TALLOC_FREE(subreq);
1120 :
1121 2393 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
1122 2393 : NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
1123 : /*
1124 : * Naive option to match our SMB1 code. Assume the
1125 : * symlink path that tripped us up was the last
1126 : * component and try again. Eventually we will have to
1127 : * deal with the returned path unprocessed component. JRA.
1128 : */
1129 0 : subreq = cli_smb2_create_fnum_send(
1130 : state, /* mem_ctx */
1131 : state->ev, /* tevent_context */
1132 : state->cli, /* cli_struct */
1133 : state->fname, /* filename */
1134 0 : (struct cli_smb2_create_flags){0},
1135 : SMB2_IMPERSONATION_IMPERSONATION,
1136 : DELETE_ACCESS, /* desired_access */
1137 : FILE_ATTRIBUTE_NORMAL, /* file attributes */
1138 : FILE_SHARE_READ|
1139 : FILE_SHARE_WRITE|
1140 : FILE_SHARE_DELETE, /* share_access */
1141 : FILE_OPEN, /* create_disposition */
1142 : FILE_DELETE_ON_CLOSE|
1143 : FILE_OPEN_REPARSE_POINT, /* create_options */
1144 : state->in_cblobs); /* in_cblobs */
1145 0 : if (tevent_req_nomem(subreq, req)) {
1146 0 : return;
1147 : }
1148 0 : tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req);
1149 0 : return;
1150 : }
1151 :
1152 2393 : if (tevent_req_nterror(req, status)) {
1153 288 : return;
1154 : }
1155 :
1156 0 : subreq =
1157 2105 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1158 2105 : if (tevent_req_nomem(subreq, req)) {
1159 0 : return;
1160 : }
1161 2105 : tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1162 : }
1163 :
1164 0 : static void cli_smb2_unlink_opened2(struct tevent_req *subreq)
1165 : {
1166 0 : struct tevent_req *req = tevent_req_callback_data(
1167 : subreq, struct tevent_req);
1168 0 : struct cli_smb2_unlink_state *state = tevent_req_data(
1169 : req, struct cli_smb2_unlink_state);
1170 0 : uint16_t fnum = 0xffff;
1171 0 : NTSTATUS status;
1172 :
1173 0 : status = cli_smb2_create_fnum_recv(
1174 : subreq, &fnum, NULL, NULL, NULL, NULL);
1175 0 : TALLOC_FREE(subreq);
1176 0 : if (tevent_req_nterror(req, status)) {
1177 0 : return;
1178 : }
1179 :
1180 0 : subreq =
1181 0 : cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum, 0);
1182 0 : if (tevent_req_nomem(subreq, req)) {
1183 0 : return;
1184 : }
1185 0 : tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req);
1186 : }
1187 :
1188 2105 : static void cli_smb2_unlink_closed(struct tevent_req *subreq)
1189 : {
1190 2105 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1191 2105 : tevent_req_simple_finish_ntstatus(subreq, status);
1192 2105 : }
1193 :
1194 2393 : NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req)
1195 : {
1196 2393 : return tevent_req_simple_recv_ntstatus(req);
1197 : }
1198 :
1199 : /***************************************************************
1200 : Utility function to parse a SMB2_FIND_POSIX_INFORMATION reply.
1201 : ***************************************************************/
1202 :
1203 1449 : static NTSTATUS parse_finfo_posix_info(const uint8_t *dir_data,
1204 : uint32_t dir_data_length,
1205 : struct file_info *finfo,
1206 : uint32_t *next_offset)
1207 : {
1208 1449 : struct smb3_file_posix_information info = {};
1209 0 : size_t consumed;
1210 0 : enum ndr_err_code ndr_err;
1211 1449 : size_t namelen = 0;
1212 1449 : size_t ret = 0;
1213 1449 : uint32_t _next_offset = 0;
1214 :
1215 1449 : if (dir_data_length < 4) {
1216 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1217 : }
1218 :
1219 1449 : _next_offset = IVAL(dir_data, 0);
1220 :
1221 1449 : if (_next_offset > dir_data_length) {
1222 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1223 : }
1224 :
1225 1449 : if (_next_offset != 0) {
1226 : /* Ensure we only read what in this record. */
1227 1443 : dir_data_length = _next_offset;
1228 : }
1229 :
1230 : /*
1231 : * Skip NextEntryOffset and FileIndex
1232 : */
1233 1449 : if (dir_data_length < 8) {
1234 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1235 : }
1236 1449 : dir_data += 8;
1237 1449 : dir_data_length -= 8;
1238 :
1239 1449 : ndr_err = ndr_pull_struct_blob_noalloc(
1240 : dir_data,
1241 : dir_data_length,
1242 : &info,
1243 : (ndr_pull_flags_fn_t)ndr_pull_smb3_file_posix_information,
1244 : &consumed);
1245 1449 : if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1246 0 : return ndr_map_error2ntstatus(ndr_err);
1247 : }
1248 1449 : if (consumed > dir_data_length) {
1249 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1250 : }
1251 1449 : dir_data += consumed;
1252 1449 : dir_data_length -= consumed;
1253 :
1254 1449 : finfo->btime_ts = interpret_long_date(info.creation_time);
1255 1449 : finfo->atime_ts = interpret_long_date(info.last_access_time);
1256 1449 : finfo->mtime_ts = interpret_long_date(info.last_write_time);
1257 1449 : finfo->ctime_ts = interpret_long_date(info.change_time);
1258 1449 : finfo->allocated_size = info.allocation_size;
1259 1449 : finfo->size = info.end_of_file;
1260 1449 : finfo->attr = info.file_attributes;
1261 1449 : finfo->ino = info.inode;
1262 1449 : finfo->st_ex_dev = info.device;
1263 1449 : finfo->st_ex_nlink = info.cc.nlinks;
1264 1449 : finfo->reparse_tag = info.cc.reparse_tag;
1265 1449 : finfo->st_ex_mode = wire_perms_to_unix(info.cc.posix_perms);
1266 1449 : sid_copy(&finfo->owner_sid, &info.cc.owner);
1267 1449 : sid_copy(&finfo->group_sid, &info.cc.group);
1268 :
1269 1449 : if (dir_data_length < 4) {
1270 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1271 : }
1272 1449 : namelen = PULL_LE_U32(dir_data, 0);
1273 :
1274 1449 : dir_data += 4;
1275 1449 : dir_data_length -= 4;
1276 :
1277 1449 : if (namelen > dir_data_length) {
1278 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1279 : }
1280 :
1281 1449 : ret = pull_string_talloc(finfo,
1282 : dir_data,
1283 : FLAGS2_UNICODE_STRINGS,
1284 : &finfo->name,
1285 : dir_data,
1286 : namelen,
1287 : STR_UNICODE);
1288 1449 : if (ret == (size_t)-1) {
1289 : /* Bad conversion. */
1290 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1291 : }
1292 :
1293 1449 : if (finfo->name == NULL) {
1294 : /* Bad conversion. */
1295 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1296 : }
1297 :
1298 1449 : *next_offset = _next_offset;
1299 1449 : return NT_STATUS_OK;
1300 : }
1301 :
1302 : /***************************************************************
1303 : Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply.
1304 : ***************************************************************/
1305 :
1306 54559 : static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data,
1307 : uint32_t dir_data_length,
1308 : struct file_info *finfo,
1309 : uint32_t *next_offset)
1310 : {
1311 54559 : size_t namelen = 0;
1312 54559 : size_t slen = 0;
1313 54559 : size_t ret = 0;
1314 :
1315 54559 : if (dir_data_length < 4) {
1316 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1317 : }
1318 :
1319 54559 : *next_offset = IVAL(dir_data, 0);
1320 :
1321 54559 : if (*next_offset > dir_data_length) {
1322 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1323 : }
1324 :
1325 54559 : if (*next_offset != 0) {
1326 : /* Ensure we only read what in this record. */
1327 46246 : dir_data_length = *next_offset;
1328 : }
1329 :
1330 54559 : if (dir_data_length < 105) {
1331 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1332 : }
1333 :
1334 54559 : finfo->btime_ts = interpret_long_date(BVAL(dir_data, 8));
1335 54559 : finfo->atime_ts = interpret_long_date(BVAL(dir_data, 16));
1336 54559 : finfo->mtime_ts = interpret_long_date(BVAL(dir_data, 24));
1337 54559 : finfo->ctime_ts = interpret_long_date(BVAL(dir_data, 32));
1338 54559 : finfo->size = BVAL(dir_data + 40, 0);
1339 54559 : finfo->allocated_size = BVAL(dir_data + 48, 0);
1340 54559 : finfo->attr = IVAL(dir_data + 56, 0);
1341 54559 : finfo->ino = BVAL(dir_data + 96, 0);
1342 54559 : namelen = IVAL(dir_data + 60,0);
1343 54559 : if (namelen > (dir_data_length - 104)) {
1344 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1345 : }
1346 54559 : finfo->reparse_tag = IVAL(dir_data + 64, 0);
1347 54559 : slen = CVAL(dir_data + 68, 0);
1348 54559 : if (slen > 24) {
1349 0 : return NT_STATUS_INFO_LENGTH_MISMATCH;
1350 : }
1351 54559 : ret = pull_string_talloc(finfo,
1352 : dir_data,
1353 : FLAGS2_UNICODE_STRINGS,
1354 : &finfo->short_name,
1355 54559 : dir_data + 70,
1356 : slen,
1357 : STR_UNICODE);
1358 54559 : if (ret == (size_t)-1) {
1359 : /* Bad conversion. */
1360 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1361 : }
1362 :
1363 54559 : ret = pull_string_talloc(finfo,
1364 : dir_data,
1365 : FLAGS2_UNICODE_STRINGS,
1366 : &finfo->name,
1367 54559 : dir_data + 104,
1368 : namelen,
1369 : STR_UNICODE);
1370 54559 : if (ret == (size_t)-1) {
1371 : /* Bad conversion. */
1372 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1373 : }
1374 :
1375 54559 : if (finfo->name == NULL) {
1376 : /* Bad conversion. */
1377 2 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
1378 : }
1379 :
1380 54557 : return NT_STATUS_OK;
1381 : }
1382 :
1383 : /*******************************************************************
1384 : Given a filename - get its directory name
1385 : ********************************************************************/
1386 :
1387 8826 : static bool windows_parent_dirname(TALLOC_CTX *mem_ctx,
1388 : const char *dir,
1389 : char **parent,
1390 : const char **name)
1391 : {
1392 0 : char *p;
1393 0 : ptrdiff_t len;
1394 :
1395 8826 : p = strrchr_m(dir, '\\'); /* Find final '\\', if any */
1396 :
1397 8826 : if (p == NULL) {
1398 94 : if (!(*parent = talloc_strdup(mem_ctx, "\\"))) {
1399 0 : return false;
1400 : }
1401 94 : if (name) {
1402 94 : *name = dir;
1403 : }
1404 94 : return true;
1405 : }
1406 :
1407 8732 : len = p-dir;
1408 :
1409 8732 : if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) {
1410 0 : return false;
1411 : }
1412 8732 : (*parent)[len] = '\0';
1413 :
1414 8732 : if (name) {
1415 8732 : *name = p+1;
1416 : }
1417 8732 : return true;
1418 : }
1419 :
1420 : struct cli_smb2_list_dir_data {
1421 : uint8_t *data;
1422 : uint32_t length;
1423 : };
1424 :
1425 : struct cli_smb2_list_state {
1426 : struct tevent_context *ev;
1427 : struct cli_state *cli;
1428 : const char *mask;
1429 :
1430 : uint16_t fnum;
1431 :
1432 : NTSTATUS status;
1433 : struct cli_smb2_list_dir_data *response;
1434 : uint32_t offset;
1435 : unsigned int info_level;
1436 : };
1437 :
1438 : static void cli_smb2_list_opened(struct tevent_req *subreq);
1439 : static void cli_smb2_list_done(struct tevent_req *subreq);
1440 : static void cli_smb2_list_closed(struct tevent_req *subreq);
1441 :
1442 8826 : struct tevent_req *cli_smb2_list_send(
1443 : TALLOC_CTX *mem_ctx,
1444 : struct tevent_context *ev,
1445 : struct cli_state *cli,
1446 : const char *pathname,
1447 : unsigned int info_level)
1448 : {
1449 8826 : struct tevent_req *req = NULL, *subreq = NULL;
1450 8826 : struct cli_smb2_list_state *state = NULL;
1451 8826 : char *parent = NULL;
1452 0 : bool ok;
1453 8826 : struct smb2_create_blobs *in_cblobs = NULL;
1454 :
1455 8826 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state);
1456 8826 : if (req == NULL) {
1457 0 : return NULL;
1458 : }
1459 8826 : state->ev = ev;
1460 8826 : state->cli = cli;
1461 8826 : state->status = NT_STATUS_OK;
1462 8826 : state->info_level = info_level;
1463 :
1464 8826 : ok = windows_parent_dirname(state, pathname, &parent, &state->mask);
1465 8826 : if (!ok) {
1466 0 : tevent_req_oom(req);
1467 0 : return tevent_req_post(req, ev);
1468 : }
1469 :
1470 8826 : if (smbXcli_conn_have_posix(cli->conn) &&
1471 : info_level == SMB2_FIND_POSIX_INFORMATION)
1472 : {
1473 0 : NTSTATUS status;
1474 :
1475 : /* The mode MUST be 0 when opening an existing file/dir, and
1476 : * will be ignored by the server.
1477 : */
1478 6 : uint8_t linear_mode[4] = { 0 };
1479 6 : DATA_BLOB blob = { .data=linear_mode,
1480 : .length=sizeof(linear_mode) };
1481 :
1482 6 : in_cblobs = talloc_zero(mem_ctx, struct smb2_create_blobs);
1483 6 : if (in_cblobs == NULL) {
1484 0 : return NULL;
1485 : }
1486 :
1487 6 : status = smb2_create_blob_add(in_cblobs, in_cblobs,
1488 : SMB2_CREATE_TAG_POSIX, blob);
1489 6 : if (tevent_req_nterror(req, status)) {
1490 0 : tevent_req_nterror(req, status);
1491 0 : return tevent_req_post(req, ev);
1492 : }
1493 : }
1494 :
1495 8826 : subreq = cli_smb2_create_fnum_send(
1496 : state, /* mem_ctx */
1497 : ev, /* ev */
1498 : cli, /* cli */
1499 : parent, /* fname */
1500 8826 : (struct cli_smb2_create_flags){0}, /* create_flags */
1501 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
1502 : SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */
1503 : FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */
1504 : FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */
1505 : FILE_OPEN, /* create_disposition */
1506 : FILE_DIRECTORY_FILE, /* create_options */
1507 : in_cblobs); /* in_cblobs */
1508 8826 : TALLOC_FREE(in_cblobs);
1509 8826 : if (tevent_req_nomem(subreq, req)) {
1510 0 : return tevent_req_post(req, ev);
1511 : }
1512 8826 : tevent_req_set_callback(subreq, cli_smb2_list_opened, req);
1513 8826 : return req;
1514 : }
1515 :
1516 8826 : static void cli_smb2_list_opened(struct tevent_req *subreq)
1517 : {
1518 8826 : struct tevent_req *req = tevent_req_callback_data(
1519 : subreq, struct tevent_req);
1520 8826 : struct cli_smb2_list_state *state = tevent_req_data(
1521 : req, struct cli_smb2_list_state);
1522 0 : NTSTATUS status;
1523 :
1524 8826 : status = cli_smb2_create_fnum_recv(
1525 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
1526 8826 : TALLOC_FREE(subreq);
1527 8826 : if (tevent_req_nterror(req, status)) {
1528 280 : return;
1529 : }
1530 :
1531 : /*
1532 : * Make our caller get back to us via cli_smb2_list_recv(),
1533 : * triggering the smb2_query_directory_send()
1534 : */
1535 8546 : tevent_req_defer_callback(req, state->ev);
1536 8546 : tevent_req_notify_callback(req);
1537 : }
1538 :
1539 16863 : static void cli_smb2_list_done(struct tevent_req *subreq)
1540 : {
1541 16863 : struct tevent_req *req = tevent_req_callback_data(
1542 : subreq, struct tevent_req);
1543 16863 : struct cli_smb2_list_state *state = tevent_req_data(
1544 : req, struct cli_smb2_list_state);
1545 16863 : struct cli_smb2_list_dir_data *response = NULL;
1546 :
1547 16863 : response = talloc(state, struct cli_smb2_list_dir_data);
1548 16863 : if (tevent_req_nomem(response, req)) {
1549 0 : return;
1550 : }
1551 :
1552 16863 : state->status = smb2cli_query_directory_recv(
1553 : subreq, response, &response->data, &response->length);
1554 16863 : TALLOC_FREE(subreq);
1555 :
1556 16863 : if (NT_STATUS_IS_OK(state->status)) {
1557 8319 : state->response = response;
1558 8319 : state->offset = 0;
1559 :
1560 8319 : tevent_req_defer_callback(req, state->ev);
1561 8319 : tevent_req_notify_callback(req);
1562 8319 : return;
1563 : }
1564 :
1565 8544 : TALLOC_FREE(response);
1566 :
1567 8544 : subreq = cli_smb2_close_fnum_send(state,
1568 : state->ev,
1569 : state->cli,
1570 8544 : state->fnum,
1571 : 0);
1572 8544 : if (tevent_req_nomem(subreq, req)) {
1573 0 : return;
1574 : }
1575 8544 : tevent_req_set_callback(subreq, cli_smb2_list_closed, req);
1576 : }
1577 :
1578 8544 : static void cli_smb2_list_closed(struct tevent_req *subreq)
1579 : {
1580 8544 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
1581 8544 : tevent_req_simple_finish_ntstatus(subreq, status);
1582 8544 : }
1583 :
1584 : /*
1585 : * Return the next finfo directory.
1586 : *
1587 : * This parses the blob returned from QUERY_DIRECTORY step by step. If
1588 : * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns
1589 : * NT_STATUS_RETRY, which will then trigger the caller again when the
1590 : * QUERY_DIRECTORY has returned with another buffer. This way we
1591 : * guarantee that no asynchronous request is open after this call
1592 : * returns an entry, so that other synchronous requests can be issued
1593 : * on the same connection while the directory listing proceeds.
1594 : */
1595 81695 : NTSTATUS cli_smb2_list_recv(
1596 : struct tevent_req *req,
1597 : TALLOC_CTX *mem_ctx,
1598 : struct file_info **pfinfo)
1599 : {
1600 81695 : struct cli_smb2_list_state *state = tevent_req_data(
1601 : req, struct cli_smb2_list_state);
1602 81695 : struct cli_smb2_list_dir_data *response = NULL;
1603 81695 : struct file_info *finfo = NULL;
1604 0 : NTSTATUS status;
1605 81695 : uint32_t next_offset = 0;
1606 0 : bool in_progress;
1607 :
1608 81695 : in_progress = tevent_req_is_in_progress(req);
1609 :
1610 81695 : if (!in_progress) {
1611 8824 : if (!tevent_req_is_nterror(req, &status)) {
1612 8544 : status = NT_STATUS_NO_MORE_FILES;
1613 : }
1614 8824 : goto fail;
1615 : }
1616 :
1617 72871 : response = state->response;
1618 72871 : if (response == NULL) {
1619 16863 : struct tevent_req *subreq = NULL;
1620 16863 : struct cli_state *cli = state->cli;
1621 16863 : struct smb2_hnd *ph = NULL;
1622 0 : uint32_t max_trans, max_avail_len;
1623 0 : bool ok;
1624 :
1625 16863 : if (!NT_STATUS_IS_OK(state->status)) {
1626 0 : status = state->status;
1627 0 : goto fail;
1628 : }
1629 :
1630 16863 : status = map_fnum_to_smb2_handle(cli, state->fnum, &ph);
1631 16863 : if (!NT_STATUS_IS_OK(status)) {
1632 0 : goto fail;
1633 : }
1634 :
1635 16863 : max_trans = smb2cli_conn_max_trans_size(cli->conn);
1636 16863 : ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len);
1637 16863 : if (ok) {
1638 16863 : max_trans = MIN(max_trans, max_avail_len);
1639 : }
1640 :
1641 16863 : subreq = smb2cli_query_directory_send(
1642 : state, /* mem_ctx */
1643 : state->ev, /* ev */
1644 : cli->conn, /* conn */
1645 16863 : cli->timeout, /* timeout_msec */
1646 : cli->smb2.session, /* session */
1647 : cli->smb2.tcon, /* tcon */
1648 16863 : state->info_level, /* level */
1649 : 0, /* flags */
1650 : 0, /* file_index */
1651 16863 : ph->fid_persistent, /* fid_persistent */
1652 16863 : ph->fid_volatile, /* fid_volatile */
1653 : state->mask, /* mask */
1654 : max_trans); /* outbuf_len */
1655 16863 : if (subreq == NULL) {
1656 0 : status = NT_STATUS_NO_MEMORY;
1657 0 : goto fail;
1658 : }
1659 16863 : tevent_req_set_callback(subreq, cli_smb2_list_done, req);
1660 16863 : return NT_STATUS_RETRY;
1661 : }
1662 :
1663 56008 : SMB_ASSERT(response->length > state->offset);
1664 :
1665 56008 : finfo = talloc_zero(mem_ctx, struct file_info);
1666 56008 : if (finfo == NULL) {
1667 0 : status = NT_STATUS_NO_MEMORY;
1668 0 : goto fail;
1669 : }
1670 :
1671 56008 : if (state->info_level == SMB2_FIND_POSIX_INFORMATION) {
1672 1449 : status = parse_finfo_posix_info(
1673 1449 : response->data + state->offset,
1674 1449 : response->length - state->offset,
1675 : finfo,
1676 : &next_offset);
1677 : } else {
1678 54559 : status = parse_finfo_id_both_directory_info(
1679 54559 : response->data + state->offset,
1680 54559 : response->length - state->offset,
1681 : finfo,
1682 : &next_offset);
1683 : }
1684 56008 : if (!NT_STATUS_IS_OK(status)) {
1685 2 : goto fail;
1686 : }
1687 :
1688 56006 : status = is_bad_finfo_name(state->cli, finfo);
1689 56006 : if (!NT_STATUS_IS_OK(status)) {
1690 0 : goto fail;
1691 : }
1692 :
1693 : /*
1694 : * parse_finfo_id_both_directory_info() checks for overflow,
1695 : * no need to check again here.
1696 : */
1697 56006 : state->offset += next_offset;
1698 :
1699 56006 : if (next_offset == 0) {
1700 8317 : TALLOC_FREE(state->response);
1701 : }
1702 :
1703 56006 : tevent_req_defer_callback(req, state->ev);
1704 56006 : tevent_req_notify_callback(req);
1705 :
1706 56006 : *pfinfo = finfo;
1707 56006 : return NT_STATUS_OK;
1708 :
1709 8826 : fail:
1710 8826 : TALLOC_FREE(finfo);
1711 8826 : tevent_req_received(req);
1712 8826 : return status;
1713 : }
1714 :
1715 : /***************************************************************
1716 : Wrapper that allows SMB2 to query a path info (basic level).
1717 : Synchronous only.
1718 : ***************************************************************/
1719 :
1720 8126 : NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli,
1721 : const char *name,
1722 : SMB_STRUCT_STAT *sbuf,
1723 : uint32_t *attributes)
1724 : {
1725 0 : NTSTATUS status;
1726 0 : struct smb_create_returns cr;
1727 8126 : uint16_t fnum = 0xffff;
1728 8126 : size_t namelen = strlen(name);
1729 :
1730 8126 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1731 : /*
1732 : * Can't use sync call while an async call is in flight
1733 : */
1734 0 : return NT_STATUS_INVALID_PARAMETER;
1735 : }
1736 :
1737 : /* SMB2 is pickier about pathnames. Ensure it doesn't
1738 : end in a '\' */
1739 8126 : if (namelen > 0 && name[namelen-1] == '\\') {
1740 168 : char *modname = talloc_strndup(talloc_tos(), name, namelen-1);
1741 168 : if (modname == NULL) {
1742 0 : return NT_STATUS_NO_MEMORY;
1743 : }
1744 168 : name = modname;
1745 : }
1746 :
1747 : /* This is commonly used as a 'cd'. Try qpathinfo on
1748 : a directory handle first. */
1749 :
1750 8126 : status = cli_smb2_create_fnum(cli,
1751 : name,
1752 8126 : (struct cli_smb2_create_flags){0},
1753 : SMB2_IMPERSONATION_IMPERSONATION,
1754 : FILE_READ_ATTRIBUTES, /* desired_access */
1755 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
1756 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1757 : FILE_OPEN, /* create_disposition */
1758 : FILE_DIRECTORY_FILE, /* create_options */
1759 : NULL,
1760 : &fnum,
1761 : &cr,
1762 : NULL,
1763 : NULL);
1764 :
1765 8126 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) {
1766 : /* Maybe a file ? */
1767 1808 : status = cli_smb2_create_fnum(cli,
1768 : name,
1769 1808 : (struct cli_smb2_create_flags){0},
1770 : SMB2_IMPERSONATION_IMPERSONATION,
1771 : FILE_READ_ATTRIBUTES, /* desired_access */
1772 : 0, /* file attributes */
1773 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1774 : FILE_OPEN, /* create_disposition */
1775 : 0, /* create_options */
1776 : NULL,
1777 : &fnum,
1778 : &cr,
1779 : NULL,
1780 : NULL);
1781 : }
1782 :
1783 8126 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1784 : /* Maybe a reparse point ? */
1785 0 : status = cli_smb2_create_fnum(cli,
1786 : name,
1787 0 : (struct cli_smb2_create_flags){0},
1788 : SMB2_IMPERSONATION_IMPERSONATION,
1789 : FILE_READ_ATTRIBUTES, /* desired_access */
1790 : 0, /* file attributes */
1791 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
1792 : FILE_OPEN, /* create_disposition */
1793 : FILE_OPEN_REPARSE_POINT, /* create_options */
1794 : NULL,
1795 : &fnum,
1796 : &cr,
1797 : NULL,
1798 : NULL);
1799 : }
1800 :
1801 8126 : if (!NT_STATUS_IS_OK(status)) {
1802 4068 : return status;
1803 : }
1804 :
1805 4058 : status = cli_smb2_close_fnum(cli, fnum);
1806 :
1807 4058 : ZERO_STRUCTP(sbuf);
1808 :
1809 4058 : sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time);
1810 4058 : sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time);
1811 4058 : sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time);
1812 4058 : sbuf->st_ex_size = cr.end_of_file;
1813 4058 : *attributes = cr.file_attributes;
1814 :
1815 4058 : return status;
1816 : }
1817 :
1818 : struct cli_smb2_query_info_fnum_state {
1819 : DATA_BLOB outbuf;
1820 : };
1821 :
1822 : static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq);
1823 :
1824 11013 : struct tevent_req *cli_smb2_query_info_fnum_send(
1825 : TALLOC_CTX *mem_ctx,
1826 : struct tevent_context *ev,
1827 : struct cli_state *cli,
1828 : uint16_t fnum,
1829 : uint8_t in_info_type,
1830 : uint8_t in_info_class,
1831 : uint32_t in_max_output_length,
1832 : const DATA_BLOB *in_input_buffer,
1833 : uint32_t in_additional_info,
1834 : uint32_t in_flags)
1835 : {
1836 11013 : struct tevent_req *req = NULL, *subreq = NULL;
1837 11013 : struct cli_smb2_query_info_fnum_state *state = NULL;
1838 11013 : struct smb2_hnd *ph = NULL;
1839 0 : NTSTATUS status;
1840 :
1841 11013 : req = tevent_req_create(
1842 : mem_ctx, &state, struct cli_smb2_query_info_fnum_state);
1843 11013 : if (req == NULL) {
1844 0 : return req;
1845 : }
1846 :
1847 11013 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
1848 11013 : if (tevent_req_nterror(req, status)) {
1849 6 : return tevent_req_post(req, ev);
1850 : }
1851 :
1852 11007 : subreq = smb2cli_query_info_send(
1853 : state,
1854 : ev,
1855 : cli->conn,
1856 11007 : cli->timeout,
1857 : cli->smb2.session,
1858 : cli->smb2.tcon,
1859 : in_info_type,
1860 : in_info_class,
1861 : in_max_output_length,
1862 : in_input_buffer,
1863 : in_additional_info,
1864 : in_flags,
1865 11007 : ph->fid_persistent,
1866 11007 : ph->fid_volatile);
1867 11007 : if (tevent_req_nomem(subreq, req)) {
1868 0 : return tevent_req_post(req, ev);
1869 : }
1870 11007 : tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req);
1871 11007 : return req;
1872 : }
1873 :
1874 11007 : static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq)
1875 : {
1876 11007 : struct tevent_req *req = tevent_req_callback_data(
1877 : subreq, struct tevent_req);
1878 11007 : struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1879 : req, struct cli_smb2_query_info_fnum_state);
1880 0 : DATA_BLOB outbuf;
1881 0 : NTSTATUS status;
1882 :
1883 11007 : status = smb2cli_query_info_recv(subreq, state, &outbuf);
1884 11007 : TALLOC_FREE(subreq);
1885 11007 : if (tevent_req_nterror(req, status)) {
1886 39 : return;
1887 : }
1888 :
1889 : /*
1890 : * We have to dup the memory here because outbuf.data is not
1891 : * returned as a talloc object by smb2cli_query_info_recv.
1892 : * It's a pointer into the received buffer.
1893 : */
1894 10968 : state->outbuf = data_blob_dup_talloc(state, outbuf);
1895 :
1896 21816 : if ((outbuf.length != 0) &&
1897 10848 : tevent_req_nomem(state->outbuf.data, req)) {
1898 0 : return;
1899 : }
1900 10968 : tevent_req_done(req);
1901 : }
1902 :
1903 11013 : NTSTATUS cli_smb2_query_info_fnum_recv(
1904 : struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf)
1905 : {
1906 11013 : struct cli_smb2_query_info_fnum_state *state = tevent_req_data(
1907 : req, struct cli_smb2_query_info_fnum_state);
1908 0 : NTSTATUS status;
1909 :
1910 11013 : if (tevent_req_is_nterror(req, &status)) {
1911 45 : return status;
1912 : }
1913 10968 : *outbuf = (DATA_BLOB) {
1914 10968 : .data = talloc_move(mem_ctx, &state->outbuf.data),
1915 10968 : .length = state->outbuf.length,
1916 : };
1917 10968 : tevent_req_received(req);
1918 10968 : return NT_STATUS_OK;
1919 : }
1920 :
1921 1217 : NTSTATUS cli_smb2_query_info_fnum(
1922 : struct cli_state *cli,
1923 : uint16_t fnum,
1924 : uint8_t in_info_type,
1925 : uint8_t in_info_class,
1926 : uint32_t in_max_output_length,
1927 : const DATA_BLOB *in_input_buffer,
1928 : uint32_t in_additional_info,
1929 : uint32_t in_flags,
1930 : TALLOC_CTX *mem_ctx,
1931 : DATA_BLOB *outbuf)
1932 : {
1933 1217 : TALLOC_CTX *frame = talloc_stackframe();
1934 1217 : struct tevent_context *ev = NULL;
1935 1217 : struct tevent_req *req = NULL;
1936 1217 : NTSTATUS status = NT_STATUS_NO_MEMORY;
1937 0 : bool ok;
1938 :
1939 1217 : if (smbXcli_conn_has_async_calls(cli->conn)) {
1940 : /*
1941 : * Can't use sync call while an async call is in flight
1942 : */
1943 0 : status = NT_STATUS_INVALID_PARAMETER;
1944 0 : goto fail;
1945 : }
1946 1217 : ev = samba_tevent_context_init(frame);
1947 1217 : if (ev == NULL) {
1948 0 : goto fail;
1949 : }
1950 1217 : req = cli_smb2_query_info_fnum_send(
1951 : frame,
1952 : ev,
1953 : cli,
1954 : fnum,
1955 : in_info_type,
1956 : in_info_class,
1957 : in_max_output_length,
1958 : in_input_buffer,
1959 : in_additional_info,
1960 : in_flags);
1961 1217 : if (req == NULL) {
1962 0 : goto fail;
1963 : }
1964 1217 : ok = tevent_req_poll_ntstatus(req, ev, &status);
1965 1217 : if (!ok) {
1966 0 : goto fail;
1967 : }
1968 1217 : status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf);
1969 1217 : fail:
1970 1217 : TALLOC_FREE(frame);
1971 1217 : return status;
1972 : }
1973 :
1974 : /***************************************************************
1975 : Helper function for pathname operations.
1976 : ***************************************************************/
1977 :
1978 : struct get_fnum_from_path_state {
1979 : struct tevent_context *ev;
1980 : struct cli_state *cli;
1981 : const char *name;
1982 : uint32_t desired_access;
1983 : uint16_t fnum;
1984 : };
1985 :
1986 : static void get_fnum_from_path_opened_file(struct tevent_req *subreq);
1987 : static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq);
1988 : static void get_fnum_from_path_opened_dir(struct tevent_req *subreq);
1989 :
1990 6395 : static struct tevent_req *get_fnum_from_path_send(
1991 : TALLOC_CTX *mem_ctx,
1992 : struct tevent_context *ev,
1993 : struct cli_state *cli,
1994 : const char *name,
1995 : uint32_t desired_access)
1996 : {
1997 6395 : struct tevent_req *req = NULL, *subreq = NULL;
1998 6395 : struct get_fnum_from_path_state *state = NULL;
1999 6395 : size_t namelen = strlen(name);
2000 :
2001 6395 : req = tevent_req_create(
2002 : mem_ctx, &state, struct get_fnum_from_path_state);
2003 6395 : if (req == NULL) {
2004 0 : return NULL;
2005 : }
2006 6395 : state->ev = ev;
2007 6395 : state->cli = cli;
2008 6395 : state->name = name;
2009 6395 : state->desired_access = desired_access;
2010 :
2011 : /*
2012 : * SMB2 is pickier about pathnames. Ensure it doesn't end in a
2013 : * '\'
2014 : */
2015 6395 : if (namelen > 0 && name[namelen-1] == '\\') {
2016 146 : state->name = talloc_strndup(state, name, namelen-1);
2017 146 : if (tevent_req_nomem(state->name, req)) {
2018 0 : return tevent_req_post(req, ev);
2019 : }
2020 : }
2021 :
2022 6395 : subreq = cli_smb2_create_fnum_send(
2023 : state, /* mem_ctx, */
2024 : ev, /* ev */
2025 : cli, /* cli */
2026 6395 : state->name, /* fname */
2027 6395 : (struct cli_smb2_create_flags){0}, /* create_flags */
2028 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */
2029 : desired_access, /* desired_access */
2030 : 0, /* file_attributes */
2031 : FILE_SHARE_READ|
2032 : FILE_SHARE_WRITE|
2033 : FILE_SHARE_DELETE, /* share_access */
2034 : FILE_OPEN, /* create_disposition */
2035 : 0, /* create_options */
2036 : NULL); /* in_cblobs */
2037 6395 : if (tevent_req_nomem(subreq, req)) {
2038 0 : return tevent_req_post(req, ev);
2039 : }
2040 6395 : tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req);
2041 6395 : return req;
2042 : }
2043 :
2044 6395 : static void get_fnum_from_path_opened_file(struct tevent_req *subreq)
2045 : {
2046 6395 : struct tevent_req *req = tevent_req_callback_data(
2047 : subreq, struct tevent_req);
2048 6395 : struct get_fnum_from_path_state *state = tevent_req_data(
2049 : req, struct get_fnum_from_path_state);
2050 0 : NTSTATUS status;
2051 :
2052 6395 : status = cli_smb2_create_fnum_recv(
2053 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
2054 6395 : TALLOC_FREE(subreq);
2055 :
2056 6395 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK) ||
2057 6395 : NT_STATUS_EQUAL(status, NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED)) {
2058 : /*
2059 : * Naive option to match our SMB1 code. Assume the
2060 : * symlink path that tripped us up was the last
2061 : * component and try again. Eventually we will have to
2062 : * deal with the returned path unprocessed component. JRA.
2063 : */
2064 0 : subreq = cli_smb2_create_fnum_send(
2065 : state, /* mem_ctx, */
2066 : state->ev, /* ev */
2067 : state->cli, /* cli */
2068 : state->name, /* fname */
2069 0 : (struct cli_smb2_create_flags){0}, /* create_flags */
2070 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2071 : state->desired_access, /* desired_access */
2072 : 0, /* file_attributes */
2073 : FILE_SHARE_READ|
2074 : FILE_SHARE_WRITE|
2075 : FILE_SHARE_DELETE, /* share_access */
2076 : FILE_OPEN, /* create_disposition */
2077 : FILE_OPEN_REPARSE_POINT, /* create_options */
2078 : NULL); /* in_cblobs */
2079 0 : if (tevent_req_nomem(subreq, req)) {
2080 0 : return;
2081 : }
2082 0 : tevent_req_set_callback(
2083 : subreq, get_fnum_from_path_opened_reparse, req);
2084 0 : return;
2085 : }
2086 :
2087 6395 : if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) {
2088 0 : subreq = cli_smb2_create_fnum_send(
2089 : state, /* mem_ctx, */
2090 : state->ev, /* ev */
2091 : state->cli, /* cli */
2092 : state->name, /* fname */
2093 0 : (struct cli_smb2_create_flags){0}, /* create_flags */
2094 : SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */
2095 : state->desired_access, /* desired_access */
2096 : 0, /* file_attributes */
2097 : FILE_SHARE_READ|
2098 : FILE_SHARE_WRITE|
2099 : FILE_SHARE_DELETE, /* share_access */
2100 : FILE_OPEN, /* create_disposition */
2101 : FILE_DIRECTORY_FILE, /* create_options */
2102 : NULL); /* in_cblobs */
2103 0 : if (tevent_req_nomem(subreq, req)) {
2104 0 : return;
2105 : }
2106 0 : tevent_req_set_callback(
2107 : subreq, get_fnum_from_path_opened_dir, req);
2108 0 : return;
2109 : }
2110 :
2111 6395 : if (tevent_req_nterror(req, status)) {
2112 616 : return;
2113 : }
2114 5779 : tevent_req_done(req);
2115 : }
2116 :
2117 0 : static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq)
2118 : {
2119 0 : struct tevent_req *req = tevent_req_callback_data(
2120 : subreq, struct tevent_req);
2121 0 : struct get_fnum_from_path_state *state = tevent_req_data(
2122 : req, struct get_fnum_from_path_state);
2123 0 : NTSTATUS status = cli_smb2_create_fnum_recv(
2124 : subreq, &state->fnum, NULL, NULL, NULL, NULL);
2125 0 : tevent_req_simple_finish_ntstatus(subreq, status);
2126 0 : }
2127 :
2128 0 : static void get_fnum_from_path_opened_dir(struct tevent_req *subreq)
2129 : {
2130 : /* Abstraction violation, but these two are just the same... */
2131 0 : get_fnum_from_path_opened_reparse(subreq);
2132 0 : }
2133 :
2134 6395 : static NTSTATUS get_fnum_from_path_recv(
2135 : struct tevent_req *req, uint16_t *pfnum)
2136 : {
2137 6395 : struct get_fnum_from_path_state *state = tevent_req_data(
2138 : req, struct get_fnum_from_path_state);
2139 6395 : NTSTATUS status = NT_STATUS_OK;
2140 :
2141 6395 : if (!tevent_req_is_nterror(req, &status)) {
2142 5779 : *pfnum = state->fnum;
2143 : }
2144 6395 : tevent_req_received(req);
2145 6395 : return status;
2146 : }
2147 :
2148 1296 : static NTSTATUS get_fnum_from_path(struct cli_state *cli,
2149 : const char *name,
2150 : uint32_t desired_access,
2151 : uint16_t *pfnum)
2152 : {
2153 1296 : TALLOC_CTX *frame = talloc_stackframe();
2154 1296 : struct tevent_context *ev = NULL;
2155 1296 : struct tevent_req *req = NULL;
2156 1296 : NTSTATUS status = NT_STATUS_NO_MEMORY;
2157 :
2158 1296 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2159 0 : status = NT_STATUS_INVALID_PARAMETER;
2160 0 : goto fail;
2161 : }
2162 1296 : ev = samba_tevent_context_init(frame);
2163 1296 : if (ev == NULL) {
2164 0 : goto fail;
2165 : }
2166 1296 : req = get_fnum_from_path_send(frame, ev, cli, name, desired_access);
2167 1296 : if (req == NULL) {
2168 0 : goto fail;
2169 : }
2170 1296 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
2171 0 : goto fail;
2172 : }
2173 1296 : status = get_fnum_from_path_recv(req, pfnum);
2174 1296 : fail:
2175 1296 : TALLOC_FREE(frame);
2176 1296 : return status;
2177 : }
2178 :
2179 : struct cli_smb2_qpathinfo_state {
2180 : struct tevent_context *ev;
2181 : struct cli_state *cli;
2182 : const char *fname;
2183 : uint16_t fnum;
2184 : uint16_t level;
2185 : uint32_t min_rdata;
2186 : uint32_t max_rdata;
2187 :
2188 : NTSTATUS status;
2189 : DATA_BLOB out;
2190 : };
2191 :
2192 : static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq);
2193 : static void cli_smb2_qpathinfo_done(struct tevent_req *subreq);
2194 : static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq);
2195 :
2196 4874 : struct tevent_req *cli_smb2_qpathinfo_send(TALLOC_CTX *mem_ctx,
2197 : struct tevent_context *ev,
2198 : struct cli_state *cli,
2199 : const char *fname,
2200 : uint16_t level,
2201 : uint32_t min_rdata,
2202 : uint32_t max_rdata)
2203 : {
2204 4874 : struct tevent_req *req = NULL, *subreq = NULL;
2205 4874 : struct cli_smb2_qpathinfo_state *state = NULL;
2206 :
2207 4874 : req = tevent_req_create(mem_ctx,
2208 : &state,
2209 : struct cli_smb2_qpathinfo_state);
2210 4874 : if (req == NULL) {
2211 0 : return NULL;
2212 : }
2213 4874 : state->ev = ev;
2214 4874 : state->cli = cli;
2215 4874 : state->level = level;
2216 4874 : state->min_rdata = min_rdata;
2217 4874 : state->max_rdata = max_rdata;
2218 :
2219 4874 : subreq = get_fnum_from_path_send(state,
2220 : ev,
2221 : cli,
2222 : fname,
2223 : FILE_READ_ATTRIBUTES);
2224 4874 : if (tevent_req_nomem(subreq, req)) {
2225 0 : return tevent_req_post(req, ev);
2226 : }
2227 4874 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_opened, req);
2228 4874 : return req;
2229 : }
2230 :
2231 4874 : static void cli_smb2_qpathinfo_opened(struct tevent_req *subreq)
2232 : {
2233 0 : struct tevent_req *req =
2234 4874 : tevent_req_callback_data(subreq, struct tevent_req);
2235 0 : struct cli_smb2_qpathinfo_state *state =
2236 4874 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2237 0 : NTSTATUS status;
2238 :
2239 4874 : status = get_fnum_from_path_recv(subreq, &state->fnum);
2240 4874 : TALLOC_FREE(subreq);
2241 4874 : if (tevent_req_nterror(req, status)) {
2242 611 : return;
2243 : }
2244 :
2245 4263 : subreq = cli_smb2_query_info_fnum_send(state,
2246 : state->ev,
2247 : state->cli,
2248 4263 : state->fnum,
2249 : 1, /* in_info_type */
2250 4263 : state->level,
2251 : state->max_rdata,
2252 : NULL, /* in_input_buffer */
2253 : 0, /* in_additional_info */
2254 : 0); /* in_flags */
2255 4263 : if (tevent_req_nomem(subreq, req)) {
2256 0 : return;
2257 : }
2258 4263 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_done, req);
2259 : }
2260 :
2261 4263 : static void cli_smb2_qpathinfo_done(struct tevent_req *subreq)
2262 : {
2263 0 : struct tevent_req *req =
2264 4263 : tevent_req_callback_data(subreq, struct tevent_req);
2265 0 : struct cli_smb2_qpathinfo_state *state =
2266 4263 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2267 :
2268 0 : state->status =
2269 4263 : cli_smb2_query_info_fnum_recv(subreq, state, &state->out);
2270 4263 : TALLOC_FREE(subreq);
2271 :
2272 4263 : if (NT_STATUS_IS_OK(state->status) &&
2273 4235 : (state->out.length < state->min_rdata)) {
2274 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2275 : }
2276 :
2277 4263 : subreq = cli_smb2_close_fnum_send(state,
2278 : state->ev,
2279 : state->cli,
2280 4263 : state->fnum,
2281 : 0);
2282 4263 : if (tevent_req_nomem(subreq, req)) {
2283 0 : return;
2284 : }
2285 4263 : tevent_req_set_callback(subreq, cli_smb2_qpathinfo_closed, req);
2286 : }
2287 :
2288 4263 : static void cli_smb2_qpathinfo_closed(struct tevent_req *subreq)
2289 : {
2290 0 : struct tevent_req *req =
2291 4263 : tevent_req_callback_data(subreq, struct tevent_req);
2292 0 : struct cli_smb2_qpathinfo_state *state =
2293 4263 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2294 0 : NTSTATUS status;
2295 :
2296 4263 : status = cli_smb2_close_fnum_recv(subreq);
2297 4263 : TALLOC_FREE(subreq);
2298 4263 : if (tevent_req_nterror(req, status)) {
2299 28 : return;
2300 : }
2301 4263 : if (tevent_req_nterror(req, state->status)) {
2302 28 : return;
2303 : }
2304 4235 : tevent_req_done(req);
2305 : }
2306 :
2307 4874 : NTSTATUS cli_smb2_qpathinfo_recv(struct tevent_req *req,
2308 : TALLOC_CTX *mem_ctx,
2309 : uint8_t **rdata,
2310 : uint32_t *num_rdata)
2311 : {
2312 0 : struct cli_smb2_qpathinfo_state *state =
2313 4874 : tevent_req_data(req, struct cli_smb2_qpathinfo_state);
2314 0 : NTSTATUS status;
2315 :
2316 4874 : if (tevent_req_is_nterror(req, &status)) {
2317 639 : return status;
2318 : }
2319 :
2320 4235 : *rdata = talloc_move(mem_ctx, &state->out.data);
2321 4235 : *num_rdata = state->out.length;
2322 4235 : tevent_req_received(req);
2323 4235 : return NT_STATUS_OK;
2324 : }
2325 :
2326 : /***************************************************************
2327 : Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on
2328 : a pathname.
2329 : Synchronous only.
2330 : ***************************************************************/
2331 :
2332 1296 : NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli,
2333 : const char *name,
2334 : uint8_t in_info_type,
2335 : uint8_t in_file_info_class,
2336 : const DATA_BLOB *p_in_data)
2337 : {
2338 0 : NTSTATUS status;
2339 1296 : uint16_t fnum = 0xffff;
2340 1296 : TALLOC_CTX *frame = talloc_stackframe();
2341 :
2342 1296 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2343 : /*
2344 : * Can't use sync call while an async call is in flight
2345 : */
2346 0 : status = NT_STATUS_INVALID_PARAMETER;
2347 0 : goto fail;
2348 : }
2349 :
2350 1296 : status = get_fnum_from_path(cli,
2351 : name,
2352 : FILE_WRITE_ATTRIBUTES,
2353 : &fnum);
2354 :
2355 1296 : if (!NT_STATUS_IS_OK(status)) {
2356 5 : goto fail;
2357 : }
2358 :
2359 1291 : status = cli_smb2_set_info_fnum(
2360 : cli,
2361 : fnum,
2362 : in_info_type,
2363 : in_file_info_class,
2364 : p_in_data, /* in_input_buffer */
2365 : 0); /* in_additional_info */
2366 1296 : fail:
2367 :
2368 1296 : if (fnum != 0xffff) {
2369 1291 : cli_smb2_close_fnum(cli, fnum);
2370 : }
2371 :
2372 1296 : cli->raw_status = status;
2373 :
2374 1296 : TALLOC_FREE(frame);
2375 1296 : return status;
2376 : }
2377 :
2378 :
2379 : /***************************************************************
2380 : Wrapper that allows SMB2 to set pathname attributes.
2381 : Synchronous only.
2382 : ***************************************************************/
2383 :
2384 1280 : NTSTATUS cli_smb2_setatr(struct cli_state *cli,
2385 : const char *name,
2386 : uint32_t attr,
2387 : time_t mtime)
2388 : {
2389 0 : uint8_t inbuf_store[40];
2390 1280 : DATA_BLOB inbuf = data_blob_null;
2391 :
2392 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2393 : level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2394 :
2395 1280 : inbuf.data = inbuf_store;
2396 1280 : inbuf.length = sizeof(inbuf_store);
2397 1280 : data_blob_clear(&inbuf);
2398 :
2399 : /*
2400 : * SMB1 uses attr == 0 to clear all attributes
2401 : * on a file (end up with FILE_ATTRIBUTE_NORMAL),
2402 : * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore
2403 : * request attribute change.
2404 : *
2405 : * SMB2 uses exactly the reverse. Unfortunately as the
2406 : * cli_setatr() ABI is exposed inside libsmbclient,
2407 : * we must make the SMB2 cli_smb2_setatr() call
2408 : * export the same ABI as the SMB1 cli_setatr()
2409 : * which calls it. This means reversing the sense
2410 : * of the requested attr argument if it's zero
2411 : * or FILE_ATTRIBUTE_NORMAL.
2412 : *
2413 : * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899
2414 : */
2415 :
2416 1280 : if (attr == 0) {
2417 116 : attr = FILE_ATTRIBUTE_NORMAL;
2418 1164 : } else if (attr == FILE_ATTRIBUTE_NORMAL) {
2419 676 : attr = 0;
2420 : }
2421 :
2422 1280 : SIVAL(inbuf.data, 32, attr);
2423 1280 : if (mtime != 0) {
2424 92 : put_long_date((char *)inbuf.data + 16,mtime);
2425 : }
2426 : /* Set all the other times to -1. */
2427 1280 : SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2428 1280 : SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL);
2429 1280 : SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL);
2430 :
2431 1280 : return cli_smb2_setpathinfo(
2432 : cli,
2433 : name,
2434 : SMB2_0_INFO_FILE, /* in_info_type */
2435 : FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2436 : &inbuf);
2437 : }
2438 :
2439 :
2440 : /***************************************************************
2441 : Wrapper that allows SMB2 to set file handle times.
2442 : Synchronous only.
2443 : ***************************************************************/
2444 :
2445 0 : NTSTATUS cli_smb2_setattrE(struct cli_state *cli,
2446 : uint16_t fnum,
2447 : time_t change_time,
2448 : time_t access_time,
2449 : time_t write_time)
2450 : {
2451 0 : uint8_t inbuf_store[40];
2452 0 : DATA_BLOB inbuf = data_blob_null;
2453 0 : NTSTATUS status;
2454 :
2455 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2456 : /*
2457 : * Can't use sync call while an async call is in flight
2458 : */
2459 0 : return NT_STATUS_INVALID_PARAMETER;
2460 : }
2461 :
2462 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
2463 : level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */
2464 :
2465 0 : inbuf.data = inbuf_store;
2466 0 : inbuf.length = sizeof(inbuf_store);
2467 0 : data_blob_clear(&inbuf);
2468 :
2469 0 : SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL);
2470 0 : if (change_time != 0) {
2471 0 : put_long_date((char *)inbuf.data + 24, change_time);
2472 : }
2473 0 : if (access_time != 0) {
2474 0 : put_long_date((char *)inbuf.data + 8, access_time);
2475 : }
2476 0 : if (write_time != 0) {
2477 0 : put_long_date((char *)inbuf.data + 16, write_time);
2478 : }
2479 :
2480 0 : status = cli_smb2_set_info_fnum(
2481 : cli,
2482 : fnum,
2483 : SMB2_0_INFO_FILE, /* in_info_type */
2484 : FSCC_FILE_BASIC_INFORMATION, /* in_file_info_class */
2485 : &inbuf, /* in_input_buffer */
2486 : 0); /* in_additional_info */
2487 0 : cli->raw_status = status;
2488 0 : return status;
2489 : }
2490 :
2491 : /***************************************************************
2492 : Wrapper that allows SMB2 to query disk attributes (size).
2493 : Synchronous only.
2494 : ***************************************************************/
2495 :
2496 1099 : NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
2497 : uint64_t *bsize, uint64_t *total, uint64_t *avail)
2498 : {
2499 0 : NTSTATUS status;
2500 1099 : uint16_t fnum = 0xffff;
2501 1099 : DATA_BLOB outbuf = data_blob_null;
2502 1099 : uint32_t sectors_per_unit = 0;
2503 1099 : uint32_t bytes_per_sector = 0;
2504 1099 : uint64_t total_size = 0;
2505 1099 : uint64_t size_free = 0;
2506 1099 : TALLOC_CTX *frame = talloc_stackframe();
2507 :
2508 1099 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2509 : /*
2510 : * Can't use sync call while an async call is in flight
2511 : */
2512 0 : status = NT_STATUS_INVALID_PARAMETER;
2513 0 : goto fail;
2514 : }
2515 :
2516 : /* First open the top level directory. */
2517 1099 : status = cli_smb2_create_fnum(cli,
2518 : path,
2519 1099 : (struct cli_smb2_create_flags){0},
2520 : SMB2_IMPERSONATION_IMPERSONATION,
2521 : FILE_READ_ATTRIBUTES, /* desired_access */
2522 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2523 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */
2524 : FILE_OPEN, /* create_disposition */
2525 : FILE_DIRECTORY_FILE, /* create_options */
2526 : NULL,
2527 : &fnum,
2528 : NULL,
2529 : NULL,
2530 : NULL);
2531 :
2532 1099 : if (!NT_STATUS_IS_OK(status)) {
2533 0 : goto fail;
2534 : }
2535 :
2536 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2537 : level 3 (SMB_FS_SIZE_INFORMATION). */
2538 :
2539 1099 : status = cli_smb2_query_info_fnum(
2540 : cli,
2541 : fnum,
2542 : 2, /* in_info_type */
2543 : 3, /* in_file_info_class */
2544 : 0xFFFF, /* in_max_output_length */
2545 : NULL, /* in_input_buffer */
2546 : 0, /* in_additional_info */
2547 : 0, /* in_flags */
2548 : frame,
2549 : &outbuf);
2550 1099 : if (!NT_STATUS_IS_OK(status)) {
2551 0 : goto fail;
2552 : }
2553 :
2554 : /* Parse the reply. */
2555 1099 : if (outbuf.length != 24) {
2556 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2557 0 : goto fail;
2558 : }
2559 :
2560 1099 : total_size = BVAL(outbuf.data, 0);
2561 1099 : size_free = BVAL(outbuf.data, 8);
2562 1099 : sectors_per_unit = IVAL(outbuf.data, 16);
2563 1099 : bytes_per_sector = IVAL(outbuf.data, 20);
2564 :
2565 1099 : if (bsize) {
2566 1099 : *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector;
2567 : }
2568 1099 : if (total) {
2569 1099 : *total = total_size;
2570 : }
2571 1099 : if (avail) {
2572 1099 : *avail = size_free;
2573 : }
2574 :
2575 1099 : status = NT_STATUS_OK;
2576 :
2577 1099 : fail:
2578 :
2579 1099 : if (fnum != 0xffff) {
2580 1099 : cli_smb2_close_fnum(cli, fnum);
2581 : }
2582 :
2583 1099 : cli->raw_status = status;
2584 :
2585 1099 : TALLOC_FREE(frame);
2586 1099 : return status;
2587 : }
2588 :
2589 : /***************************************************************
2590 : Wrapper that allows SMB2 to query file system sizes.
2591 : Synchronous only.
2592 : ***************************************************************/
2593 :
2594 0 : NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli,
2595 : uint64_t *total_allocation_units,
2596 : uint64_t *caller_allocation_units,
2597 : uint64_t *actual_allocation_units,
2598 : uint64_t *sectors_per_allocation_unit,
2599 : uint64_t *bytes_per_sector)
2600 : {
2601 0 : NTSTATUS status;
2602 0 : uint16_t fnum = 0xffff;
2603 0 : DATA_BLOB outbuf = data_blob_null;
2604 0 : TALLOC_CTX *frame = talloc_stackframe();
2605 :
2606 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2607 : /*
2608 : * Can't use sync call while an async call is in flight
2609 : */
2610 0 : status = NT_STATUS_INVALID_PARAMETER;
2611 0 : goto fail;
2612 : }
2613 :
2614 : /* First open the top level directory. */
2615 0 : status =
2616 0 : cli_smb2_create_fnum(cli, "",
2617 0 : (struct cli_smb2_create_flags){0},
2618 : SMB2_IMPERSONATION_IMPERSONATION,
2619 : FILE_READ_ATTRIBUTES, /* desired_access */
2620 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2621 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2622 : FILE_SHARE_DELETE, /* share_access */
2623 : FILE_OPEN, /* create_disposition */
2624 : FILE_DIRECTORY_FILE, /* create_options */
2625 : NULL,
2626 : &fnum,
2627 : NULL,
2628 : NULL,
2629 : NULL);
2630 :
2631 0 : if (!NT_STATUS_IS_OK(status)) {
2632 0 : goto fail;
2633 : }
2634 :
2635 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2636 : level 7 (SMB_FS_FULL_SIZE_INFORMATION). */
2637 :
2638 0 : status = cli_smb2_query_info_fnum(
2639 : cli,
2640 : fnum,
2641 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2642 : FSCC_FS_FULL_SIZE_INFORMATION, /* in_file_info_class */
2643 : 0xFFFF, /* in_max_output_length */
2644 : NULL, /* in_input_buffer */
2645 : 0, /* in_additional_info */
2646 : 0, /* in_flags */
2647 : frame,
2648 : &outbuf);
2649 0 : if (!NT_STATUS_IS_OK(status)) {
2650 0 : goto fail;
2651 : }
2652 :
2653 0 : if (outbuf.length < 32) {
2654 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2655 0 : goto fail;
2656 : }
2657 :
2658 0 : *total_allocation_units = BIG_UINT(outbuf.data, 0);
2659 0 : *caller_allocation_units = BIG_UINT(outbuf.data, 8);
2660 0 : *actual_allocation_units = BIG_UINT(outbuf.data, 16);
2661 0 : *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24);
2662 0 : *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28);
2663 :
2664 0 : fail:
2665 :
2666 0 : if (fnum != 0xffff) {
2667 0 : cli_smb2_close_fnum(cli, fnum);
2668 : }
2669 :
2670 0 : cli->raw_status = status;
2671 :
2672 0 : TALLOC_FREE(frame);
2673 0 : return status;
2674 : }
2675 :
2676 : /***************************************************************
2677 : Wrapper that allows SMB2 to query file system attributes.
2678 : Synchronous only.
2679 : ***************************************************************/
2680 :
2681 79 : NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
2682 : {
2683 0 : NTSTATUS status;
2684 79 : uint16_t fnum = 0xffff;
2685 79 : DATA_BLOB outbuf = data_blob_null;
2686 79 : TALLOC_CTX *frame = talloc_stackframe();
2687 :
2688 79 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2689 : /*
2690 : * Can't use sync call while an async call is in flight
2691 : */
2692 0 : status = NT_STATUS_INVALID_PARAMETER;
2693 0 : goto fail;
2694 : }
2695 :
2696 : /* First open the top level directory. */
2697 0 : status =
2698 79 : cli_smb2_create_fnum(cli, "",
2699 79 : (struct cli_smb2_create_flags){0},
2700 : SMB2_IMPERSONATION_IMPERSONATION,
2701 : FILE_READ_ATTRIBUTES, /* desired_access */
2702 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2703 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2704 : FILE_SHARE_DELETE, /* share_access */
2705 : FILE_OPEN, /* create_disposition */
2706 : FILE_DIRECTORY_FILE, /* create_options */
2707 : NULL,
2708 : &fnum,
2709 : NULL,
2710 : NULL,
2711 : NULL);
2712 :
2713 79 : if (!NT_STATUS_IS_OK(status)) {
2714 0 : goto fail;
2715 : }
2716 :
2717 79 : status = cli_smb2_query_info_fnum(
2718 : cli,
2719 : fnum,
2720 : 2, /* in_info_type */
2721 : 5, /* in_file_info_class */
2722 : 0xFFFF, /* in_max_output_length */
2723 : NULL, /* in_input_buffer */
2724 : 0, /* in_additional_info */
2725 : 0, /* in_flags */
2726 : frame,
2727 : &outbuf);
2728 79 : if (!NT_STATUS_IS_OK(status)) {
2729 0 : goto fail;
2730 : }
2731 :
2732 79 : if (outbuf.length < 12) {
2733 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2734 0 : goto fail;
2735 : }
2736 :
2737 79 : *fs_attr = IVAL(outbuf.data, 0);
2738 :
2739 79 : fail:
2740 :
2741 79 : if (fnum != 0xffff) {
2742 79 : cli_smb2_close_fnum(cli, fnum);
2743 : }
2744 :
2745 79 : cli->raw_status = status;
2746 :
2747 79 : TALLOC_FREE(frame);
2748 79 : return status;
2749 : }
2750 :
2751 : /***************************************************************
2752 : Wrapper that allows SMB2 to query file system volume info.
2753 : Synchronous only.
2754 : ***************************************************************/
2755 :
2756 16 : NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli,
2757 : TALLOC_CTX *mem_ctx,
2758 : char **_volume_name,
2759 : uint32_t *pserial_number,
2760 : time_t *pdate)
2761 : {
2762 0 : NTSTATUS status;
2763 16 : uint16_t fnum = 0xffff;
2764 16 : DATA_BLOB outbuf = data_blob_null;
2765 0 : uint32_t nlen;
2766 16 : char *volume_name = NULL;
2767 16 : TALLOC_CTX *frame = talloc_stackframe();
2768 :
2769 16 : if (smbXcli_conn_has_async_calls(cli->conn)) {
2770 : /*
2771 : * Can't use sync call while an async call is in flight
2772 : */
2773 0 : status = NT_STATUS_INVALID_PARAMETER;
2774 0 : goto fail;
2775 : }
2776 :
2777 : /* First open the top level directory. */
2778 0 : status =
2779 16 : cli_smb2_create_fnum(cli, "",
2780 16 : (struct cli_smb2_create_flags){0},
2781 : SMB2_IMPERSONATION_IMPERSONATION,
2782 : FILE_READ_ATTRIBUTES, /* desired_access */
2783 : FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
2784 : FILE_SHARE_READ | FILE_SHARE_WRITE |
2785 : FILE_SHARE_DELETE, /* share_access */
2786 : FILE_OPEN, /* create_disposition */
2787 : FILE_DIRECTORY_FILE, /* create_options */
2788 : NULL,
2789 : &fnum,
2790 : NULL,
2791 : NULL,
2792 : NULL);
2793 :
2794 16 : if (!NT_STATUS_IS_OK(status)) {
2795 0 : goto fail;
2796 : }
2797 :
2798 : /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2),
2799 : level 1 (SMB_FS_VOLUME_INFORMATION). */
2800 :
2801 16 : status = cli_smb2_query_info_fnum(
2802 : cli,
2803 : fnum,
2804 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
2805 : /* in_file_info_class */
2806 : FSCC_FS_VOLUME_INFORMATION,
2807 : 0xFFFF, /* in_max_output_length */
2808 : NULL, /* in_input_buffer */
2809 : 0, /* in_additional_info */
2810 : 0, /* in_flags */
2811 : frame,
2812 : &outbuf);
2813 16 : if (!NT_STATUS_IS_OK(status)) {
2814 0 : goto fail;
2815 : }
2816 :
2817 16 : if (outbuf.length < 24) {
2818 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2819 0 : goto fail;
2820 : }
2821 :
2822 16 : if (pdate) {
2823 0 : struct timespec ts;
2824 16 : ts = interpret_long_date(BVAL(outbuf.data, 0));
2825 16 : *pdate = ts.tv_sec;
2826 : }
2827 16 : if (pserial_number) {
2828 16 : *pserial_number = IVAL(outbuf.data,8);
2829 : }
2830 16 : nlen = IVAL(outbuf.data,12);
2831 16 : if (nlen + 18 < 18) {
2832 : /* Integer wrap. */
2833 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2834 0 : goto fail;
2835 : }
2836 : /*
2837 : * The next check is safe as we know outbuf.length >= 24
2838 : * from above.
2839 : */
2840 16 : if (nlen > (outbuf.length - 18)) {
2841 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2842 0 : goto fail;
2843 : }
2844 :
2845 16 : pull_string_talloc(mem_ctx,
2846 16 : (const char *)outbuf.data,
2847 : 0,
2848 : &volume_name,
2849 16 : outbuf.data + 18,
2850 : nlen,
2851 : STR_UNICODE);
2852 16 : if (volume_name == NULL) {
2853 0 : status = map_nt_error_from_unix(errno);
2854 0 : goto fail;
2855 : }
2856 :
2857 16 : *_volume_name = volume_name;
2858 :
2859 16 : fail:
2860 :
2861 16 : if (fnum != 0xffff) {
2862 16 : cli_smb2_close_fnum(cli, fnum);
2863 : }
2864 :
2865 16 : cli->raw_status = status;
2866 :
2867 16 : TALLOC_FREE(frame);
2868 16 : return status;
2869 : }
2870 :
2871 : struct cli_smb2_mxac_state {
2872 : struct tevent_context *ev;
2873 : struct cli_state *cli;
2874 : const char *fname;
2875 : struct smb2_create_blobs in_cblobs;
2876 : uint16_t fnum;
2877 : NTSTATUS status;
2878 : uint32_t mxac;
2879 : };
2880 :
2881 : static void cli_smb2_mxac_opened(struct tevent_req *subreq);
2882 : static void cli_smb2_mxac_closed(struct tevent_req *subreq);
2883 :
2884 4 : struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx,
2885 : struct tevent_context *ev,
2886 : struct cli_state *cli,
2887 : const char *fname)
2888 : {
2889 4 : struct tevent_req *req = NULL, *subreq = NULL;
2890 4 : struct cli_smb2_mxac_state *state = NULL;
2891 0 : NTSTATUS status;
2892 :
2893 4 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state);
2894 4 : if (req == NULL) {
2895 0 : return NULL;
2896 : }
2897 4 : *state = (struct cli_smb2_mxac_state) {
2898 : .ev = ev,
2899 : .cli = cli,
2900 : .fname = fname,
2901 : };
2902 :
2903 4 : status = smb2_create_blob_add(state,
2904 4 : &state->in_cblobs,
2905 : SMB2_CREATE_TAG_MXAC,
2906 : data_blob(NULL, 0));
2907 4 : if (tevent_req_nterror(req, status)) {
2908 0 : return tevent_req_post(req, ev);
2909 : }
2910 :
2911 4 : subreq = cli_smb2_create_fnum_send(
2912 : state,
2913 4 : state->ev,
2914 4 : state->cli,
2915 4 : state->fname,
2916 4 : (struct cli_smb2_create_flags){0},
2917 : SMB2_IMPERSONATION_IMPERSONATION,
2918 : FILE_READ_ATTRIBUTES,
2919 : 0, /* file attributes */
2920 : FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
2921 : FILE_OPEN,
2922 : 0, /* create_options */
2923 4 : &state->in_cblobs);
2924 4 : if (tevent_req_nomem(subreq, req)) {
2925 0 : return tevent_req_post(req, ev);
2926 : }
2927 4 : tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req);
2928 4 : return req;
2929 : }
2930 :
2931 4 : static void cli_smb2_mxac_opened(struct tevent_req *subreq)
2932 : {
2933 4 : struct tevent_req *req = tevent_req_callback_data(
2934 : subreq, struct tevent_req);
2935 4 : struct cli_smb2_mxac_state *state = tevent_req_data(
2936 : req, struct cli_smb2_mxac_state);
2937 4 : struct smb2_create_blobs out_cblobs = {0};
2938 4 : struct smb2_create_blob *mxac_blob = NULL;
2939 0 : NTSTATUS status;
2940 :
2941 4 : status = cli_smb2_create_fnum_recv(
2942 : subreq, &state->fnum, NULL, state, &out_cblobs, NULL);
2943 4 : TALLOC_FREE(subreq);
2944 :
2945 4 : if (tevent_req_nterror(req, status)) {
2946 0 : return;
2947 : }
2948 :
2949 4 : mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC);
2950 4 : if (mxac_blob == NULL) {
2951 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2952 0 : goto close;
2953 : }
2954 4 : if (mxac_blob->data.length != 8) {
2955 0 : state->status = NT_STATUS_INVALID_NETWORK_RESPONSE;
2956 0 : goto close;
2957 : }
2958 :
2959 4 : state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0));
2960 4 : state->mxac = IVAL(mxac_blob->data.data, 4);
2961 :
2962 4 : close:
2963 4 : subreq = cli_smb2_close_fnum_send(state,
2964 : state->ev,
2965 : state->cli,
2966 4 : state->fnum,
2967 : 0);
2968 4 : if (tevent_req_nomem(subreq, req)) {
2969 0 : return;
2970 : }
2971 4 : tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req);
2972 :
2973 4 : return;
2974 : }
2975 :
2976 4 : static void cli_smb2_mxac_closed(struct tevent_req *subreq)
2977 : {
2978 4 : struct tevent_req *req = tevent_req_callback_data(
2979 : subreq, struct tevent_req);
2980 0 : NTSTATUS status;
2981 :
2982 4 : status = cli_smb2_close_fnum_recv(subreq);
2983 4 : if (tevent_req_nterror(req, status)) {
2984 0 : return;
2985 : }
2986 :
2987 4 : tevent_req_done(req);
2988 : }
2989 :
2990 4 : NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac)
2991 : {
2992 4 : struct cli_smb2_mxac_state *state = tevent_req_data(
2993 : req, struct cli_smb2_mxac_state);
2994 0 : NTSTATUS status;
2995 :
2996 4 : if (tevent_req_is_nterror(req, &status)) {
2997 0 : return status;
2998 : }
2999 :
3000 4 : if (!NT_STATUS_IS_OK(state->status)) {
3001 0 : return state->status;
3002 : }
3003 :
3004 4 : *mxac = state->mxac;
3005 4 : return NT_STATUS_OK;
3006 : }
3007 :
3008 4 : NTSTATUS cli_smb2_query_mxac(struct cli_state *cli,
3009 : const char *fname,
3010 : uint32_t *_mxac)
3011 : {
3012 4 : TALLOC_CTX *frame = talloc_stackframe();
3013 4 : struct tevent_context *ev = NULL;
3014 4 : struct tevent_req *req = NULL;
3015 4 : NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
3016 0 : bool ok;
3017 :
3018 4 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3019 : /*
3020 : * Can't use sync call while an async call is in flight
3021 : */
3022 0 : status = NT_STATUS_INVALID_PARAMETER;
3023 0 : goto fail;
3024 : }
3025 :
3026 4 : ev = samba_tevent_context_init(frame);
3027 4 : if (ev == NULL) {
3028 0 : goto fail;
3029 : }
3030 4 : req = cli_smb2_query_mxac_send(frame, ev, cli, fname);
3031 4 : if (req == NULL) {
3032 0 : goto fail;
3033 : }
3034 4 : ok = tevent_req_poll_ntstatus(req, ev, &status);
3035 4 : if (!ok) {
3036 0 : goto fail;
3037 : }
3038 4 : status = cli_smb2_query_mxac_recv(req, _mxac);
3039 :
3040 4 : fail:
3041 4 : cli->raw_status = status;
3042 4 : TALLOC_FREE(frame);
3043 4 : return status;
3044 : }
3045 :
3046 : struct cli_smb2_rename_fnum_state {
3047 : DATA_BLOB inbuf;
3048 : };
3049 :
3050 : static void cli_smb2_rename_fnum_done(struct tevent_req *subreq);
3051 :
3052 225 : static struct tevent_req *cli_smb2_rename_fnum_send(
3053 : TALLOC_CTX *mem_ctx,
3054 : struct tevent_context *ev,
3055 : struct cli_state *cli,
3056 : uint16_t fnum,
3057 : const char *fname_dst,
3058 : bool replace)
3059 : {
3060 225 : struct tevent_req *req = NULL, *subreq = NULL;
3061 225 : struct cli_smb2_rename_fnum_state *state = NULL;
3062 225 : size_t namelen = strlen(fname_dst);
3063 225 : smb_ucs2_t *converted_str = NULL;
3064 225 : size_t converted_size_bytes = 0;
3065 0 : size_t inbuf_size;
3066 0 : bool ok;
3067 :
3068 225 : req = tevent_req_create(
3069 : mem_ctx, &state, struct cli_smb2_rename_fnum_state);
3070 225 : if (req == NULL) {
3071 0 : return NULL;
3072 : }
3073 :
3074 : /*
3075 : * SMB2 is pickier about pathnames. Ensure it doesn't start in
3076 : * a '\'
3077 : */
3078 225 : if (*fname_dst == '\\') {
3079 181 : fname_dst++;
3080 : }
3081 :
3082 : /*
3083 : * SMB2 is pickier about pathnames. Ensure it doesn't end in a
3084 : * '\'
3085 : */
3086 225 : if (namelen > 0 && fname_dst[namelen-1] == '\\') {
3087 0 : fname_dst = talloc_strndup(state, fname_dst, namelen-1);
3088 0 : if (tevent_req_nomem(fname_dst, req)) {
3089 0 : return tevent_req_post(req, ev);
3090 : }
3091 : }
3092 :
3093 225 : ok = push_ucs2_talloc(
3094 : state, &converted_str, fname_dst, &converted_size_bytes);
3095 225 : if (!ok) {
3096 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3097 0 : return tevent_req_post(req, ev);
3098 : }
3099 :
3100 : /*
3101 : * W2K8 insists the dest name is not null terminated. Remove
3102 : * the last 2 zero bytes and reduce the name length.
3103 : */
3104 225 : if (converted_size_bytes < 2) {
3105 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3106 0 : return tevent_req_post(req, ev);
3107 : }
3108 225 : converted_size_bytes -= 2;
3109 :
3110 225 : inbuf_size = 20 + converted_size_bytes;
3111 225 : if (inbuf_size < 20) {
3112 : /* Integer wrap check. */
3113 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
3114 0 : return tevent_req_post(req, ev);
3115 : }
3116 :
3117 : /*
3118 : * The Windows 10 SMB2 server has a minimum length
3119 : * for a SMB2_FILE_RENAME_INFORMATION buffer of
3120 : * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH
3121 : * if the length is less. This isn't an alignment
3122 : * issue as Windows client accepts happily 2-byte align
3123 : * for larger target name sizes. Also the Windows 10
3124 : * SMB1 server doesn't have this restriction.
3125 : *
3126 : * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403
3127 : */
3128 225 : inbuf_size = MAX(inbuf_size, 24);
3129 :
3130 225 : state->inbuf = data_blob_talloc_zero(state, inbuf_size);
3131 225 : if (tevent_req_nomem(state->inbuf.data, req)) {
3132 0 : return tevent_req_post(req, ev);
3133 : }
3134 :
3135 225 : if (replace) {
3136 14 : SCVAL(state->inbuf.data, 0, 1);
3137 : }
3138 :
3139 225 : SIVAL(state->inbuf.data, 16, converted_size_bytes);
3140 225 : memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes);
3141 :
3142 225 : TALLOC_FREE(converted_str);
3143 :
3144 : /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1),
3145 : level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */
3146 :
3147 225 : subreq = cli_smb2_set_info_fnum_send(
3148 : state, /* mem_ctx */
3149 : ev, /* ev */
3150 : cli, /* cli */
3151 : fnum, /* fnum */
3152 : SMB2_0_INFO_FILE, /* in_info_type */
3153 : FSCC_FILE_RENAME_INFORMATION, /* in_file_info_class */
3154 225 : &state->inbuf, /* in_input_buffer */
3155 : 0); /* in_additional_info */
3156 225 : if (tevent_req_nomem(subreq, req)) {
3157 0 : return tevent_req_post(req, ev);
3158 : }
3159 225 : tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req);
3160 225 : return req;
3161 : }
3162 :
3163 225 : static void cli_smb2_rename_fnum_done(struct tevent_req *subreq)
3164 : {
3165 225 : NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq);
3166 225 : tevent_req_simple_finish_ntstatus(subreq, status);
3167 225 : }
3168 :
3169 225 : static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req)
3170 : {
3171 225 : return tevent_req_simple_recv_ntstatus(req);
3172 : }
3173 :
3174 : /***************************************************************
3175 : Wrapper that allows SMB2 to rename a file.
3176 : ***************************************************************/
3177 :
3178 : struct cli_smb2_rename_state {
3179 : struct tevent_context *ev;
3180 : struct cli_state *cli;
3181 : const char *fname_dst;
3182 : bool replace;
3183 : uint16_t fnum;
3184 :
3185 : NTSTATUS rename_status;
3186 : };
3187 :
3188 : static void cli_smb2_rename_opened(struct tevent_req *subreq);
3189 : static void cli_smb2_rename_renamed(struct tevent_req *subreq);
3190 : static void cli_smb2_rename_closed(struct tevent_req *subreq);
3191 :
3192 225 : struct tevent_req *cli_smb2_rename_send(
3193 : TALLOC_CTX *mem_ctx,
3194 : struct tevent_context *ev,
3195 : struct cli_state *cli,
3196 : const char *fname_src,
3197 : const char *fname_dst,
3198 : bool replace)
3199 : {
3200 225 : struct tevent_req *req = NULL, *subreq = NULL;
3201 225 : struct cli_smb2_rename_state *state = NULL;
3202 0 : NTSTATUS status;
3203 :
3204 225 : req = tevent_req_create(
3205 : mem_ctx, &state, struct cli_smb2_rename_state);
3206 225 : if (req == NULL) {
3207 0 : return NULL;
3208 : }
3209 :
3210 : /*
3211 : * Strip a MSDFS path from fname_dst if we were given one.
3212 : */
3213 225 : status = cli_dfs_target_check(state,
3214 : cli,
3215 : fname_dst,
3216 : &fname_dst);
3217 225 : if (tevent_req_nterror(req, status)) {
3218 0 : return tevent_req_post(req, ev);
3219 : }
3220 :
3221 225 : state->ev = ev;
3222 225 : state->cli = cli;
3223 225 : state->fname_dst = fname_dst;
3224 225 : state->replace = replace;
3225 :
3226 225 : subreq = get_fnum_from_path_send(
3227 : state, ev, cli, fname_src, DELETE_ACCESS);
3228 225 : if (tevent_req_nomem(subreq, req)) {
3229 0 : return tevent_req_post(req, ev);
3230 : }
3231 225 : tevent_req_set_callback(subreq, cli_smb2_rename_opened, req);
3232 225 : return req;
3233 : }
3234 :
3235 225 : static void cli_smb2_rename_opened(struct tevent_req *subreq)
3236 : {
3237 225 : struct tevent_req *req = tevent_req_callback_data(
3238 : subreq, struct tevent_req);
3239 225 : struct cli_smb2_rename_state *state = tevent_req_data(
3240 : req, struct cli_smb2_rename_state);
3241 0 : NTSTATUS status;
3242 :
3243 225 : status = get_fnum_from_path_recv(subreq, &state->fnum);
3244 225 : TALLOC_FREE(subreq);
3245 225 : if (tevent_req_nterror(req, status)) {
3246 0 : return;
3247 : }
3248 :
3249 225 : subreq = cli_smb2_rename_fnum_send(
3250 : state,
3251 : state->ev,
3252 : state->cli,
3253 225 : state->fnum,
3254 : state->fname_dst,
3255 225 : state->replace);
3256 225 : if (tevent_req_nomem(subreq, req)) {
3257 0 : return;
3258 : }
3259 225 : tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req);
3260 : }
3261 :
3262 225 : static void cli_smb2_rename_renamed(struct tevent_req *subreq)
3263 : {
3264 225 : struct tevent_req *req = tevent_req_callback_data(
3265 : subreq, struct tevent_req);
3266 225 : struct cli_smb2_rename_state *state = tevent_req_data(
3267 : req, struct cli_smb2_rename_state);
3268 :
3269 225 : state->rename_status = cli_smb2_rename_fnum_recv(subreq);
3270 225 : TALLOC_FREE(subreq);
3271 :
3272 225 : subreq = cli_smb2_close_fnum_send(state,
3273 : state->ev,
3274 : state->cli,
3275 225 : state->fnum,
3276 : 0);
3277 225 : if (tevent_req_nomem(subreq, req)) {
3278 0 : return;
3279 : }
3280 225 : tevent_req_set_callback(subreq, cli_smb2_rename_closed, req);
3281 : }
3282 :
3283 225 : static void cli_smb2_rename_closed(struct tevent_req *subreq)
3284 : {
3285 225 : NTSTATUS status = cli_smb2_close_fnum_recv(subreq);
3286 225 : tevent_req_simple_finish_ntstatus(subreq, status);
3287 225 : }
3288 :
3289 225 : NTSTATUS cli_smb2_rename_recv(struct tevent_req *req)
3290 : {
3291 225 : struct cli_smb2_rename_state *state = tevent_req_data(
3292 : req, struct cli_smb2_rename_state);
3293 225 : NTSTATUS status = NT_STATUS_OK;
3294 :
3295 225 : if (!tevent_req_is_nterror(req, &status)) {
3296 225 : status = state->rename_status;
3297 : }
3298 225 : tevent_req_received(req);
3299 225 : return status;
3300 : }
3301 :
3302 : /***************************************************************
3303 : Wrapper that allows SMB2 to set an EA on a fnum.
3304 : Synchronous only.
3305 : ***************************************************************/
3306 :
3307 0 : NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli,
3308 : uint16_t fnum,
3309 : const char *ea_name,
3310 : const char *ea_val,
3311 : size_t ea_len)
3312 : {
3313 0 : NTSTATUS status;
3314 0 : DATA_BLOB inbuf = data_blob_null;
3315 0 : size_t bloblen = 0;
3316 0 : char *ea_name_ascii = NULL;
3317 0 : size_t namelen = 0;
3318 0 : TALLOC_CTX *frame = talloc_stackframe();
3319 :
3320 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3321 : /*
3322 : * Can't use sync call while an async call is in flight
3323 : */
3324 0 : status = NT_STATUS_INVALID_PARAMETER;
3325 0 : goto fail;
3326 : }
3327 :
3328 : /* Marshall the SMB2 EA data. */
3329 0 : if (ea_len > 0xFFFF) {
3330 0 : status = NT_STATUS_INVALID_PARAMETER;
3331 0 : goto fail;
3332 : }
3333 :
3334 0 : if (!push_ascii_talloc(frame,
3335 : &ea_name_ascii,
3336 : ea_name,
3337 : &namelen)) {
3338 0 : status = NT_STATUS_INVALID_PARAMETER;
3339 0 : goto fail;
3340 : }
3341 :
3342 0 : if (namelen < 2 || namelen > 0xFF) {
3343 0 : status = NT_STATUS_INVALID_PARAMETER;
3344 0 : goto fail;
3345 : }
3346 :
3347 0 : bloblen = 8 + ea_len + namelen;
3348 : /* Round up to a 4 byte boundary. */
3349 0 : bloblen = ((bloblen + 3)&~3);
3350 :
3351 0 : inbuf = data_blob_talloc_zero(frame, bloblen);
3352 0 : if (inbuf.data == NULL) {
3353 0 : status = NT_STATUS_NO_MEMORY;
3354 0 : goto fail;
3355 : }
3356 : /* namelen doesn't include the NULL byte. */
3357 0 : SCVAL(inbuf.data, 5, namelen - 1);
3358 0 : SSVAL(inbuf.data, 6, ea_len);
3359 0 : memcpy(inbuf.data + 8, ea_name_ascii, namelen);
3360 0 : memcpy(inbuf.data + 8 + namelen, ea_val, ea_len);
3361 :
3362 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
3363 : level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3364 :
3365 0 : status = cli_smb2_set_info_fnum(
3366 : cli,
3367 : fnum,
3368 : SMB2_0_INFO_FILE, /* in_info_type */
3369 : FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3370 : &inbuf, /* in_input_buffer */
3371 : 0); /* in_additional_info */
3372 :
3373 0 : fail:
3374 :
3375 0 : cli->raw_status = status;
3376 :
3377 0 : TALLOC_FREE(frame);
3378 0 : return status;
3379 : }
3380 :
3381 : /***************************************************************
3382 : Wrapper that allows SMB2 to set an EA on a pathname.
3383 : Synchronous only.
3384 : ***************************************************************/
3385 :
3386 0 : NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
3387 : const char *name,
3388 : const char *ea_name,
3389 : const char *ea_val,
3390 : size_t ea_len)
3391 : {
3392 0 : NTSTATUS status;
3393 0 : uint16_t fnum = 0xffff;
3394 :
3395 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3396 : /*
3397 : * Can't use sync call while an async call is in flight
3398 : */
3399 0 : status = NT_STATUS_INVALID_PARAMETER;
3400 0 : goto fail;
3401 : }
3402 :
3403 0 : status = get_fnum_from_path(cli,
3404 : name,
3405 : FILE_WRITE_EA,
3406 : &fnum);
3407 :
3408 0 : if (!NT_STATUS_IS_OK(status)) {
3409 0 : goto fail;
3410 : }
3411 :
3412 0 : status = cli_set_ea_fnum(cli,
3413 : fnum,
3414 : ea_name,
3415 : ea_val,
3416 : ea_len);
3417 0 : if (!NT_STATUS_IS_OK(status)) {
3418 0 : goto fail;
3419 : }
3420 :
3421 0 : fail:
3422 :
3423 0 : if (fnum != 0xffff) {
3424 0 : cli_smb2_close_fnum(cli, fnum);
3425 : }
3426 :
3427 0 : cli->raw_status = status;
3428 :
3429 0 : return status;
3430 : }
3431 :
3432 : /***************************************************************
3433 : Wrapper that allows SMB2 to get an EA list on a pathname.
3434 : Synchronous only.
3435 : ***************************************************************/
3436 :
3437 0 : NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
3438 : const char *name,
3439 : TALLOC_CTX *ctx,
3440 : size_t *pnum_eas,
3441 : struct ea_struct **pea_array)
3442 : {
3443 0 : NTSTATUS status;
3444 0 : uint16_t fnum = 0xffff;
3445 0 : DATA_BLOB outbuf = data_blob_null;
3446 0 : struct ea_list *ea_list = NULL;
3447 0 : struct ea_list *eal = NULL;
3448 0 : size_t ea_count = 0;
3449 0 : TALLOC_CTX *frame = talloc_stackframe();
3450 :
3451 0 : *pnum_eas = 0;
3452 0 : *pea_array = NULL;
3453 :
3454 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3455 : /*
3456 : * Can't use sync call while an async call is in flight
3457 : */
3458 0 : status = NT_STATUS_INVALID_PARAMETER;
3459 0 : goto fail;
3460 : }
3461 :
3462 0 : status = get_fnum_from_path(cli,
3463 : name,
3464 : FILE_READ_EA,
3465 : &fnum);
3466 :
3467 0 : if (!NT_STATUS_IS_OK(status)) {
3468 0 : goto fail;
3469 : }
3470 :
3471 : /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1),
3472 : level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */
3473 :
3474 0 : status = cli_smb2_query_info_fnum(
3475 : cli,
3476 : fnum,
3477 : SMB2_0_INFO_FILE, /* in_info_type */
3478 : FSCC_FILE_FULL_EA_INFORMATION, /* in_file_info_class */
3479 : 0xFFFF, /* in_max_output_length */
3480 : NULL, /* in_input_buffer */
3481 : 0, /* in_additional_info */
3482 : 0, /* in_flags */
3483 : frame,
3484 : &outbuf);
3485 :
3486 0 : if (!NT_STATUS_IS_OK(status)) {
3487 0 : goto fail;
3488 : }
3489 :
3490 : /* Parse the reply. */
3491 0 : ea_list = read_nttrans_ea_list(ctx,
3492 0 : (const char *)outbuf.data,
3493 : outbuf.length);
3494 0 : if (ea_list == NULL) {
3495 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3496 0 : goto fail;
3497 : }
3498 :
3499 : /* Convert to an array. */
3500 0 : for (eal = ea_list; eal; eal = eal->next) {
3501 0 : ea_count++;
3502 : }
3503 :
3504 0 : if (ea_count) {
3505 0 : *pea_array = talloc_array(ctx, struct ea_struct, ea_count);
3506 0 : if (*pea_array == NULL) {
3507 0 : status = NT_STATUS_NO_MEMORY;
3508 0 : goto fail;
3509 : }
3510 0 : ea_count = 0;
3511 0 : for (eal = ea_list; eal; eal = eal->next) {
3512 0 : (*pea_array)[ea_count++] = eal->ea;
3513 : }
3514 0 : *pnum_eas = ea_count;
3515 : }
3516 :
3517 0 : fail:
3518 :
3519 0 : if (fnum != 0xffff) {
3520 0 : cli_smb2_close_fnum(cli, fnum);
3521 : }
3522 :
3523 0 : cli->raw_status = status;
3524 :
3525 0 : TALLOC_FREE(frame);
3526 0 : return status;
3527 : }
3528 :
3529 : /***************************************************************
3530 : Wrapper that allows SMB2 to get user quota.
3531 : Synchronous only.
3532 : ***************************************************************/
3533 :
3534 15 : NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
3535 : int quota_fnum,
3536 : SMB_NTQUOTA_STRUCT *pqt)
3537 : {
3538 0 : NTSTATUS status;
3539 15 : DATA_BLOB inbuf = data_blob_null;
3540 15 : DATA_BLOB info_blob = data_blob_null;
3541 15 : DATA_BLOB outbuf = data_blob_null;
3542 15 : TALLOC_CTX *frame = talloc_stackframe();
3543 0 : unsigned sid_len;
3544 0 : unsigned int offset;
3545 15 : struct smb2_query_quota_info query = {0};
3546 15 : struct file_get_quota_info info = {0};
3547 0 : enum ndr_err_code err;
3548 15 : struct ndr_push *ndr_push = NULL;
3549 :
3550 15 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3551 : /*
3552 : * Can't use sync call while an async call is in flight
3553 : */
3554 0 : status = NT_STATUS_INVALID_PARAMETER;
3555 0 : goto fail;
3556 : }
3557 :
3558 15 : sid_len = ndr_size_dom_sid(&pqt->sid, 0);
3559 :
3560 15 : query.return_single = 1;
3561 :
3562 15 : info.next_entry_offset = 0;
3563 15 : info.sid_length = sid_len;
3564 15 : info.sid = pqt->sid;
3565 :
3566 15 : err = ndr_push_struct_blob(
3567 : &info_blob,
3568 : frame,
3569 : &info,
3570 : (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
3571 :
3572 15 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3573 0 : status = NT_STATUS_INTERNAL_ERROR;
3574 0 : goto fail;
3575 : }
3576 :
3577 15 : query.sid_list_length = info_blob.length;
3578 15 : ndr_push = ndr_push_init_ctx(frame);
3579 15 : if (!ndr_push) {
3580 0 : status = NT_STATUS_NO_MEMORY;
3581 0 : goto fail;
3582 : }
3583 :
3584 15 : err = ndr_push_smb2_query_quota_info(ndr_push,
3585 : NDR_SCALARS | NDR_BUFFERS,
3586 : &query);
3587 :
3588 15 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3589 0 : status = NT_STATUS_INTERNAL_ERROR;
3590 0 : goto fail;
3591 : }
3592 :
3593 15 : err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
3594 15 : info_blob.length);
3595 :
3596 15 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3597 0 : status = NT_STATUS_INTERNAL_ERROR;
3598 0 : goto fail;
3599 : }
3600 15 : inbuf.data = ndr_push->data;
3601 15 : inbuf.length = ndr_push->offset;
3602 :
3603 15 : status = cli_smb2_query_info_fnum(
3604 : cli,
3605 : quota_fnum,
3606 : 4, /* in_info_type */
3607 : 0, /* in_file_info_class */
3608 : 0xFFFF, /* in_max_output_length */
3609 : &inbuf, /* in_input_buffer */
3610 : 0, /* in_additional_info */
3611 : 0, /* in_flags */
3612 : frame,
3613 : &outbuf);
3614 :
3615 15 : if (!NT_STATUS_IS_OK(status)) {
3616 5 : goto fail;
3617 : }
3618 :
3619 10 : if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
3620 : pqt)) {
3621 0 : status = NT_STATUS_INVALID_NETWORK_RESPONSE;
3622 0 : DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
3623 : }
3624 :
3625 10 : fail:
3626 15 : cli->raw_status = status;
3627 :
3628 15 : TALLOC_FREE(frame);
3629 15 : return status;
3630 : }
3631 :
3632 : /***************************************************************
3633 : Wrapper that allows SMB2 to list user quota.
3634 : Synchronous only.
3635 : ***************************************************************/
3636 :
3637 8 : NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
3638 : TALLOC_CTX *mem_ctx,
3639 : int quota_fnum,
3640 : SMB_NTQUOTA_LIST **pqt_list,
3641 : bool first)
3642 : {
3643 0 : NTSTATUS status;
3644 8 : DATA_BLOB inbuf = data_blob_null;
3645 8 : DATA_BLOB outbuf = data_blob_null;
3646 8 : TALLOC_CTX *frame = talloc_stackframe();
3647 8 : struct smb2_query_quota_info info = {0};
3648 0 : enum ndr_err_code err;
3649 :
3650 8 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3651 : /*
3652 : * Can't use sync call while an async call is in flight
3653 : */
3654 0 : status = NT_STATUS_INVALID_PARAMETER;
3655 0 : goto cleanup;
3656 : }
3657 :
3658 8 : info.restart_scan = first ? 1 : 0;
3659 :
3660 8 : err = ndr_push_struct_blob(
3661 : &inbuf,
3662 : frame,
3663 : &info,
3664 : (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
3665 :
3666 8 : if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
3667 0 : status = NT_STATUS_INTERNAL_ERROR;
3668 0 : goto cleanup;
3669 : }
3670 :
3671 8 : status = cli_smb2_query_info_fnum(
3672 : cli,
3673 : quota_fnum,
3674 : 4, /* in_info_type */
3675 : 0, /* in_file_info_class */
3676 : 0xFFFF, /* in_max_output_length */
3677 : &inbuf, /* in_input_buffer */
3678 : 0, /* in_additional_info */
3679 : 0, /* in_flags */
3680 : frame,
3681 : &outbuf);
3682 :
3683 : /*
3684 : * safeguard against panic from calling parse_user_quota_list with
3685 : * NULL buffer
3686 : */
3687 8 : if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
3688 0 : status = NT_STATUS_NO_MORE_ENTRIES;
3689 : }
3690 :
3691 8 : if (!NT_STATUS_IS_OK(status)) {
3692 4 : goto cleanup;
3693 : }
3694 :
3695 4 : status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
3696 : pqt_list);
3697 :
3698 8 : cleanup:
3699 8 : cli->raw_status = status;
3700 :
3701 8 : TALLOC_FREE(frame);
3702 8 : return status;
3703 : }
3704 :
3705 : /***************************************************************
3706 : Wrapper that allows SMB2 to get file system quota.
3707 : Synchronous only.
3708 : ***************************************************************/
3709 :
3710 0 : NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
3711 : int quota_fnum,
3712 : SMB_NTQUOTA_STRUCT *pqt)
3713 : {
3714 0 : NTSTATUS status;
3715 0 : DATA_BLOB outbuf = data_blob_null;
3716 0 : TALLOC_CTX *frame = talloc_stackframe();
3717 :
3718 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3719 : /*
3720 : * Can't use sync call while an async call is in flight
3721 : */
3722 0 : status = NT_STATUS_INVALID_PARAMETER;
3723 0 : goto cleanup;
3724 : }
3725 :
3726 0 : status = cli_smb2_query_info_fnum(
3727 : cli,
3728 : quota_fnum,
3729 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3730 : FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3731 : 0xFFFF, /* in_max_output_length */
3732 : NULL, /* in_input_buffer */
3733 : 0, /* in_additional_info */
3734 : 0, /* in_flags */
3735 : frame,
3736 : &outbuf);
3737 :
3738 0 : if (!NT_STATUS_IS_OK(status)) {
3739 0 : goto cleanup;
3740 : }
3741 :
3742 0 : status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
3743 :
3744 0 : cleanup:
3745 0 : cli->raw_status = status;
3746 :
3747 0 : TALLOC_FREE(frame);
3748 0 : return status;
3749 : }
3750 :
3751 : /***************************************************************
3752 : Wrapper that allows SMB2 to set user quota.
3753 : Synchronous only.
3754 : ***************************************************************/
3755 :
3756 4 : NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
3757 : int quota_fnum,
3758 : SMB_NTQUOTA_LIST *qtl)
3759 : {
3760 0 : NTSTATUS status;
3761 4 : DATA_BLOB inbuf = data_blob_null;
3762 4 : TALLOC_CTX *frame = talloc_stackframe();
3763 :
3764 4 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3765 : /*
3766 : * Can't use sync call while an async call is in flight
3767 : */
3768 0 : status = NT_STATUS_INVALID_PARAMETER;
3769 0 : goto cleanup;
3770 : }
3771 :
3772 4 : status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
3773 4 : if (!NT_STATUS_IS_OK(status)) {
3774 0 : goto cleanup;
3775 : }
3776 :
3777 4 : status = cli_smb2_set_info_fnum(
3778 : cli,
3779 : quota_fnum,
3780 : 4, /* in_info_type */
3781 : 0, /* in_file_info_class */
3782 : &inbuf, /* in_input_buffer */
3783 : 0); /* in_additional_info */
3784 4 : cleanup:
3785 :
3786 4 : cli->raw_status = status;
3787 :
3788 4 : TALLOC_FREE(frame);
3789 :
3790 4 : return status;
3791 : }
3792 :
3793 0 : NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli,
3794 : int quota_fnum,
3795 : SMB_NTQUOTA_STRUCT *pqt)
3796 : {
3797 0 : NTSTATUS status;
3798 0 : DATA_BLOB inbuf = data_blob_null;
3799 0 : TALLOC_CTX *frame = talloc_stackframe();
3800 :
3801 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
3802 : /*
3803 : * Can't use sync call while an async call is in flight
3804 : */
3805 0 : status = NT_STATUS_INVALID_PARAMETER;
3806 0 : goto cleanup;
3807 : }
3808 :
3809 0 : status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
3810 0 : if (!NT_STATUS_IS_OK(status)) {
3811 0 : goto cleanup;
3812 : }
3813 :
3814 0 : status = cli_smb2_set_info_fnum(
3815 : cli,
3816 : quota_fnum,
3817 : SMB2_0_INFO_FILESYSTEM, /* in_info_type */
3818 : FSCC_FS_QUOTA_INFORMATION, /* in_file_info_class */
3819 : &inbuf, /* in_input_buffer */
3820 : 0); /* in_additional_info */
3821 0 : cleanup:
3822 0 : cli->raw_status = status;
3823 :
3824 0 : TALLOC_FREE(frame);
3825 0 : return status;
3826 : }
3827 :
3828 : struct cli_smb2_read_state {
3829 : struct tevent_context *ev;
3830 : struct cli_state *cli;
3831 : struct smb2_hnd *ph;
3832 : uint64_t start_offset;
3833 : uint32_t size;
3834 : uint32_t received;
3835 : uint8_t *buf;
3836 : };
3837 :
3838 : static void cli_smb2_read_done(struct tevent_req *subreq);
3839 :
3840 3633 : struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
3841 : struct tevent_context *ev,
3842 : struct cli_state *cli,
3843 : uint16_t fnum,
3844 : off_t offset,
3845 : size_t size)
3846 : {
3847 0 : NTSTATUS status;
3848 0 : struct tevent_req *req, *subreq;
3849 0 : struct cli_smb2_read_state *state;
3850 :
3851 3633 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state);
3852 3633 : if (req == NULL) {
3853 0 : return NULL;
3854 : }
3855 3633 : state->ev = ev;
3856 3633 : state->cli = cli;
3857 3633 : state->start_offset = (uint64_t)offset;
3858 3633 : state->size = (uint32_t)size;
3859 3633 : state->received = 0;
3860 3633 : state->buf = NULL;
3861 :
3862 3633 : status = map_fnum_to_smb2_handle(cli,
3863 : fnum,
3864 3633 : &state->ph);
3865 3633 : if (tevent_req_nterror(req, status)) {
3866 0 : return tevent_req_post(req, ev);
3867 : }
3868 :
3869 3633 : subreq = smb2cli_read_send(state,
3870 3633 : state->ev,
3871 3633 : state->cli->conn,
3872 3633 : state->cli->timeout,
3873 3633 : state->cli->smb2.session,
3874 3633 : state->cli->smb2.tcon,
3875 3633 : state->size,
3876 3633 : state->start_offset,
3877 3633 : state->ph->fid_persistent,
3878 3633 : state->ph->fid_volatile,
3879 : 0, /* minimum_count */
3880 : 0); /* remaining_bytes */
3881 :
3882 3633 : if (tevent_req_nomem(subreq, req)) {
3883 0 : return tevent_req_post(req, ev);
3884 : }
3885 3633 : tevent_req_set_callback(subreq, cli_smb2_read_done, req);
3886 3633 : return req;
3887 : }
3888 :
3889 3633 : static void cli_smb2_read_done(struct tevent_req *subreq)
3890 : {
3891 3633 : struct tevent_req *req = tevent_req_callback_data(
3892 : subreq, struct tevent_req);
3893 3633 : struct cli_smb2_read_state *state = tevent_req_data(
3894 : req, struct cli_smb2_read_state);
3895 0 : NTSTATUS status;
3896 :
3897 3633 : status = smb2cli_read_recv(subreq, state,
3898 : &state->buf, &state->received);
3899 3633 : if (tevent_req_nterror(req, status)) {
3900 329 : return;
3901 : }
3902 :
3903 3304 : if (state->received > state->size) {
3904 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
3905 0 : return;
3906 : }
3907 :
3908 3304 : tevent_req_done(req);
3909 : }
3910 :
3911 3633 : NTSTATUS cli_smb2_read_recv(struct tevent_req *req,
3912 : ssize_t *received,
3913 : uint8_t **rcvbuf)
3914 : {
3915 0 : NTSTATUS status;
3916 3633 : struct cli_smb2_read_state *state = tevent_req_data(
3917 : req, struct cli_smb2_read_state);
3918 :
3919 3633 : if (tevent_req_is_nterror(req, &status)) {
3920 329 : state->cli->raw_status = status;
3921 329 : return status;
3922 : }
3923 : /*
3924 : * As in cli_read_andx_recv() rcvbuf is talloced from the request, so
3925 : * better make sure that you copy it away before you talloc_free(req).
3926 : * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it!
3927 : */
3928 3304 : *received = (ssize_t)state->received;
3929 3304 : *rcvbuf = state->buf;
3930 3304 : state->cli->raw_status = NT_STATUS_OK;
3931 3304 : return NT_STATUS_OK;
3932 : }
3933 :
3934 : struct cli_smb2_write_state {
3935 : struct tevent_context *ev;
3936 : struct cli_state *cli;
3937 : struct smb2_hnd *ph;
3938 : uint32_t flags;
3939 : const uint8_t *buf;
3940 : uint64_t offset;
3941 : uint32_t size;
3942 : uint32_t written;
3943 : };
3944 :
3945 : static void cli_smb2_write_written(struct tevent_req *req);
3946 :
3947 2291 : struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx,
3948 : struct tevent_context *ev,
3949 : struct cli_state *cli,
3950 : uint16_t fnum,
3951 : uint16_t mode,
3952 : const uint8_t *buf,
3953 : off_t offset,
3954 : size_t size)
3955 : {
3956 0 : NTSTATUS status;
3957 2291 : struct tevent_req *req, *subreq = NULL;
3958 2291 : struct cli_smb2_write_state *state = NULL;
3959 :
3960 2291 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state);
3961 2291 : if (req == NULL) {
3962 0 : return NULL;
3963 : }
3964 2291 : state->ev = ev;
3965 2291 : state->cli = cli;
3966 : /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
3967 2291 : state->flags = (uint32_t)mode;
3968 2291 : state->buf = buf;
3969 2291 : state->offset = (uint64_t)offset;
3970 2291 : state->size = (uint32_t)size;
3971 2291 : state->written = 0;
3972 :
3973 2291 : status = map_fnum_to_smb2_handle(cli,
3974 : fnum,
3975 2291 : &state->ph);
3976 2291 : if (tevent_req_nterror(req, status)) {
3977 0 : return tevent_req_post(req, ev);
3978 : }
3979 :
3980 2291 : subreq = smb2cli_write_send(state,
3981 2291 : state->ev,
3982 2291 : state->cli->conn,
3983 2291 : state->cli->timeout,
3984 2291 : state->cli->smb2.session,
3985 2291 : state->cli->smb2.tcon,
3986 2291 : state->size,
3987 2291 : state->offset,
3988 2291 : state->ph->fid_persistent,
3989 2291 : state->ph->fid_volatile,
3990 : 0, /* remaining_bytes */
3991 2291 : state->flags, /* flags */
3992 2291 : state->buf);
3993 :
3994 2291 : if (tevent_req_nomem(subreq, req)) {
3995 0 : return tevent_req_post(req, ev);
3996 : }
3997 2291 : tevent_req_set_callback(subreq, cli_smb2_write_written, req);
3998 2291 : return req;
3999 : }
4000 :
4001 2291 : static void cli_smb2_write_written(struct tevent_req *subreq)
4002 : {
4003 2291 : struct tevent_req *req = tevent_req_callback_data(
4004 : subreq, struct tevent_req);
4005 2291 : struct cli_smb2_write_state *state = tevent_req_data(
4006 : req, struct cli_smb2_write_state);
4007 0 : NTSTATUS status;
4008 0 : uint32_t written;
4009 :
4010 2291 : status = smb2cli_write_recv(subreq, &written);
4011 2291 : TALLOC_FREE(subreq);
4012 2291 : if (tevent_req_nterror(req, status)) {
4013 0 : return;
4014 : }
4015 :
4016 2291 : state->written = written;
4017 :
4018 2291 : tevent_req_done(req);
4019 : }
4020 :
4021 2291 : NTSTATUS cli_smb2_write_recv(struct tevent_req *req,
4022 : size_t *pwritten)
4023 : {
4024 2291 : struct cli_smb2_write_state *state = tevent_req_data(
4025 : req, struct cli_smb2_write_state);
4026 0 : NTSTATUS status;
4027 :
4028 2291 : if (tevent_req_is_nterror(req, &status)) {
4029 0 : state->cli->raw_status = status;
4030 0 : tevent_req_received(req);
4031 0 : return status;
4032 : }
4033 :
4034 2291 : if (pwritten != NULL) {
4035 2291 : *pwritten = (size_t)state->written;
4036 : }
4037 2291 : state->cli->raw_status = NT_STATUS_OK;
4038 2291 : tevent_req_received(req);
4039 2291 : return NT_STATUS_OK;
4040 : }
4041 :
4042 : /***************************************************************
4043 : Wrapper that allows SMB2 async write using an fnum.
4044 : This is mostly cut-and-paste from Volker's code inside
4045 : source3/libsmb/clireadwrite.c, adapted for SMB2.
4046 :
4047 : Done this way so I can reuse all the logic inside cli_push()
4048 : for free :-).
4049 : ***************************************************************/
4050 :
4051 : struct cli_smb2_writeall_state {
4052 : struct tevent_context *ev;
4053 : struct cli_state *cli;
4054 : struct smb2_hnd *ph;
4055 : uint32_t flags;
4056 : const uint8_t *buf;
4057 : uint64_t offset;
4058 : uint32_t size;
4059 : uint32_t written;
4060 : };
4061 :
4062 : static void cli_smb2_writeall_written(struct tevent_req *req);
4063 :
4064 495 : struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx,
4065 : struct tevent_context *ev,
4066 : struct cli_state *cli,
4067 : uint16_t fnum,
4068 : uint16_t mode,
4069 : const uint8_t *buf,
4070 : off_t offset,
4071 : size_t size)
4072 : {
4073 0 : NTSTATUS status;
4074 495 : struct tevent_req *req, *subreq = NULL;
4075 495 : struct cli_smb2_writeall_state *state = NULL;
4076 0 : uint32_t to_write;
4077 0 : uint32_t max_size;
4078 0 : bool ok;
4079 :
4080 495 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state);
4081 495 : if (req == NULL) {
4082 0 : return NULL;
4083 : }
4084 495 : state->ev = ev;
4085 495 : state->cli = cli;
4086 : /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */
4087 495 : state->flags = (uint32_t)mode;
4088 495 : state->buf = buf;
4089 495 : state->offset = (uint64_t)offset;
4090 495 : state->size = (uint32_t)size;
4091 495 : state->written = 0;
4092 :
4093 495 : status = map_fnum_to_smb2_handle(cli,
4094 : fnum,
4095 495 : &state->ph);
4096 495 : if (tevent_req_nterror(req, status)) {
4097 0 : return tevent_req_post(req, ev);
4098 : }
4099 :
4100 495 : to_write = state->size;
4101 495 : max_size = smb2cli_conn_max_write_size(state->cli->conn);
4102 495 : to_write = MIN(max_size, to_write);
4103 495 : ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4104 495 : if (ok) {
4105 495 : to_write = MIN(max_size, to_write);
4106 : }
4107 :
4108 495 : subreq = smb2cli_write_send(state,
4109 495 : state->ev,
4110 495 : state->cli->conn,
4111 495 : state->cli->timeout,
4112 495 : state->cli->smb2.session,
4113 495 : state->cli->smb2.tcon,
4114 : to_write,
4115 495 : state->offset,
4116 495 : state->ph->fid_persistent,
4117 495 : state->ph->fid_volatile,
4118 : 0, /* remaining_bytes */
4119 495 : state->flags, /* flags */
4120 495 : state->buf + state->written);
4121 :
4122 495 : if (tevent_req_nomem(subreq, req)) {
4123 0 : return tevent_req_post(req, ev);
4124 : }
4125 495 : tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4126 495 : return req;
4127 : }
4128 :
4129 570 : static void cli_smb2_writeall_written(struct tevent_req *subreq)
4130 : {
4131 570 : struct tevent_req *req = tevent_req_callback_data(
4132 : subreq, struct tevent_req);
4133 570 : struct cli_smb2_writeall_state *state = tevent_req_data(
4134 : req, struct cli_smb2_writeall_state);
4135 0 : NTSTATUS status;
4136 0 : uint32_t written, to_write;
4137 0 : uint32_t max_size;
4138 0 : bool ok;
4139 :
4140 570 : status = smb2cli_write_recv(subreq, &written);
4141 570 : TALLOC_FREE(subreq);
4142 570 : if (tevent_req_nterror(req, status)) {
4143 495 : return;
4144 : }
4145 :
4146 570 : state->written += written;
4147 :
4148 570 : if (state->written > state->size) {
4149 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4150 0 : return;
4151 : }
4152 :
4153 570 : to_write = state->size - state->written;
4154 :
4155 570 : if (to_write == 0) {
4156 495 : tevent_req_done(req);
4157 495 : return;
4158 : }
4159 :
4160 75 : max_size = smb2cli_conn_max_write_size(state->cli->conn);
4161 75 : to_write = MIN(max_size, to_write);
4162 75 : ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
4163 75 : if (ok) {
4164 75 : to_write = MIN(max_size, to_write);
4165 : }
4166 :
4167 75 : subreq = smb2cli_write_send(state,
4168 : state->ev,
4169 75 : state->cli->conn,
4170 75 : state->cli->timeout,
4171 75 : state->cli->smb2.session,
4172 75 : state->cli->smb2.tcon,
4173 : to_write,
4174 75 : state->offset + state->written,
4175 75 : state->ph->fid_persistent,
4176 75 : state->ph->fid_volatile,
4177 : 0, /* remaining_bytes */
4178 : state->flags, /* flags */
4179 75 : state->buf + state->written);
4180 :
4181 75 : if (tevent_req_nomem(subreq, req)) {
4182 0 : return;
4183 : }
4184 75 : tevent_req_set_callback(subreq, cli_smb2_writeall_written, req);
4185 : }
4186 :
4187 495 : NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req,
4188 : size_t *pwritten)
4189 : {
4190 495 : struct cli_smb2_writeall_state *state = tevent_req_data(
4191 : req, struct cli_smb2_writeall_state);
4192 0 : NTSTATUS status;
4193 :
4194 495 : if (tevent_req_is_nterror(req, &status)) {
4195 0 : state->cli->raw_status = status;
4196 0 : return status;
4197 : }
4198 495 : if (pwritten != NULL) {
4199 495 : *pwritten = (size_t)state->written;
4200 : }
4201 495 : state->cli->raw_status = NT_STATUS_OK;
4202 495 : return NT_STATUS_OK;
4203 : }
4204 :
4205 : struct cli_smb2_splice_state {
4206 : struct tevent_context *ev;
4207 : struct cli_state *cli;
4208 : struct smb2_hnd *src_ph;
4209 : struct smb2_hnd *dst_ph;
4210 : int (*splice_cb)(off_t n, void *priv);
4211 : void *priv;
4212 : off_t written;
4213 : off_t size;
4214 : off_t src_offset;
4215 : off_t dst_offset;
4216 : bool resized;
4217 : struct req_resume_key_rsp resume_rsp;
4218 : struct srv_copychunk_copy cc_copy;
4219 : };
4220 :
4221 : static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4222 : struct tevent_req *req);
4223 :
4224 0 : static void cli_splice_copychunk_done(struct tevent_req *subreq)
4225 : {
4226 0 : struct tevent_req *req = tevent_req_callback_data(
4227 : subreq, struct tevent_req);
4228 0 : struct cli_smb2_splice_state *state =
4229 0 : tevent_req_data(req,
4230 : struct cli_smb2_splice_state);
4231 0 : struct smbXcli_conn *conn = state->cli->conn;
4232 0 : DATA_BLOB out_input_buffer = data_blob_null;
4233 0 : DATA_BLOB out_output_buffer = data_blob_null;
4234 0 : struct srv_copychunk_rsp cc_copy_rsp;
4235 0 : enum ndr_err_code ndr_ret;
4236 0 : NTSTATUS status;
4237 :
4238 0 : status = smb2cli_ioctl_recv(subreq, state,
4239 : &out_input_buffer,
4240 : &out_output_buffer);
4241 0 : TALLOC_FREE(subreq);
4242 0 : if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) ||
4243 0 : state->resized) && tevent_req_nterror(req, status)) {
4244 0 : return;
4245 : }
4246 :
4247 0 : ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp,
4248 : (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp);
4249 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4250 0 : DEBUG(0, ("failed to unmarshall copy chunk rsp\n"));
4251 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4252 0 : return;
4253 : }
4254 :
4255 0 : if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
4256 0 : uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written,
4257 : cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written);
4258 0 : if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) ||
4259 0 : max_chunks > smb2cli_conn_cc_max_chunks(conn)) &&
4260 0 : tevent_req_nterror(req, status)) {
4261 0 : return;
4262 : }
4263 :
4264 0 : state->resized = true;
4265 0 : smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written);
4266 0 : smb2cli_conn_set_cc_max_chunks(conn, max_chunks);
4267 : } else {
4268 0 : if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4269 0 : (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) ||
4270 0 : (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) {
4271 0 : tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4272 0 : return;
4273 : }
4274 0 : state->src_offset += cc_copy_rsp.total_bytes_written;
4275 0 : state->dst_offset += cc_copy_rsp.total_bytes_written;
4276 0 : state->written += cc_copy_rsp.total_bytes_written;
4277 0 : if (!state->splice_cb(state->written, state->priv)) {
4278 0 : tevent_req_nterror(req, NT_STATUS_CANCELLED);
4279 0 : return;
4280 : }
4281 : }
4282 :
4283 0 : cli_splice_copychunk_send(state, req);
4284 : }
4285 :
4286 0 : static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state,
4287 : struct tevent_req *req)
4288 : {
4289 0 : struct tevent_req *subreq;
4290 0 : enum ndr_err_code ndr_ret;
4291 0 : struct smbXcli_conn *conn = state->cli->conn;
4292 0 : struct srv_copychunk_copy *cc_copy = &state->cc_copy;
4293 0 : off_t src_offset = state->src_offset;
4294 0 : off_t dst_offset = state->dst_offset;
4295 0 : uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn),
4296 : state->size - state->written);
4297 0 : DATA_BLOB in_input_buffer = data_blob_null;
4298 0 : DATA_BLOB in_output_buffer = data_blob_null;
4299 :
4300 0 : if (state->size - state->written == 0) {
4301 0 : tevent_req_done(req);
4302 0 : return;
4303 : }
4304 :
4305 0 : cc_copy->chunk_count = 0;
4306 0 : while (req_len) {
4307 0 : cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset;
4308 0 : cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset;
4309 0 : cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len,
4310 : smb2cli_conn_cc_chunk_len(conn));
4311 0 : if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) {
4312 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4313 0 : return;
4314 : }
4315 0 : req_len -= cc_copy->chunks[cc_copy->chunk_count].length;
4316 0 : if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) ||
4317 0 : (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) {
4318 0 : tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE);
4319 0 : return;
4320 : }
4321 0 : src_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4322 0 : dst_offset += cc_copy->chunks[cc_copy->chunk_count].length;
4323 0 : cc_copy->chunk_count++;
4324 : }
4325 :
4326 0 : ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy,
4327 : (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy);
4328 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4329 0 : DEBUG(0, ("failed to marshall copy chunk req\n"));
4330 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
4331 0 : return;
4332 : }
4333 :
4334 0 : subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn,
4335 0 : state->cli->timeout,
4336 0 : state->cli->smb2.session,
4337 0 : state->cli->smb2.tcon,
4338 0 : state->dst_ph->fid_persistent, /* in_fid_persistent */
4339 0 : state->dst_ph->fid_volatile, /* in_fid_volatile */
4340 : FSCTL_SRV_COPYCHUNK_WRITE,
4341 : 0, /* in_max_input_length */
4342 : &in_input_buffer,
4343 : 12, /* in_max_output_length */
4344 : &in_output_buffer,
4345 : SMB2_IOCTL_FLAG_IS_FSCTL);
4346 0 : if (tevent_req_nomem(subreq, req)) {
4347 0 : return;
4348 : }
4349 0 : tevent_req_set_callback(subreq,
4350 : cli_splice_copychunk_done,
4351 : req);
4352 : }
4353 :
4354 0 : static void cli_splice_key_done(struct tevent_req *subreq)
4355 : {
4356 0 : struct tevent_req *req = tevent_req_callback_data(
4357 : subreq, struct tevent_req);
4358 0 : struct cli_smb2_splice_state *state =
4359 0 : tevent_req_data(req,
4360 : struct cli_smb2_splice_state);
4361 0 : enum ndr_err_code ndr_ret;
4362 0 : NTSTATUS status;
4363 :
4364 0 : DATA_BLOB out_input_buffer = data_blob_null;
4365 0 : DATA_BLOB out_output_buffer = data_blob_null;
4366 :
4367 0 : status = smb2cli_ioctl_recv(subreq, state,
4368 : &out_input_buffer,
4369 : &out_output_buffer);
4370 0 : TALLOC_FREE(subreq);
4371 0 : if (tevent_req_nterror(req, status)) {
4372 0 : return;
4373 : }
4374 :
4375 0 : ndr_ret = ndr_pull_struct_blob(&out_output_buffer,
4376 0 : state, &state->resume_rsp,
4377 : (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp);
4378 0 : if (ndr_ret != NDR_ERR_SUCCESS) {
4379 0 : DEBUG(0, ("failed to unmarshall resume key rsp\n"));
4380 0 : tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4381 0 : return;
4382 : }
4383 :
4384 0 : memcpy(&state->cc_copy.source_key,
4385 0 : &state->resume_rsp.resume_key,
4386 : sizeof state->resume_rsp.resume_key);
4387 :
4388 0 : cli_splice_copychunk_send(state, req);
4389 : }
4390 :
4391 0 : struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx,
4392 : struct tevent_context *ev,
4393 : struct cli_state *cli,
4394 : uint16_t src_fnum, uint16_t dst_fnum,
4395 : off_t size, off_t src_offset, off_t dst_offset,
4396 : int (*splice_cb)(off_t n, void *priv),
4397 : void *priv)
4398 : {
4399 0 : struct tevent_req *req;
4400 0 : struct tevent_req *subreq;
4401 0 : struct cli_smb2_splice_state *state;
4402 0 : NTSTATUS status;
4403 0 : DATA_BLOB in_input_buffer = data_blob_null;
4404 0 : DATA_BLOB in_output_buffer = data_blob_null;
4405 :
4406 0 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state);
4407 0 : if (req == NULL) {
4408 0 : return NULL;
4409 : }
4410 0 : state->cli = cli;
4411 0 : state->ev = ev;
4412 0 : state->splice_cb = splice_cb;
4413 0 : state->priv = priv;
4414 0 : state->size = size;
4415 0 : state->written = 0;
4416 0 : state->src_offset = src_offset;
4417 0 : state->dst_offset = dst_offset;
4418 0 : state->cc_copy.chunks = talloc_array(state,
4419 : struct srv_copychunk,
4420 : smb2cli_conn_cc_max_chunks(cli->conn));
4421 0 : if (state->cc_copy.chunks == NULL) {
4422 0 : return NULL;
4423 : }
4424 :
4425 0 : status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph);
4426 0 : if (tevent_req_nterror(req, status))
4427 0 : return tevent_req_post(req, ev);
4428 :
4429 0 : status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph);
4430 0 : if (tevent_req_nterror(req, status))
4431 0 : return tevent_req_post(req, ev);
4432 :
4433 0 : subreq = smb2cli_ioctl_send(state, ev, cli->conn,
4434 0 : cli->timeout,
4435 : cli->smb2.session,
4436 : cli->smb2.tcon,
4437 0 : state->src_ph->fid_persistent, /* in_fid_persistent */
4438 0 : state->src_ph->fid_volatile, /* in_fid_volatile */
4439 : FSCTL_SRV_REQUEST_RESUME_KEY,
4440 : 0, /* in_max_input_length */
4441 : &in_input_buffer,
4442 : 32, /* in_max_output_length */
4443 : &in_output_buffer,
4444 : SMB2_IOCTL_FLAG_IS_FSCTL);
4445 0 : if (tevent_req_nomem(subreq, req)) {
4446 0 : return NULL;
4447 : }
4448 0 : tevent_req_set_callback(subreq,
4449 : cli_splice_key_done,
4450 : req);
4451 :
4452 0 : return req;
4453 : }
4454 :
4455 0 : NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written)
4456 : {
4457 0 : struct cli_smb2_splice_state *state = tevent_req_data(
4458 : req, struct cli_smb2_splice_state);
4459 0 : NTSTATUS status;
4460 :
4461 0 : if (tevent_req_is_nterror(req, &status)) {
4462 0 : state->cli->raw_status = status;
4463 0 : tevent_req_received(req);
4464 0 : return status;
4465 : }
4466 0 : if (written != NULL) {
4467 0 : *written = state->written;
4468 : }
4469 0 : state->cli->raw_status = NT_STATUS_OK;
4470 0 : tevent_req_received(req);
4471 0 : return NT_STATUS_OK;
4472 : }
4473 :
4474 : /***************************************************************
4475 : SMB2 enum shadow copy data.
4476 : ***************************************************************/
4477 :
4478 : struct cli_smb2_shadow_copy_data_fnum_state {
4479 : struct cli_state *cli;
4480 : uint16_t fnum;
4481 : struct smb2_hnd *ph;
4482 : DATA_BLOB out_input_buffer;
4483 : DATA_BLOB out_output_buffer;
4484 : };
4485 :
4486 : static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq);
4487 :
4488 1616 : static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send(
4489 : TALLOC_CTX *mem_ctx,
4490 : struct tevent_context *ev,
4491 : struct cli_state *cli,
4492 : uint16_t fnum,
4493 : bool get_names)
4494 : {
4495 0 : struct tevent_req *req, *subreq;
4496 0 : struct cli_smb2_shadow_copy_data_fnum_state *state;
4497 0 : NTSTATUS status;
4498 :
4499 1616 : req = tevent_req_create(mem_ctx, &state,
4500 : struct cli_smb2_shadow_copy_data_fnum_state);
4501 1616 : if (req == NULL) {
4502 0 : return NULL;
4503 : }
4504 :
4505 1616 : state->cli = cli;
4506 1616 : state->fnum = fnum;
4507 :
4508 1616 : status = map_fnum_to_smb2_handle(cli, fnum, &state->ph);
4509 1616 : if (tevent_req_nterror(req, status)) {
4510 0 : return tevent_req_post(req, ev);
4511 : }
4512 :
4513 : /*
4514 : * TODO. Under SMB2 we should send a zero max_output_length
4515 : * ioctl to get the required size, then send another ioctl
4516 : * to get the data, but the current SMB1 implementation just
4517 : * does one roundtrip with a 64K buffer size. Do the same
4518 : * for now. JRA.
4519 : */
4520 :
4521 1616 : subreq = smb2cli_ioctl_send(state, ev, state->cli->conn,
4522 1616 : state->cli->timeout,
4523 1616 : state->cli->smb2.session,
4524 1616 : state->cli->smb2.tcon,
4525 1616 : state->ph->fid_persistent, /* in_fid_persistent */
4526 1616 : state->ph->fid_volatile, /* in_fid_volatile */
4527 : FSCTL_GET_SHADOW_COPY_DATA,
4528 : 0, /* in_max_input_length */
4529 : NULL, /* in_input_buffer */
4530 : get_names ?
4531 : CLI_BUFFER_SIZE : 16, /* in_max_output_length */
4532 : NULL, /* in_output_buffer */
4533 : SMB2_IOCTL_FLAG_IS_FSCTL);
4534 :
4535 1616 : if (tevent_req_nomem(subreq, req)) {
4536 0 : return tevent_req_post(req, ev);
4537 : }
4538 1616 : tevent_req_set_callback(subreq,
4539 : cli_smb2_shadow_copy_data_fnum_done,
4540 : req);
4541 :
4542 1616 : return req;
4543 : }
4544 :
4545 1616 : static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq)
4546 : {
4547 1616 : struct tevent_req *req = tevent_req_callback_data(
4548 : subreq, struct tevent_req);
4549 1616 : struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4550 : req, struct cli_smb2_shadow_copy_data_fnum_state);
4551 0 : NTSTATUS status;
4552 :
4553 1616 : status = smb2cli_ioctl_recv(subreq, state,
4554 : &state->out_input_buffer,
4555 : &state->out_output_buffer);
4556 1616 : tevent_req_simple_finish_ntstatus(subreq, status);
4557 1616 : }
4558 :
4559 1616 : static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req,
4560 : TALLOC_CTX *mem_ctx,
4561 : bool get_names,
4562 : char ***pnames,
4563 : int *pnum_names)
4564 : {
4565 1616 : struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data(
4566 : req, struct cli_smb2_shadow_copy_data_fnum_state);
4567 1616 : char **names = NULL;
4568 1616 : uint32_t num_names = 0;
4569 1616 : uint32_t num_names_returned = 0;
4570 1616 : uint32_t dlength = 0;
4571 0 : uint32_t i;
4572 1616 : uint8_t *endp = NULL;
4573 0 : NTSTATUS status;
4574 :
4575 1616 : if (tevent_req_is_nterror(req, &status)) {
4576 202 : return status;
4577 : }
4578 :
4579 1414 : if (state->out_output_buffer.length < 16) {
4580 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4581 : }
4582 :
4583 1414 : num_names = IVAL(state->out_output_buffer.data, 0);
4584 1414 : num_names_returned = IVAL(state->out_output_buffer.data, 4);
4585 1414 : dlength = IVAL(state->out_output_buffer.data, 8);
4586 :
4587 1414 : if (num_names > 0x7FFFFFFF) {
4588 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4589 : }
4590 :
4591 1414 : if (get_names == false) {
4592 707 : *pnum_names = (int)num_names;
4593 707 : return NT_STATUS_OK;
4594 : }
4595 707 : if (num_names != num_names_returned) {
4596 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4597 : }
4598 707 : if (dlength + 12 < 12) {
4599 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4600 : }
4601 : /*
4602 : * NB. The below is an allowable return if there are
4603 : * more snapshots than the buffer size we told the
4604 : * server we can receive. We currently don't support
4605 : * this.
4606 : */
4607 707 : if (dlength + 12 > state->out_output_buffer.length) {
4608 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4609 : }
4610 707 : if (state->out_output_buffer.length +
4611 : (2 * sizeof(SHADOW_COPY_LABEL)) <
4612 : state->out_output_buffer.length) {
4613 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4614 : }
4615 :
4616 707 : names = talloc_array(mem_ctx, char *, num_names_returned);
4617 707 : if (names == NULL) {
4618 0 : return NT_STATUS_NO_MEMORY;
4619 : }
4620 :
4621 707 : endp = state->out_output_buffer.data +
4622 707 : state->out_output_buffer.length;
4623 :
4624 2812 : for (i=0; i<num_names_returned; i++) {
4625 0 : bool ret;
4626 0 : uint8_t *src;
4627 0 : size_t converted_size;
4628 :
4629 2105 : src = state->out_output_buffer.data + 12 +
4630 2105 : (i * 2 * sizeof(SHADOW_COPY_LABEL));
4631 :
4632 2105 : if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) {
4633 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4634 : }
4635 2105 : ret = convert_string_talloc(
4636 : names, CH_UTF16LE, CH_UNIX,
4637 : src, 2 * sizeof(SHADOW_COPY_LABEL),
4638 2105 : &names[i], &converted_size);
4639 2105 : if (!ret) {
4640 0 : TALLOC_FREE(names);
4641 0 : return NT_STATUS_INVALID_NETWORK_RESPONSE;
4642 : }
4643 : }
4644 707 : *pnum_names = num_names;
4645 707 : *pnames = names;
4646 707 : return NT_STATUS_OK;
4647 : }
4648 :
4649 1616 : NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx,
4650 : struct cli_state *cli,
4651 : uint16_t fnum,
4652 : bool get_names,
4653 : char ***pnames,
4654 : int *pnum_names)
4655 : {
4656 1616 : TALLOC_CTX *frame = talloc_stackframe();
4657 0 : struct tevent_context *ev;
4658 0 : struct tevent_req *req;
4659 1616 : NTSTATUS status = NT_STATUS_NO_MEMORY;
4660 :
4661 1616 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4662 : /*
4663 : * Can't use sync call while an async call is in flight
4664 : */
4665 0 : status = NT_STATUS_INVALID_PARAMETER;
4666 0 : goto fail;
4667 : }
4668 1616 : ev = samba_tevent_context_init(frame);
4669 1616 : if (ev == NULL) {
4670 0 : goto fail;
4671 : }
4672 1616 : req = cli_smb2_shadow_copy_data_fnum_send(frame,
4673 : ev,
4674 : cli,
4675 : fnum,
4676 : get_names);
4677 1616 : if (req == NULL) {
4678 0 : goto fail;
4679 : }
4680 1616 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4681 0 : goto fail;
4682 : }
4683 1616 : status = cli_smb2_shadow_copy_data_fnum_recv(req,
4684 : mem_ctx,
4685 : get_names,
4686 : pnames,
4687 : pnum_names);
4688 1616 : fail:
4689 1616 : cli->raw_status = status;
4690 :
4691 1616 : TALLOC_FREE(frame);
4692 1616 : return status;
4693 : }
4694 :
4695 : /***************************************************************
4696 : Wrapper that allows SMB2 to truncate a file.
4697 : Synchronous only.
4698 : ***************************************************************/
4699 :
4700 50 : NTSTATUS cli_smb2_ftruncate(struct cli_state *cli,
4701 : uint16_t fnum,
4702 : uint64_t newsize)
4703 : {
4704 0 : NTSTATUS status;
4705 50 : uint8_t buf[8] = {0};
4706 50 : DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) };
4707 50 : TALLOC_CTX *frame = talloc_stackframe();
4708 :
4709 50 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4710 : /*
4711 : * Can't use sync call while an async call is in flight
4712 : */
4713 0 : status = NT_STATUS_INVALID_PARAMETER;
4714 0 : goto fail;
4715 : }
4716 :
4717 50 : SBVAL(buf, 0, newsize);
4718 :
4719 : /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1),
4720 : level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */
4721 :
4722 50 : status = cli_smb2_set_info_fnum(
4723 : cli,
4724 : fnum,
4725 : SMB2_0_INFO_FILE, /* in_info_type */
4726 : FSCC_FILE_END_OF_FILE_INFORMATION, /* in_file_info_class */
4727 : &inbuf, /* in_input_buffer */
4728 : 0);
4729 :
4730 50 : fail:
4731 :
4732 50 : cli->raw_status = status;
4733 :
4734 50 : TALLOC_FREE(frame);
4735 50 : return status;
4736 : }
4737 :
4738 : struct cli_smb2_notify_state {
4739 : struct tevent_req *subreq;
4740 : struct notify_change *changes;
4741 : size_t num_changes;
4742 : };
4743 :
4744 : static void cli_smb2_notify_done(struct tevent_req *subreq);
4745 : static bool cli_smb2_notify_cancel(struct tevent_req *req);
4746 :
4747 42 : struct tevent_req *cli_smb2_notify_send(
4748 : TALLOC_CTX *mem_ctx,
4749 : struct tevent_context *ev,
4750 : struct cli_state *cli,
4751 : uint16_t fnum,
4752 : uint32_t buffer_size,
4753 : uint32_t completion_filter,
4754 : bool recursive)
4755 : {
4756 42 : struct tevent_req *req = NULL;
4757 42 : struct cli_smb2_notify_state *state = NULL;
4758 42 : struct smb2_hnd *ph = NULL;
4759 0 : NTSTATUS status;
4760 :
4761 42 : req = tevent_req_create(mem_ctx, &state,
4762 : struct cli_smb2_notify_state);
4763 42 : if (req == NULL) {
4764 0 : return NULL;
4765 : }
4766 :
4767 42 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4768 42 : if (tevent_req_nterror(req, status)) {
4769 0 : return tevent_req_post(req, ev);
4770 : }
4771 :
4772 84 : state->subreq = smb2cli_notify_send(
4773 : state,
4774 : ev,
4775 : cli->conn,
4776 42 : cli->timeout,
4777 : cli->smb2.session,
4778 : cli->smb2.tcon,
4779 : buffer_size,
4780 42 : ph->fid_persistent,
4781 42 : ph->fid_volatile,
4782 : completion_filter,
4783 : recursive);
4784 42 : if (tevent_req_nomem(state->subreq, req)) {
4785 0 : return tevent_req_post(req, ev);
4786 : }
4787 42 : tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req);
4788 42 : tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel);
4789 42 : return req;
4790 : }
4791 :
4792 0 : static bool cli_smb2_notify_cancel(struct tevent_req *req)
4793 : {
4794 0 : struct cli_smb2_notify_state *state = tevent_req_data(
4795 : req, struct cli_smb2_notify_state);
4796 0 : bool ok;
4797 :
4798 0 : ok = tevent_req_cancel(state->subreq);
4799 0 : return ok;
4800 : }
4801 :
4802 42 : static void cli_smb2_notify_done(struct tevent_req *subreq)
4803 : {
4804 42 : struct tevent_req *req = tevent_req_callback_data(
4805 : subreq, struct tevent_req);
4806 42 : struct cli_smb2_notify_state *state = tevent_req_data(
4807 : req, struct cli_smb2_notify_state);
4808 0 : uint8_t *base;
4809 0 : uint32_t len;
4810 0 : uint32_t ofs;
4811 0 : NTSTATUS status;
4812 :
4813 42 : status = smb2cli_notify_recv(subreq, state, &base, &len);
4814 42 : TALLOC_FREE(subreq);
4815 :
4816 42 : if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) {
4817 0 : tevent_req_done(req);
4818 0 : return;
4819 : }
4820 42 : if (tevent_req_nterror(req, status)) {
4821 12 : return;
4822 : }
4823 :
4824 30 : ofs = 0;
4825 :
4826 30 : while (len - ofs >= 12) {
4827 0 : struct notify_change *tmp;
4828 0 : struct notify_change *c;
4829 30 : uint32_t next_ofs = IVAL(base, ofs);
4830 30 : uint32_t file_name_length = IVAL(base, ofs+8);
4831 0 : size_t namelen;
4832 0 : bool ok;
4833 :
4834 30 : tmp = talloc_realloc(
4835 : state,
4836 : state->changes,
4837 : struct notify_change,
4838 : state->num_changes + 1);
4839 30 : if (tevent_req_nomem(tmp, req)) {
4840 0 : return;
4841 : }
4842 30 : state->changes = tmp;
4843 30 : c = &state->changes[state->num_changes];
4844 30 : state->num_changes += 1;
4845 :
4846 60 : if (smb_buffer_oob(len, ofs, next_ofs) ||
4847 30 : smb_buffer_oob(len, ofs+12, file_name_length)) {
4848 0 : tevent_req_nterror(
4849 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4850 0 : return;
4851 : }
4852 :
4853 30 : c->action = IVAL(base, ofs+4);
4854 :
4855 30 : ok = convert_string_talloc(
4856 30 : state->changes,
4857 : CH_UTF16LE,
4858 : CH_UNIX,
4859 30 : base + ofs + 12,
4860 : file_name_length,
4861 30 : &c->name,
4862 : &namelen);
4863 30 : if (!ok) {
4864 0 : tevent_req_nterror(
4865 : req, NT_STATUS_INVALID_NETWORK_RESPONSE);
4866 0 : return;
4867 : }
4868 :
4869 30 : if (next_ofs == 0) {
4870 30 : break;
4871 : }
4872 0 : ofs += next_ofs;
4873 : }
4874 :
4875 30 : tevent_req_done(req);
4876 : }
4877 :
4878 42 : NTSTATUS cli_smb2_notify_recv(struct tevent_req *req,
4879 : TALLOC_CTX *mem_ctx,
4880 : struct notify_change **pchanges,
4881 : uint32_t *pnum_changes)
4882 : {
4883 42 : struct cli_smb2_notify_state *state = tevent_req_data(
4884 : req, struct cli_smb2_notify_state);
4885 0 : NTSTATUS status;
4886 :
4887 42 : if (tevent_req_is_nterror(req, &status)) {
4888 12 : return status;
4889 : }
4890 30 : *pchanges = talloc_move(mem_ctx, &state->changes);
4891 30 : *pnum_changes = state->num_changes;
4892 30 : return NT_STATUS_OK;
4893 : }
4894 :
4895 0 : NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum,
4896 : uint32_t buffer_size, uint32_t completion_filter,
4897 : bool recursive, TALLOC_CTX *mem_ctx,
4898 : struct notify_change **pchanges,
4899 : uint32_t *pnum_changes)
4900 : {
4901 0 : TALLOC_CTX *frame = talloc_stackframe();
4902 0 : struct tevent_context *ev;
4903 0 : struct tevent_req *req;
4904 0 : NTSTATUS status = NT_STATUS_NO_MEMORY;
4905 :
4906 0 : if (smbXcli_conn_has_async_calls(cli->conn)) {
4907 : /*
4908 : * Can't use sync call while an async call is in flight
4909 : */
4910 0 : status = NT_STATUS_INVALID_PARAMETER;
4911 0 : goto fail;
4912 : }
4913 0 : ev = samba_tevent_context_init(frame);
4914 0 : if (ev == NULL) {
4915 0 : goto fail;
4916 : }
4917 0 : req = cli_smb2_notify_send(
4918 : frame,
4919 : ev,
4920 : cli,
4921 : fnum,
4922 : buffer_size,
4923 : completion_filter,
4924 : recursive);
4925 0 : if (req == NULL) {
4926 0 : goto fail;
4927 : }
4928 0 : if (!tevent_req_poll_ntstatus(req, ev, &status)) {
4929 0 : goto fail;
4930 : }
4931 0 : status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes);
4932 0 : fail:
4933 0 : TALLOC_FREE(frame);
4934 0 : return status;
4935 : }
4936 :
4937 : struct cli_smb2_fsctl_state {
4938 : DATA_BLOB out;
4939 : };
4940 :
4941 : static void cli_smb2_fsctl_done(struct tevent_req *subreq);
4942 :
4943 70 : struct tevent_req *cli_smb2_fsctl_send(
4944 : TALLOC_CTX *mem_ctx,
4945 : struct tevent_context *ev,
4946 : struct cli_state *cli,
4947 : uint16_t fnum,
4948 : uint32_t ctl_code,
4949 : const DATA_BLOB *in,
4950 : uint32_t max_out)
4951 : {
4952 70 : struct tevent_req *req = NULL, *subreq = NULL;
4953 70 : struct cli_smb2_fsctl_state *state = NULL;
4954 70 : struct smb2_hnd *ph = NULL;
4955 0 : NTSTATUS status;
4956 :
4957 70 : req = tevent_req_create(mem_ctx, &state, struct cli_smb2_fsctl_state);
4958 70 : if (req == NULL) {
4959 0 : return NULL;
4960 : }
4961 :
4962 70 : status = map_fnum_to_smb2_handle(cli, fnum, &ph);
4963 70 : if (tevent_req_nterror(req, status)) {
4964 0 : return tevent_req_post(req, ev);
4965 : }
4966 :
4967 70 : subreq = smb2cli_ioctl_send(
4968 : state,
4969 : ev,
4970 : cli->conn,
4971 70 : cli->timeout,
4972 : cli->smb2.session,
4973 : cli->smb2.tcon,
4974 70 : ph->fid_persistent,
4975 70 : ph->fid_volatile,
4976 : ctl_code,
4977 : 0, /* in_max_input_length */
4978 : in,
4979 : max_out,
4980 : NULL,
4981 : SMB2_IOCTL_FLAG_IS_FSCTL);
4982 :
4983 70 : if (tevent_req_nomem(subreq, req)) {
4984 0 : return tevent_req_post(req, ev);
4985 : }
4986 70 : tevent_req_set_callback(subreq, cli_smb2_fsctl_done, req);
4987 70 : return req;
4988 : }
4989 :
4990 70 : static void cli_smb2_fsctl_done(struct tevent_req *subreq)
4991 : {
4992 70 : struct tevent_req *req = tevent_req_callback_data(
4993 : subreq, struct tevent_req);
4994 70 : struct cli_smb2_fsctl_state *state = tevent_req_data(
4995 : req, struct cli_smb2_fsctl_state);
4996 0 : NTSTATUS status;
4997 :
4998 70 : status = smb2cli_ioctl_recv(subreq, state, NULL, &state->out);
4999 70 : tevent_req_simple_finish_ntstatus(subreq, status);
5000 70 : }
5001 :
5002 70 : NTSTATUS cli_smb2_fsctl_recv(
5003 : struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *out)
5004 : {
5005 70 : struct cli_smb2_fsctl_state *state = tevent_req_data(
5006 : req, struct cli_smb2_fsctl_state);
5007 70 : NTSTATUS status = NT_STATUS_OK;
5008 :
5009 70 : if (tevent_req_is_nterror(req, &status)) {
5010 50 : tevent_req_received(req);
5011 50 : return status;
5012 : }
5013 :
5014 20 : if (state->out.length == 0) {
5015 20 : *out = (DATA_BLOB) { .data = NULL, };
5016 : } else {
5017 : /*
5018 : * Can't use talloc_move() here, the outblobs from
5019 : * smb2cli_ioctl_recv() are not standalone talloc
5020 : * objects but just peek into the larger buffers
5021 : * received, hanging off "state".
5022 : */
5023 0 : *out = data_blob_talloc(
5024 : mem_ctx, state->out.data, state->out.length);
5025 0 : if (out->data == NULL) {
5026 0 : status = NT_STATUS_NO_MEMORY;
5027 : }
5028 : }
5029 :
5030 20 : tevent_req_received(req);
5031 20 : return NT_STATUS_OK;
5032 : }
|