Coverage Report

Created: 2025-07-23 07:04

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