Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smb2_close.c
Line
Count
Source
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 "smbd/smbd.h"
23
#include "smbd/globals.h"
24
#include "../libcli/smb/smb_common.h"
25
#include "../lib/util/tevent_ntstatus.h"
26
27
#undef DBGC_CLASS
28
0
#define DBGC_CLASS DBGC_SMB2
29
30
static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
31
                 struct tevent_context *ev,
32
                 struct smbd_smb2_request *smb2req,
33
                 struct files_struct *in_fsp,
34
                 uint16_t in_flags);
35
static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
36
             uint16_t *out_flags,
37
             struct timespec *out_creation_ts,
38
             struct timespec *out_last_access_ts,
39
             struct timespec *out_last_write_ts,
40
             struct timespec *out_change_ts,
41
             uint64_t *out_allocation_size,
42
             uint64_t *out_end_of_file,
43
             uint32_t *out_file_attributes);
44
45
static void smbd_smb2_request_close_done(struct tevent_req *subreq);
46
47
NTSTATUS smbd_smb2_request_process_close(struct smbd_smb2_request *req)
48
0
{
49
0
  const uint8_t *inbody;
50
0
  uint16_t in_flags;
51
0
  uint64_t in_file_id_persistent;
52
0
  uint64_t in_file_id_volatile;
53
0
  struct files_struct *in_fsp;
54
0
  NTSTATUS status;
55
0
  struct tevent_req *subreq;
56
57
0
  status = smbd_smb2_request_verify_sizes(req, 0x18);
58
0
  if (!NT_STATUS_IS_OK(status)) {
59
0
    return smbd_smb2_request_error(req, status);
60
0
  }
61
0
  inbody = SMBD_SMB2_IN_BODY_PTR(req);
62
63
0
  in_flags    = SVAL(inbody, 0x02);
64
0
  in_file_id_persistent = BVAL(inbody, 0x08);
65
0
  in_file_id_volatile = BVAL(inbody, 0x10);
66
67
0
  in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
68
0
  if (in_fsp == NULL) {
69
0
    return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
70
0
  }
71
72
0
  subreq = smbd_smb2_close_send(req, req->sconn->ev_ctx,
73
0
              req, in_fsp, in_flags);
74
0
  if (subreq == NULL) {
75
0
    return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
76
0
  }
77
0
  tevent_req_set_callback(subreq, smbd_smb2_request_close_done, req);
78
79
0
  return smbd_smb2_request_pending_queue(req, subreq, 500);
80
0
}
81
82
static void smbd_smb2_request_close_done(struct tevent_req *subreq)
83
0
{
84
0
  struct smbd_smb2_request *req =
85
0
    tevent_req_callback_data(subreq,
86
0
    struct smbd_smb2_request);
87
0
  DATA_BLOB outbody;
88
0
  uint16_t out_flags = 0;
89
0
  connection_struct *conn = req->tcon->compat;
90
0
  struct timespec out_creation_ts = { 0, };
91
0
  struct timespec out_last_access_ts = { 0, };
92
0
  struct timespec out_last_write_ts = { 0, };
93
0
  struct timespec out_change_ts = { 0, };
94
0
  uint64_t out_allocation_size = 0;
95
0
  uint64_t out_end_of_file = 0;
96
0
  uint32_t out_file_attributes = 0;
97
0
  NTSTATUS status;
98
0
  NTSTATUS error;
99
100
0
  status = smbd_smb2_close_recv(subreq,
101
0
              &out_flags,
102
0
              &out_creation_ts,
103
0
              &out_last_access_ts,
104
0
              &out_last_write_ts,
105
0
              &out_change_ts,
106
0
              &out_allocation_size,
107
0
              &out_end_of_file,
108
0
              &out_file_attributes);
109
0
  TALLOC_FREE(subreq);
110
0
  if (!NT_STATUS_IS_OK(status)) {
111
0
    error = smbd_smb2_request_error(req, status);
112
0
    if (!NT_STATUS_IS_OK(error)) {
113
0
      smbd_server_connection_terminate(req->xconn,
114
0
               nt_errstr(error));
115
0
      return;
116
0
    }
117
0
    return;
118
0
  }
119
120
0
  outbody = smbd_smb2_generate_outbody(req, 0x3C);
121
0
  if (outbody.data == NULL) {
122
0
    error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
123
0
    if (!NT_STATUS_IS_OK(error)) {
124
0
      smbd_server_connection_terminate(req->xconn,
125
0
               nt_errstr(error));
126
0
      return;
127
0
    }
128
0
    return;
129
0
  }
130
131
0
  SSVAL(outbody.data, 0x00, 0x3C); /* struct size */
132
0
  SSVAL(outbody.data, 0x02, out_flags);
133
0
  SIVAL(outbody.data, 0x04, 0);    /* reserved */
134
0
  put_long_date_full_timespec(conn->ts_res,
135
0
    (char *)outbody.data + 0x08, &out_creation_ts);
136
0
  put_long_date_full_timespec(conn->ts_res,
137
0
    (char *)outbody.data + 0x10, &out_last_access_ts);
138
0
  put_long_date_full_timespec(conn->ts_res,
139
0
    (char *)outbody.data + 0x18, &out_last_write_ts);
140
0
  put_long_date_full_timespec(conn->ts_res,
141
0
    (char *)outbody.data + 0x20, &out_change_ts);
142
0
  SBVAL(outbody.data, 0x28, out_allocation_size);
143
0
  SBVAL(outbody.data, 0x30, out_end_of_file);
144
0
  SIVAL(outbody.data, 0x38, out_file_attributes);
145
146
0
  error = smbd_smb2_request_done(req, outbody, NULL);
147
0
  if (!NT_STATUS_IS_OK(error)) {
148
0
    smbd_server_connection_terminate(req->xconn,
149
0
             nt_errstr(error));
150
0
    return;
151
0
  }
152
0
}
153
154
static void setup_close_full_information(connection_struct *conn,
155
        struct smb_filename *smb_fname,
156
        struct timespec *out_creation_ts,
157
        struct timespec *out_last_access_ts,
158
        struct timespec *out_last_write_ts,
159
        struct timespec *out_change_ts,
160
        uint16_t *out_flags,
161
        uint64_t *out_allocation_size,
162
        uint64_t *out_end_of_file)
163
0
{
164
0
  *out_flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
165
0
  *out_last_write_ts = smb_fname->st.st_ex_mtime;
166
0
  *out_last_access_ts = smb_fname->st.st_ex_atime;
167
0
  *out_creation_ts = get_create_timespec(conn, NULL, smb_fname);
168
0
  *out_change_ts = smb_fname->st.st_ex_ctime;
169
170
0
  if (lp_dos_filetime_resolution(SNUM(conn))) {
171
0
    dos_filetime_timespec(out_creation_ts);
172
0
    dos_filetime_timespec(out_last_write_ts);
173
0
    dos_filetime_timespec(out_last_access_ts);
174
0
    dos_filetime_timespec(out_change_ts);
175
0
  }
176
0
  if (!S_ISDIR(smb_fname->st.st_ex_mode)) {
177
0
    *out_end_of_file = get_file_size_stat(&smb_fname->st);
178
0
  }
179
180
0
  *out_allocation_size = SMB_VFS_GET_ALLOC_SIZE(conn, NULL, &smb_fname->st);
181
0
}
182
183
static NTSTATUS smbd_smb2_close(struct smbd_smb2_request *req,
184
        struct files_struct **_fsp,
185
        uint16_t in_flags,
186
        uint16_t *out_flags,
187
        struct timespec *out_creation_ts,
188
        struct timespec *out_last_access_ts,
189
        struct timespec *out_last_write_ts,
190
        struct timespec *out_change_ts,
191
        uint64_t *out_allocation_size,
192
        uint64_t *out_end_of_file,
193
        uint32_t *out_file_attributes)
194
0
{
195
0
  NTSTATUS status;
196
0
  struct smb_request *smbreq;
197
0
  connection_struct *conn = req->tcon->compat;
198
0
  struct files_struct *fsp = *_fsp;
199
0
  struct smb_filename *smb_fname = NULL;
200
201
0
  *out_creation_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
202
0
  *out_last_access_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
203
0
  *out_last_write_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
204
0
  *out_change_ts = (struct timespec){0, SAMBA_UTIME_OMIT};
205
206
0
  *out_flags = 0;
207
0
  *out_allocation_size = 0;
208
0
  *out_end_of_file = 0;
209
0
  *out_file_attributes = 0;
210
211
0
  DEBUG(10,("smbd_smb2_close: %s - %s\n",
212
0
      fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
213
214
0
  smbreq = smbd_smb2_fake_smb_request(req, fsp);
215
0
  if (smbreq == NULL) {
216
0
    return NT_STATUS_NO_MEMORY;
217
0
  }
218
219
0
  if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
220
0
    *out_file_attributes = fdos_mode(fsp);
221
0
    fsp->fsp_flags.fstat_before_close = true;
222
0
  }
223
224
0
  status = close_file_smb(smbreq, fsp, NORMAL_CLOSE);
225
0
  if (!NT_STATUS_IS_OK(status)) {
226
0
    DEBUG(5,("smbd_smb2_close: close_file[%s]: %s\n",
227
0
       smb_fname_str_dbg(smb_fname), nt_errstr(status)));
228
0
    file_free(smbreq, fsp);
229
0
    *_fsp = fsp = NULL;
230
0
    return status;
231
0
  }
232
233
0
  if (in_flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
234
0
    setup_close_full_information(conn,
235
0
        fsp->fsp_name,
236
0
        out_creation_ts,
237
0
        out_last_access_ts,
238
0
        out_last_write_ts,
239
0
        out_change_ts,
240
0
        out_flags,
241
0
        out_allocation_size,
242
0
        out_end_of_file);
243
0
  }
244
245
0
  file_free(smbreq, fsp);
246
0
  *_fsp = fsp = NULL;
247
0
  return NT_STATUS_OK;
248
0
}
249
250
struct smbd_smb2_close_state {
251
  struct smbd_smb2_request *smb2req;
252
  struct files_struct *in_fsp;
253
  uint16_t in_flags;
254
  uint16_t out_flags;
255
  struct timespec out_creation_ts;
256
  struct timespec out_last_access_ts;
257
  struct timespec out_last_write_ts;
258
  struct timespec out_change_ts;
259
  uint64_t out_allocation_size;
260
  uint64_t out_end_of_file;
261
  uint32_t out_file_attributes;
262
  struct tevent_queue *wait_queue;
263
};
264
265
static void smbd_smb2_close_wait_done(struct tevent_req *subreq);
266
static void smbd_smb2_close_delay_lease_break_done(struct tevent_req *subreq);
267
268
static struct tevent_req *smbd_smb2_close_send(TALLOC_CTX *mem_ctx,
269
                 struct tevent_context *ev,
270
                 struct smbd_smb2_request *smb2req,
271
                 struct files_struct *in_fsp,
272
                 uint16_t in_flags)
273
0
{
274
0
  struct tevent_req *req;
275
0
  struct smbd_smb2_close_state *state;
276
0
  const char *fsp_name_str = NULL;
277
0
  const char *fsp_fnum_str = NULL;
278
0
  unsigned i;
279
0
  NTSTATUS status;
280
281
0
  if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
282
0
    fsp_name_str = fsp_str_dbg(in_fsp);
283
0
    fsp_fnum_str = fsp_fnum_dbg(in_fsp);
284
0
  }
285
286
0
  DBG_DEBUG("%s - %s\n", fsp_name_str, fsp_fnum_str);
287
288
0
  req = tevent_req_create(mem_ctx, &state,
289
0
        struct smbd_smb2_close_state);
290
0
  if (req == NULL) {
291
0
    return NULL;
292
0
  }
293
0
  state->smb2req = smb2req;
294
0
  state->in_fsp = in_fsp;
295
0
  state->in_flags = in_flags;
296
297
0
  in_fsp->fsp_flags.closing = true;
298
299
0
  i = 0;
300
0
  while (i < in_fsp->num_aio_requests) {
301
0
    bool ok = tevent_req_cancel(in_fsp->aio_requests[i]);
302
0
    if (ok) {
303
0
      continue;
304
0
    }
305
0
    i += 1;
306
0
  }
307
308
0
  if (in_fsp->num_aio_requests != 0) {
309
0
    struct tevent_req *subreq;
310
311
0
    state->wait_queue = tevent_queue_create(state,
312
0
          "smbd_smb2_close_send_wait_queue");
313
0
    if (tevent_req_nomem(state->wait_queue, req)) {
314
0
      return tevent_req_post(req, ev);
315
0
    }
316
    /*
317
     * Now wait until all aio requests on this fsp are
318
     * finished.
319
     *
320
     * We don't set a callback, as we just want to block the
321
     * wait queue and the talloc_free() of fsp->aio_request
322
     * will remove the item from the wait queue.
323
     */
324
0
    subreq = tevent_queue_wait_send(in_fsp->aio_requests,
325
0
          smb2req->sconn->ev_ctx,
326
0
          state->wait_queue);
327
0
    if (tevent_req_nomem(subreq, req)) {
328
0
      return tevent_req_post(req, ev);
329
0
    }
330
331
    /*
332
     * Now we add our own waiter to the end of the queue,
333
     * this way we get notified when all pending requests are
334
     * finished.
335
     */
336
0
    subreq = tevent_queue_wait_send(state,
337
0
          smb2req->sconn->ev_ctx,
338
0
          state->wait_queue);
339
0
    if (tevent_req_nomem(subreq, req)) {
340
0
      return tevent_req_post(req, ev);
341
0
    }
342
343
0
    tevent_req_set_callback(subreq, smbd_smb2_close_wait_done, req);
344
0
    return req;
345
0
  }
346
347
0
  if (in_fsp->fsp_flags.initial_delete_on_close) {
348
0
    struct tevent_req *subreq = NULL;
349
0
    struct share_mode_lock *lck = NULL;
350
0
    struct timeval timeout;
351
352
0
    lck = get_existing_share_mode_lock(mem_ctx,
353
0
               in_fsp->file_id);
354
0
    if (lck == NULL) {
355
0
      tevent_req_nterror(req,
356
0
             NT_STATUS_UNSUCCESSFUL);
357
0
      return tevent_req_post(req, ev);
358
0
    }
359
360
0
    if (is_delete_on_close_set(lck, in_fsp->name_hash)) {
361
0
      TALLOC_FREE(lck);
362
0
      goto do_close;
363
0
    }
364
365
0
    timeout = tevent_timeval_set(OPLOCK_BREAK_TIMEOUT, 0);
366
0
    timeout = timeval_sum(&smb2req->request_time, &timeout);
367
368
0
    subreq = delay_for_handle_lease_break_send(req,
369
0
                 ev,
370
0
                 timeout,
371
0
                 in_fsp,
372
0
                 SEC_RIGHTS_DIR_ALL,
373
0
                 false,
374
0
                 &lck);
375
0
    if (tevent_req_nomem(subreq, req)) {
376
0
      TALLOC_FREE(lck);
377
0
      return tevent_req_post(req, ev);
378
0
    }
379
0
    if (tevent_req_is_in_progress(subreq)) {
380
0
      tevent_req_set_callback(
381
0
        subreq,
382
0
        smbd_smb2_close_delay_lease_break_done,
383
0
        req);
384
0
      return req;
385
0
    }
386
387
0
    status = delay_for_handle_lease_break_recv(subreq,
388
0
                 mem_ctx,
389
0
                 &lck);
390
0
    TALLOC_FREE(subreq);
391
0
    TALLOC_FREE(lck);
392
0
    if (tevent_req_nterror(req, status)) {
393
0
      return tevent_req_post(req, ev);
394
0
    }
395
    /* Fall through */
396
0
  }
397
398
0
do_close:
399
0
  status = smbd_smb2_close(smb2req,
400
0
         &state->in_fsp,
401
0
         state->in_flags,
402
0
         &state->out_flags,
403
0
         &state->out_creation_ts,
404
0
         &state->out_last_access_ts,
405
0
         &state->out_last_write_ts,
406
0
         &state->out_change_ts,
407
0
         &state->out_allocation_size,
408
0
         &state->out_end_of_file,
409
0
         &state->out_file_attributes);
410
0
  if (tevent_req_nterror(req, status)) {
411
0
    DBG_INFO("%s - %s: close file failed: %s\n",
412
0
       fsp_name_str, fsp_fnum_str,
413
0
       nt_errstr(status));
414
0
    return tevent_req_post(req, ev);
415
0
  }
416
417
0
  tevent_req_done(req);
418
0
  return tevent_req_post(req, ev);
419
0
}
420
421
static void smbd_smb2_close_wait_done(struct tevent_req *subreq)
422
0
{
423
0
  struct tevent_req *req = tevent_req_callback_data(
424
0
    subreq, struct tevent_req);
425
0
  struct smbd_smb2_close_state *state = tevent_req_data(
426
0
    req, struct smbd_smb2_close_state);
427
0
  NTSTATUS status;
428
429
0
  tevent_queue_wait_recv(subreq);
430
0
  TALLOC_FREE(subreq);
431
432
0
  status = smbd_smb2_close(state->smb2req,
433
0
         &state->in_fsp,
434
0
         state->in_flags,
435
0
         &state->out_flags,
436
0
         &state->out_creation_ts,
437
0
         &state->out_last_access_ts,
438
0
         &state->out_last_write_ts,
439
0
         &state->out_change_ts,
440
0
         &state->out_allocation_size,
441
0
         &state->out_end_of_file,
442
0
         &state->out_file_attributes);
443
0
  if (tevent_req_nterror(req, status)) {
444
0
    return;
445
0
  }
446
0
  tevent_req_done(req);
447
0
}
448
449
static void smbd_smb2_close_delay_lease_break_done(struct tevent_req *subreq)
450
0
{
451
0
  struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
452
0
  struct smbd_smb2_close_state *state = tevent_req_data(
453
0
    req, struct smbd_smb2_close_state);
454
0
  struct share_mode_lock *lck = NULL;
455
0
  NTSTATUS status;
456
0
  bool ok;
457
458
0
  status = delay_for_handle_lease_break_recv(subreq, state, &lck);
459
0
  TALLOC_FREE(subreq);
460
0
  TALLOC_FREE(lck);
461
0
  if (tevent_req_nterror(req, status)) {
462
0
    return;
463
0
  }
464
465
  /*
466
   * Make sure we run as the user again
467
   */
468
0
  ok = change_to_user_and_service(
469
0
    state->smb2req->tcon->compat,
470
0
    state->smb2req->session->global->session_wire_id);
471
0
  if (!ok) {
472
0
    tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
473
0
    return;
474
0
  }
475
476
0
  status = smbd_smb2_close(state->smb2req,
477
0
         &state->in_fsp,
478
0
         state->in_flags,
479
0
         &state->out_flags,
480
0
         &state->out_creation_ts,
481
0
         &state->out_last_access_ts,
482
0
         &state->out_last_write_ts,
483
0
         &state->out_change_ts,
484
0
         &state->out_allocation_size,
485
0
         &state->out_end_of_file,
486
0
         &state->out_file_attributes);
487
0
  if (tevent_req_nterror(req, status)) {
488
0
    return;
489
0
  }
490
0
  tevent_req_done(req);
491
0
}
492
493
static NTSTATUS smbd_smb2_close_recv(struct tevent_req *req,
494
             uint16_t *out_flags,
495
             struct timespec *out_creation_ts,
496
             struct timespec *out_last_access_ts,
497
             struct timespec *out_last_write_ts,
498
             struct timespec *out_change_ts,
499
             uint64_t *out_allocation_size,
500
             uint64_t *out_end_of_file,
501
             uint32_t *out_file_attributes)
502
0
{
503
0
  struct smbd_smb2_close_state *state =
504
0
    tevent_req_data(req,
505
0
    struct smbd_smb2_close_state);
506
0
  NTSTATUS status;
507
508
0
  if (tevent_req_is_nterror(req, &status)) {
509
0
    tevent_req_received(req);
510
0
    return status;
511
0
  }
512
513
0
  *out_flags = state->out_flags;
514
0
  *out_creation_ts = state->out_creation_ts;
515
0
  *out_last_access_ts = state->out_last_access_ts;
516
0
  *out_last_write_ts = state->out_last_write_ts;
517
0
  *out_change_ts = state->out_change_ts;
518
0
  *out_allocation_size = state->out_allocation_size;
519
0
  *out_end_of_file = state->out_end_of_file;
520
0
  *out_file_attributes = state->out_file_attributes;
521
522
0
  tevent_req_received(req);
523
0
  return NT_STATUS_OK;
524
0
}