Coverage Report

Created: 2026-04-01 06:26

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