Coverage Report

Created: 2025-07-23 07:04

/src/samba/source3/smbd/smb2_query_directory.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
   Core SMB2 server
4
5
   Copyright (C) Stefan Metzmacher 2009
6
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
17
   You should have received a copy of the GNU General Public License
18
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
*/
20
21
#include "includes.h"
22
#include "locking/share_mode_lock.h"
23
#include "smbd/smbd.h"
24
#include "smbd/globals.h"
25
#include "../libcli/smb/smb_common.h"
26
#include "trans2.h"
27
#include "../lib/util/tevent_ntstatus.h"
28
#include "system/filesys.h"
29
#include "lib/pthreadpool/pthreadpool_tevent.h"
30
#include "source3/smbd/dir.h"
31
32
#undef DBGC_CLASS
33
0
#define DBGC_CLASS DBGC_SMB2
34
35
static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
36
                struct tevent_context *ev,
37
                struct smbd_smb2_request *smb2req,
38
                struct files_struct *in_fsp,
39
                uint8_t in_file_info_class,
40
                uint8_t in_flags,
41
                uint32_t in_file_index,
42
                uint32_t in_output_buffer_length,
43
                const char *in_file_name);
44
static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
45
            TALLOC_CTX *mem_ctx,
46
            DATA_BLOB *out_output_buffer);
47
48
static void smbd_smb2_request_find_done(struct tevent_req *subreq);
49
NTSTATUS smbd_smb2_request_process_query_directory(struct smbd_smb2_request *req)
50
0
{
51
0
  NTSTATUS status;
52
0
  const uint8_t *inbody;
53
0
  uint8_t in_file_info_class;
54
0
  uint8_t in_flags;
55
0
  uint32_t in_file_index;
56
0
  uint64_t in_file_id_persistent;
57
0
  uint64_t in_file_id_volatile;
58
0
  struct files_struct *in_fsp;
59
0
  uint16_t in_file_name_offset;
60
0
  uint16_t in_file_name_length;
61
0
  DATA_BLOB in_file_name_buffer;
62
0
  char *in_file_name_string;
63
0
  size_t in_file_name_string_size;
64
0
  uint32_t in_output_buffer_length;
65
0
  struct tevent_req *subreq;
66
0
  bool ok;
67
68
0
  status = smbd_smb2_request_verify_sizes(req, 0x21);
69
0
  if (!NT_STATUS_IS_OK(status)) {
70
0
    return smbd_smb2_request_error(req, status);
71
0
  }
72
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
73
74
0
  in_file_info_class    = CVAL(inbody, 0x02);
75
0
  in_flags      = CVAL(inbody, 0x03);
76
0
  in_file_index     = IVAL(inbody, 0x04);
77
0
  in_file_id_persistent   = BVAL(inbody, 0x08);
78
0
  in_file_id_volatile   = BVAL(inbody, 0x10);
79
0
  in_file_name_offset   = SVAL(inbody, 0x18);
80
0
  in_file_name_length   = SVAL(inbody, 0x1A);
81
0
  in_output_buffer_length   = IVAL(inbody, 0x1C);
82
83
0
  if (in_file_name_offset == 0 && in_file_name_length == 0) {
84
    /* This is ok */
85
0
  } else if (in_file_name_offset !=
86
0
       (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
87
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
88
0
  }
89
90
0
  if (in_file_name_length > SMBD_SMB2_IN_DYN_LEN(req)) {
91
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
92
0
  }
93
94
  /* The output header is 8 bytes. */
95
0
  if (in_output_buffer_length <= 8) {
96
0
    return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
97
0
  }
98
99
0
  DEBUG(10,("smbd_smb2_request_find_done: in_output_buffer_length = %u\n",
100
0
    (unsigned int)in_output_buffer_length ));
101
102
  /* Take into account the output header. */
103
0
  in_output_buffer_length -= 8;
104
105
0
  in_file_name_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
106
0
  in_file_name_buffer.length = in_file_name_length;
107
108
0
  ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
109
0
           in_file_name_buffer.data,
110
0
           in_file_name_buffer.length,
111
0
           &in_file_name_string,
112
0
           &in_file_name_string_size);
113
0
  if (!ok) {
114
0
    return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
115
0
  }
116
117
0
  if (in_file_name_buffer.length == 0) {
118
0
    in_file_name_string_size = 0;
119
0
  }
120
121
0
  if (strlen(in_file_name_string) != in_file_name_string_size) {
122
0
    return smbd_smb2_request_error(req, NT_STATUS_OBJECT_NAME_INVALID);
123
0
  }
124
125
0
  in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
126
0
  if (in_fsp == NULL) {
127
0
    return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
128
0
  }
129
130
0
  subreq = smbd_smb2_query_directory_send(req, req->sconn->ev_ctx,
131
0
             req, in_fsp,
132
0
             in_file_info_class,
133
0
             in_flags,
134
0
             in_file_index,
135
0
             in_output_buffer_length,
136
0
             in_file_name_string);
137
0
  if (subreq == NULL) {
138
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
139
0
  }
140
0
  tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req);
141
142
0
  return smbd_smb2_request_pending_queue(req, subreq, 500);
143
0
}
144
145
static void smbd_smb2_request_find_done(struct tevent_req *subreq)
146
0
{
147
0
  struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
148
0
          struct smbd_smb2_request);
149
0
  DATA_BLOB outbody;
150
0
  DATA_BLOB outdyn;
151
0
  uint16_t out_output_buffer_offset;
152
0
  DATA_BLOB out_output_buffer = data_blob_null;
153
0
  NTSTATUS status;
154
0
  NTSTATUS error; /* transport error */
155
156
0
  status = smbd_smb2_query_directory_recv(subreq,
157
0
             req,
158
0
             &out_output_buffer);
159
0
  TALLOC_FREE(subreq);
160
0
  if (!NT_STATUS_IS_OK(status)) {
161
0
    error = smbd_smb2_request_error(req, status);
162
0
    if (!NT_STATUS_IS_OK(error)) {
163
0
      smbd_server_connection_terminate(req->xconn,
164
0
               nt_errstr(error));
165
0
      return;
166
0
    }
167
0
    return;
168
0
  }
169
170
0
  out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
171
172
0
  outbody = smbd_smb2_generate_outbody(req, 0x08);
173
0
  if (outbody.data == NULL) {
174
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
175
0
    if (!NT_STATUS_IS_OK(error)) {
176
0
      smbd_server_connection_terminate(req->xconn,
177
0
               nt_errstr(error));
178
0
      return;
179
0
    }
180
0
    return;
181
0
  }
182
183
0
  SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */
184
0
  SSVAL(outbody.data, 0x02,
185
0
        out_output_buffer_offset);  /* output buffer offset */
186
0
  SIVAL(outbody.data, 0x04,
187
0
        out_output_buffer.length);  /* output buffer length */
188
189
0
  DEBUG(10,("smbd_smb2_request_find_done: out_output_buffer.length = %u\n",
190
0
    (unsigned int)out_output_buffer.length ));
191
192
0
  outdyn = out_output_buffer;
193
194
0
  error = smbd_smb2_request_done(req, outbody, &outdyn);
195
0
  if (!NT_STATUS_IS_OK(error)) {
196
0
    smbd_server_connection_terminate(req->xconn,
197
0
             nt_errstr(error));
198
0
    return;
199
0
  }
200
0
}
201
202
static struct tevent_req *fetch_dos_mode_send(
203
  TALLOC_CTX *mem_ctx,
204
  struct tevent_context *ev,
205
  struct files_struct *dir_fsp,
206
  struct smb_filename **smb_fname,
207
  uint32_t info_level,
208
  uint8_t *entry_marshall_buf);
209
210
static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req);
211
212
struct smbd_smb2_query_directory_state {
213
  struct tevent_context *ev;
214
  struct smbd_smb2_request *smb2req;
215
  uint32_t find_async_delay_usec;
216
  DATA_BLOB out_output_buffer;
217
  struct smb_request *smbreq;
218
  int in_output_buffer_length;
219
  struct files_struct *dirfsp;
220
  const char *in_file_name;
221
  NTSTATUS empty_status;
222
  uint32_t info_level;
223
  uint32_t max_count;
224
  char *pdata;
225
  char *base_data;
226
  char *end_data;
227
  uint32_t num;
228
  uint32_t dirtype;
229
  bool dont_descend;
230
  bool async_dosmode;
231
  int last_entry_off;
232
  size_t max_async_dosmode_active;
233
  uint32_t async_dosmode_active;
234
  bool done;
235
};
236
237
static bool smb2_query_directory_next_entry(struct tevent_req *req);
238
static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq);
239
static void smb2_query_directory_waited(struct tevent_req *subreq);
240
241
static struct tevent_req *smbd_smb2_query_directory_send(TALLOC_CTX *mem_ctx,
242
                struct tevent_context *ev,
243
                struct smbd_smb2_request *smb2req,
244
                struct files_struct *fsp,
245
                uint8_t in_file_info_class,
246
                uint8_t in_flags,
247
                uint32_t in_file_index,
248
                uint32_t in_output_buffer_length,
249
                const char *in_file_name)
250
0
{
251
0
  struct smbXsrv_connection *xconn = smb2req->xconn;
252
0
  struct tevent_req *req;
253
0
  struct smbd_smb2_query_directory_state *state;
254
0
  connection_struct *conn = smb2req->tcon->compat;
255
0
  const struct loadparm_substitution *lp_sub =
256
0
    loadparm_s3_global_substitution();
257
0
  NTSTATUS status;
258
0
  bool wcard_has_wild = false;
259
0
  struct tm tm = {};
260
0
  char *p;
261
0
  bool stop = false;
262
0
  bool ok;
263
0
  bool posix_dir_handle = fsp->fsp_flags.posix_open;
264
265
0
  req = tevent_req_create(mem_ctx, &state,
266
0
        struct smbd_smb2_query_directory_state);
267
0
  if (req == NULL) {
268
0
    return NULL;
269
0
  }
270
0
  state->ev = ev;
271
0
  state->dirfsp = fsp;
272
0
  state->smb2req = smb2req;
273
0
  state->in_output_buffer_length = in_output_buffer_length;
274
0
  state->in_file_name = in_file_name;
275
0
  state->out_output_buffer = data_blob_null;
276
0
  state->dirtype = FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY;
277
278
0
  DEBUG(10,("smbd_smb2_query_directory_send: %s - %s\n",
279
0
      fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
280
281
0
  state->smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
282
0
  if (tevent_req_nomem(state->smbreq, req)) {
283
0
    return tevent_req_post(req, ev);
284
0
  }
285
286
0
  if (!fsp->fsp_flags.is_directory) {
287
0
    tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
288
0
    return tevent_req_post(req, ev);
289
0
  }
290
291
0
  if (state->in_file_name[0] == '\0') {
292
0
    tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
293
0
    return tevent_req_post(req, ev);
294
0
  }
295
0
  if (strchr_m(state->in_file_name, '\\') != NULL) {
296
0
    tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
297
0
    return tevent_req_post(req, ev);
298
0
  }
299
0
  if (strchr_m(state->in_file_name, '/') != NULL) {
300
0
    tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID);
301
0
    return tevent_req_post(req, ev);
302
0
  }
303
304
0
  p = strptime(state->in_file_name, GMT_FORMAT, &tm);
305
0
  if ((p != NULL) && (*p =='\0')) {
306
    /*
307
     * Bogus find that asks for a shadow copy timestamp as a
308
     * directory. The correct response is that it does not exist as
309
     * a directory.
310
     */
311
0
    tevent_req_nterror(req, NT_STATUS_NO_SUCH_FILE);
312
0
    return tevent_req_post(req, ev);
313
0
  }
314
315
0
  if (in_output_buffer_length > xconn->smb2.server.max_trans) {
316
0
    DEBUG(2,("smbd_smb2_query_directory_send: "
317
0
       "client ignored max trans:%s: 0x%08X: 0x%08X\n",
318
0
       __location__, in_output_buffer_length,
319
0
       xconn->smb2.server.max_trans));
320
0
    tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
321
0
    return tevent_req_post(req, ev);
322
0
  }
323
324
0
  status = smbd_smb2_request_verify_creditcharge(smb2req,
325
0
          in_output_buffer_length);
326
327
0
  if (!NT_STATUS_IS_OK(status)) {
328
0
    tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
329
0
    return tevent_req_post(req, ev);
330
0
  }
331
332
0
  switch (in_file_info_class) {
333
0
  case SMB2_FIND_DIRECTORY_INFO:
334
0
    state->info_level = SMB_FIND_FILE_DIRECTORY_INFO;
335
0
    break;
336
337
0
  case SMB2_FIND_FULL_DIRECTORY_INFO:
338
0
    state->info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
339
0
    break;
340
341
0
  case SMB2_FIND_BOTH_DIRECTORY_INFO:
342
0
    state->info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO;
343
0
    break;
344
345
0
  case SMB2_FIND_NAME_INFO:
346
0
    state->info_level = SMB_FIND_FILE_NAMES_INFO;
347
0
    break;
348
349
0
  case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
350
0
    state->info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO;
351
0
    break;
352
353
0
  case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
354
0
    state->info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO;
355
0
    break;
356
357
0
  case SMB2_FIND_POSIX_INFORMATION:
358
0
    if (!fsp->fsp_flags.posix_open) {
359
0
      tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
360
0
      return tevent_req_post(req, ev);
361
0
    }
362
0
    state->info_level = FSCC_FILE_POSIX_INFORMATION;
363
0
    break;
364
0
  default:
365
0
    tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS);
366
0
    return tevent_req_post(req, ev);
367
0
  }
368
369
0
  if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) {
370
0
    struct vfs_open_how how = { .flags = O_RDONLY, };
371
0
    bool file_was_created;
372
373
0
#ifdef O_DIRECTORY
374
0
    how.flags |= O_DIRECTORY;
375
0
#endif
376
377
0
    status = reopen_from_fsp(conn->cwd_fsp,
378
0
           fsp->fsp_name,
379
0
           fsp,
380
0
           &how,
381
0
           &file_was_created);
382
0
    if (tevent_req_nterror(req, status)) {
383
0
      return tevent_req_post(req, ev);
384
0
    }
385
0
  }
386
387
0
  if (!state->smbreq->posix_pathnames) {
388
0
    wcard_has_wild = ms_has_wild(state->in_file_name);
389
0
  }
390
391
  /* Ensure we've canonicalized any search path if not a wildcard. */
392
0
  if (!wcard_has_wild) {
393
    /*
394
     * We still need to do the case processing
395
     * to save off the client-supplied last component.
396
     * At least we know there's no @GMT normalization
397
     * or MS-DFS paths to do in a directory mask.
398
     */
399
0
    state->in_file_name = get_original_lcomp(state,
400
0
            conn,
401
0
            state->in_file_name,
402
0
            0);
403
0
    if (tevent_req_nomem(state->in_file_name, req)) {
404
0
      return tevent_req_post(req, ev);
405
0
    }
406
0
  }
407
408
0
  if (fsp->dptr == NULL) {
409
0
    status = dptr_create(conn,
410
0
             NULL, /* req */
411
0
             fsp,
412
0
             false, /* old_handle */
413
0
             state->in_file_name, /* wcard */
414
0
             state->dirtype,
415
0
             &fsp->dptr);
416
0
    if (!NT_STATUS_IS_OK(status)) {
417
0
      tevent_req_nterror(req, status);
418
0
      return tevent_req_post(req, ev);
419
0
    }
420
421
0
    state->empty_status = NT_STATUS_NO_SUCH_FILE;
422
0
  } else {
423
0
    state->empty_status = STATUS_NO_MORE_FILES;
424
0
  }
425
426
0
  if (in_flags & SMB2_CONTINUE_FLAG_RESTART) {
427
0
    dptr_RewindDir(fsp->dptr);
428
0
  }
429
430
0
  if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) {
431
0
    state->max_count = 1;
432
0
  } else {
433
0
    state->max_count = UINT16_MAX;
434
0
  }
435
436
0
#define DIR_ENTRY_SAFETY_MARGIN 4096
437
438
0
  state->out_output_buffer = data_blob_talloc(state, NULL,
439
0
      in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN);
440
0
  if (tevent_req_nomem(state->out_output_buffer.data, req)) {
441
0
    return tevent_req_post(req, ev);
442
0
  }
443
444
0
  state->out_output_buffer.length = 0;
445
0
  state->pdata = (char *)state->out_output_buffer.data;
446
0
  state->base_data = state->pdata;
447
  /*
448
   * end_data must include the safety margin as it's what is
449
   * used to determine if pushed strings have been truncated.
450
   */
451
0
  state->end_data = state->pdata + in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN - 1;
452
453
0
  DEBUG(8,("smbd_smb2_query_directory_send: dirpath=<%s> dontdescend=<%s>, "
454
0
    "in_output_buffer_length = %u\n",
455
0
     fsp->fsp_name->base_name, lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)),
456
0
    (unsigned int)in_output_buffer_length ));
457
458
0
  state->dont_descend = in_list(
459
0
    fsp->fsp_name->base_name,
460
0
    lp_dont_descend(talloc_tos(), lp_sub, SNUM(conn)),
461
0
    posix_dir_handle ? true : conn->case_sensitive);
462
463
  /*
464
   * SMB_FIND_FILE_NAMES_INFO doesn't need stat information
465
   *
466
   * This may change when we try to improve the delete on close
467
   * handling in future.
468
   */
469
0
  if (state->info_level != SMB_FIND_FILE_NAMES_INFO) {
470
0
    state->async_dosmode = lp_smbd_async_dosmode(SNUM(conn));
471
0
  }
472
473
0
  if (state->async_dosmode) {
474
0
    size_t max_threads;
475
476
0
    max_threads = pthreadpool_tevent_max_threads(conn->sconn->pool);
477
0
    if (max_threads == 0 || !per_thread_cwd_supported()) {
478
0
      state->async_dosmode = false;
479
0
    }
480
481
0
    state->max_async_dosmode_active = lp_smbd_max_async_dosmode(
482
0
              SNUM(conn));
483
0
    if (state->max_async_dosmode_active == 0) {
484
0
      state->max_async_dosmode_active = max_threads * 2;
485
0
    }
486
0
  }
487
488
0
  if (state->async_dosmode) {
489
    /*
490
     * Should we only set async_internal
491
     * if we're not the last request in
492
     * a compound chain?
493
     */
494
0
    smb2_request_set_async_internal(smb2req, true);
495
0
  }
496
497
  /*
498
   * This gets set in autobuild for some tests
499
   */
500
0
  state->find_async_delay_usec = lp_parm_ulong(SNUM(conn), "smbd",
501
0
                 "find async delay usec",
502
0
                 0);
503
504
0
  while (!stop) {
505
0
    stop = smb2_query_directory_next_entry(req);
506
0
  }
507
508
0
  if (!tevent_req_is_in_progress(req)) {
509
0
    return tevent_req_post(req, ev);
510
0
  }
511
512
0
  ok = aio_add_req_to_fsp(fsp, req);
513
0
  if (!ok) {
514
0
    DBG_ERR("Could not add req to fsp\n");
515
0
    tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
516
0
    return tevent_req_post(req, ev);
517
0
  }
518
519
0
  return req;
520
0
}
521
522
static bool smb2_query_directory_next_entry(struct tevent_req *req)
523
0
{
524
0
  struct smbd_smb2_query_directory_state *state = tevent_req_data(
525
0
    req, struct smbd_smb2_query_directory_state);
526
0
  struct smb_filename *smb_fname = NULL; /* relative to fsp !! */
527
0
  int off = state->out_output_buffer.length;
528
0
  int space_remaining = state->in_output_buffer_length - off;
529
0
  struct file_id file_id;
530
0
  NTSTATUS status;
531
0
  bool get_dosmode = !state->async_dosmode;
532
0
  bool stop = false;
533
534
0
  SMB_ASSERT(space_remaining >= 0);
535
536
0
  status = smbd_dirptr_lanman2_entry(state,
537
0
             state->dirfsp,
538
0
             state->smbreq->flags2,
539
0
             state->in_file_name,
540
0
             state->dirtype,
541
0
             state->info_level,
542
0
             false, /* requires_resume_key */
543
0
             state->dont_descend,
544
0
             get_dosmode,
545
0
             8, /* align to 8 bytes */
546
0
             false, /* no padding */
547
0
             &state->pdata,
548
0
             state->base_data,
549
0
             state->end_data,
550
0
             space_remaining,
551
0
             &smb_fname,
552
0
             &state->last_entry_off,
553
0
             NULL,
554
0
             &file_id);
555
556
0
  off = (int)PTR_DIFF(state->pdata, state->base_data);
557
558
0
  if (!NT_STATUS_IS_OK(status)) {
559
0
    if (NT_STATUS_EQUAL(status, NT_STATUS_ILLEGAL_CHARACTER)) {
560
      /*
561
       * Bad character conversion on name. Ignore this
562
       * entry.
563
       */
564
0
      return false;
565
0
    }
566
567
0
    if (state->num > 0) {
568
0
      goto last_entry_done;
569
0
    }
570
571
0
    if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
572
0
      tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH);
573
0
      return true;
574
0
    }
575
576
0
    tevent_req_nterror(req, state->empty_status);
577
0
    return true;
578
0
  }
579
580
0
  if (state->async_dosmode) {
581
0
    struct tevent_req *subreq = NULL;
582
0
    uint8_t *buf = NULL;
583
0
    size_t outstanding_aio;
584
585
0
    buf = (uint8_t *)state->base_data + state->last_entry_off;
586
587
0
    subreq = fetch_dos_mode_send(state,
588
0
               state->ev,
589
0
               state->dirfsp,
590
0
               &smb_fname,
591
0
               state->info_level,
592
0
               buf);
593
0
    if (tevent_req_nomem(subreq, req)) {
594
0
      return true;
595
0
    }
596
0
    tevent_req_set_callback(subreq,
597
0
          smb2_query_directory_dos_mode_done,
598
0
          req);
599
600
0
    state->async_dosmode_active++;
601
602
0
    outstanding_aio = pthreadpool_tevent_queued_jobs(
603
0
      state->dirfsp->conn->sconn->pool);
604
605
0
    if (outstanding_aio > state->max_async_dosmode_active) {
606
0
      stop = true;
607
0
    }
608
0
  }
609
610
0
  TALLOC_FREE(smb_fname);
611
612
0
  state->num++;
613
0
  state->out_output_buffer.length = off;
614
615
0
  if (!state->done && state->num < state->max_count) {
616
0
    return stop;
617
0
  }
618
619
0
last_entry_done:
620
0
  SIVAL(state->out_output_buffer.data, state->last_entry_off, 0);
621
622
0
  state->done = true;
623
624
0
  if (state->async_dosmode_active > 0) {
625
0
    return true;
626
0
  }
627
628
0
  if (state->find_async_delay_usec > 0) {
629
0
    struct timeval tv;
630
0
    struct tevent_req *subreq = NULL;
631
632
    /*
633
     * Should we only set async_internal
634
     * if we're not the last request in
635
     * a compound chain?
636
     */
637
0
    smb2_request_set_async_internal(state->smb2req, true);
638
639
0
    tv = timeval_current_ofs(0, state->find_async_delay_usec);
640
641
0
    subreq = tevent_wakeup_send(state, state->ev, tv);
642
0
    if (tevent_req_nomem(subreq, req)) {
643
0
      return true;
644
0
    }
645
0
    tevent_req_set_callback(subreq,
646
0
          smb2_query_directory_waited,
647
0
          req);
648
0
    return true;
649
0
  }
650
651
0
  tevent_req_done(req);
652
0
  return true;
653
0
}
654
655
static void smb2_query_directory_check_next_entry(struct tevent_req *req);
656
657
static void smb2_query_directory_dos_mode_done(struct tevent_req *subreq)
658
0
{
659
0
  struct tevent_req *req =
660
0
    tevent_req_callback_data(subreq,
661
0
    struct tevent_req);
662
0
  struct smbd_smb2_query_directory_state *state =
663
0
    tevent_req_data(req,
664
0
    struct smbd_smb2_query_directory_state);
665
0
  NTSTATUS status;
666
0
  bool ok;
667
668
  /*
669
   * Make sure we run as the user again
670
   */
671
0
  ok = change_to_user_and_service_by_fsp(state->dirfsp);
672
0
  SMB_ASSERT(ok);
673
674
0
  status = fetch_dos_mode_recv(subreq);
675
0
  TALLOC_FREE(subreq);
676
0
  if (tevent_req_nterror(req, status)) {
677
0
    return;
678
0
  }
679
680
0
  state->async_dosmode_active--;
681
682
0
  smb2_query_directory_check_next_entry(req);
683
0
  return;
684
0
}
685
686
static void smb2_query_directory_check_next_entry(struct tevent_req *req)
687
0
{
688
0
  struct smbd_smb2_query_directory_state *state = tevent_req_data(
689
0
    req, struct smbd_smb2_query_directory_state);
690
0
  bool stop = false;
691
692
0
  if (!state->done) {
693
0
    while (!stop) {
694
0
      stop = smb2_query_directory_next_entry(req);
695
0
    }
696
0
    return;
697
0
  }
698
699
0
  if (state->async_dosmode_active > 0) {
700
0
    return;
701
0
  }
702
703
0
  if (state->find_async_delay_usec > 0) {
704
0
    struct timeval tv;
705
0
    struct tevent_req *subreq = NULL;
706
707
0
    tv = timeval_current_ofs(0, state->find_async_delay_usec);
708
709
0
    subreq = tevent_wakeup_send(state, state->ev, tv);
710
0
    if (tevent_req_nomem(subreq, req)) {
711
0
      tevent_req_post(req, state->ev);
712
0
      return;
713
0
    }
714
0
    tevent_req_set_callback(subreq,
715
0
          smb2_query_directory_waited,
716
0
          req);
717
0
    return;
718
0
  }
719
720
0
  tevent_req_done(req);
721
0
  return;
722
0
}
723
724
static void smb2_query_directory_waited(struct tevent_req *subreq)
725
0
{
726
0
  struct tevent_req *req = tevent_req_callback_data(
727
0
    subreq, struct tevent_req);
728
0
  bool ok;
729
730
0
  ok = tevent_wakeup_recv(subreq);
731
0
  TALLOC_FREE(subreq);
732
0
  if (!ok) {
733
0
    tevent_req_oom(req);
734
0
    return;
735
0
  }
736
0
  tevent_req_done(req);
737
0
}
738
739
static NTSTATUS smbd_smb2_query_directory_recv(struct tevent_req *req,
740
            TALLOC_CTX *mem_ctx,
741
            DATA_BLOB *out_output_buffer)
742
0
{
743
0
  NTSTATUS status;
744
0
  struct smbd_smb2_query_directory_state *state = tevent_req_data(req,
745
0
               struct smbd_smb2_query_directory_state);
746
747
0
  if (tevent_req_is_nterror(req, &status)) {
748
0
    tevent_req_received(req);
749
0
    return status;
750
0
  }
751
752
0
  *out_output_buffer = state->out_output_buffer;
753
0
  talloc_steal(mem_ctx, out_output_buffer->data);
754
755
0
  tevent_req_received(req);
756
0
  return NT_STATUS_OK;
757
0
}
758
759
struct fetch_dos_mode_state {
760
  struct files_struct *dir_fsp;
761
  struct smb_filename *smb_fname;
762
  uint32_t info_level;
763
  uint8_t *entry_marshall_buf;
764
};
765
766
static void fetch_dos_mode_done(struct tevent_req *subreq);
767
768
static struct tevent_req *fetch_dos_mode_send(
769
      TALLOC_CTX *mem_ctx,
770
      struct tevent_context *ev,
771
      struct files_struct *dir_fsp,
772
      struct smb_filename **smb_fname,
773
      uint32_t info_level,
774
      uint8_t *entry_marshall_buf)
775
0
{
776
0
  struct tevent_req *req = NULL;
777
0
  struct fetch_dos_mode_state *state = NULL;
778
0
  struct tevent_req *subreq = NULL;
779
780
0
  req = tevent_req_create(mem_ctx, &state, struct fetch_dos_mode_state);
781
0
  if (req == NULL) {
782
0
    return NULL;
783
0
  }
784
0
  *state = (struct fetch_dos_mode_state) {
785
0
    .dir_fsp = dir_fsp,
786
0
    .info_level = info_level,
787
0
    .entry_marshall_buf = entry_marshall_buf,
788
0
  };
789
790
0
  state->smb_fname = talloc_move(state, smb_fname);
791
792
0
  subreq = dos_mode_at_send(state, ev, dir_fsp, state->smb_fname);
793
0
  if (tevent_req_nomem(subreq, req)) {
794
0
    return tevent_req_post(req, ev);
795
0
  }
796
0
  tevent_req_set_callback(subreq, fetch_dos_mode_done, req);
797
798
0
  return req;
799
0
}
800
801
static void fetch_dos_mode_done(struct tevent_req *subreq)
802
0
{
803
0
  struct tevent_req *req =
804
0
    tevent_req_callback_data(subreq,
805
0
    struct tevent_req);
806
0
  struct fetch_dos_mode_state *state =
807
0
    tevent_req_data(req,
808
0
    struct fetch_dos_mode_state);
809
0
  uint32_t dfs_dosmode;
810
0
  uint32_t dosmode;
811
0
  struct timespec btime_ts = {0};
812
0
  off_t dosmode_off;
813
0
  off_t btime_off;
814
0
  NTSTATUS status;
815
816
0
  status = dos_mode_at_recv(subreq, &dosmode);
817
0
  TALLOC_FREE(subreq);
818
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
819
0
    tevent_req_done(req);
820
0
    return;
821
0
  }
822
0
  if (tevent_req_nterror(req, status)) {
823
0
    return;
824
0
  }
825
826
0
  switch (state->info_level) {
827
0
  case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
828
0
  case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
829
0
  case SMB_FIND_FILE_DIRECTORY_INFO:
830
0
  case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
831
0
  case SMB_FIND_ID_FULL_DIRECTORY_INFO:
832
0
    btime_off = 8;
833
0
    dosmode_off = 56;
834
0
    break;
835
836
0
  default:
837
0
    DBG_ERR("Unsupported info_level [%u]\n", state->info_level);
838
0
    tevent_req_nterror(req, NT_STATUS_INVALID_LEVEL);
839
0
    return;
840
0
  }
841
842
843
0
  dfs_dosmode = IVAL(state->entry_marshall_buf, dosmode_off);
844
0
  if (dfs_dosmode == 0) {
845
    /*
846
     * DOS mode for a DFS link, only overwrite if still set to 0 and
847
     * not already populated by the lower layer for a DFS link in
848
     * smbd_dirptr_lanman2_mode_fn().
849
     */
850
0
    SIVAL(state->entry_marshall_buf, dosmode_off, dosmode);
851
0
  }
852
853
0
  btime_ts = get_create_timespec(state->dir_fsp->conn,
854
0
               NULL,
855
0
               state->smb_fname);
856
0
  if (lp_dos_filetime_resolution(SNUM(state->dir_fsp->conn))) {
857
0
    dos_filetime_timespec(&btime_ts);
858
0
  }
859
860
0
  put_long_date_full_timespec(state->dir_fsp->conn->ts_res,
861
0
             (char *)state->entry_marshall_buf + btime_off,
862
0
             &btime_ts);
863
864
0
  tevent_req_done(req);
865
0
  return;
866
0
}
867
868
static NTSTATUS fetch_dos_mode_recv(struct tevent_req *req)
869
0
{
870
0
  NTSTATUS status;
871
872
0
  if (tevent_req_is_nterror(req, &status)) {
873
0
    tevent_req_received(req);
874
0
    return status;
875
0
  }
876
877
0
  tevent_req_received(req);
878
0
  return NT_STATUS_OK;
879
0
}