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