Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/durable.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Durable Handle default VFS implementation
4
5
   Copyright (C) Stefan Metzmacher 2012
6
   Copyright (C) Michael Adam 2012
7
   Copyright (C) Volker Lendecke 2012
8
9
   This program is free software; you can redistribute it and/or modify
10
   it under the terms of the GNU General Public License as published by
11
   the Free Software Foundation; either version 3 of the License, or
12
   (at your option) any later version.
13
14
   This program is distributed in the hope that it will be useful,
15
   but WITHOUT ANY WARRANTY; without even the implied warranty of
16
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
   GNU General Public License for more details.
18
19
   You should have received a copy of the GNU General Public License
20
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
*/
22
23
#include "includes.h"
24
#include "system/filesys.h"
25
#include "lib/util/server_id.h"
26
#include "locking/share_mode_lock.h"
27
#include "smbd/smbd.h"
28
#include "smbd/globals.h"
29
#include "libcli/security/security.h"
30
#include "messages.h"
31
#include "librpc/gen_ndr/ndr_open_files.h"
32
#include "serverid.h"
33
#include "fake_file.h"
34
#include "locking/leases_db.h"
35
36
NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
37
            TALLOC_CTX *mem_ctx,
38
            DATA_BLOB *cookie_blob)
39
0
{
40
0
  struct connection_struct *conn = fsp->conn;
41
0
  enum ndr_err_code ndr_err;
42
0
  struct vfs_default_durable_cookie cookie;
43
44
0
  if (!lp_durable_handles(SNUM(conn))) {
45
0
    return NT_STATUS_NOT_SUPPORTED;
46
0
  }
47
48
0
  if (lp_kernel_share_modes(SNUM(conn))) {
49
    /*
50
     * We do not support durable handles
51
     * if file system sharemodes are used
52
     */
53
0
    return NT_STATUS_NOT_SUPPORTED;
54
0
  }
55
56
0
  if (lp_kernel_oplocks(SNUM(conn))) {
57
    /*
58
     * We do not support durable handles
59
     * if kernel oplocks are used
60
     */
61
0
    return NT_STATUS_NOT_SUPPORTED;
62
0
  }
63
64
0
  if ((fsp->current_lock_count > 0) &&
65
0
      lp_posix_locking(fsp->conn->params))
66
0
  {
67
    /*
68
     * We do not support durable handles
69
     * if the handle has posix locks.
70
     */
71
0
    return NT_STATUS_NOT_SUPPORTED;
72
0
  }
73
74
0
  if (fsp->fsp_flags.is_directory) {
75
0
    return NT_STATUS_NOT_SUPPORTED;
76
0
  }
77
78
0
  if (fsp_is_alternate_stream(fsp)) {
79
    /*
80
     * We do not support durable handles
81
     * on streams for now.
82
     */
83
0
    return NT_STATUS_NOT_SUPPORTED;
84
0
  }
85
86
0
  if (is_fake_file(fsp->fsp_name)) {
87
    /*
88
     * We do not support durable handles
89
     * on fake files.
90
     */
91
0
    return NT_STATUS_NOT_SUPPORTED;
92
0
  }
93
94
0
  ZERO_STRUCT(cookie);
95
0
  cookie.id = fsp->file_id;
96
0
  cookie.servicepath = conn->connectpath;
97
0
  cookie.base_name = fsp->fsp_name->base_name;
98
0
  cookie.initial_allocation_size = fsp->initial_allocation_size;
99
0
  cookie.position_information = fh_get_position_information(fsp->fh);
100
0
  cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
101
102
0
  cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
103
0
  cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
104
0
  cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
105
0
  cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
106
0
  cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
107
0
  cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
108
0
  cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
109
0
  cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
110
0
  cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
111
0
  cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
112
0
  cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
113
0
  cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
114
0
  cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
115
0
  cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
116
0
  cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
117
0
  cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
118
119
0
  if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
120
0
    DBG_DEBUG("Fresh cookie\n");
121
0
    NDR_PRINT_DEBUG(vfs_default_durable_cookie, &cookie);
122
0
  }
123
124
0
  ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
125
0
      (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
126
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
127
0
    NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
128
0
    return status;
129
0
  }
130
131
0
  return NT_STATUS_OK;
132
0
}
133
134
struct durable_disconnect_state {
135
  NTSTATUS status;
136
  struct files_struct *fsp;
137
};
138
139
static void default_durable_disconnect_fn(struct share_mode_lock *lck,
140
            struct byte_range_lock *br_lck,
141
            void *private_data)
142
0
{
143
0
  struct durable_disconnect_state *state = private_data;
144
0
  struct files_struct *fsp = state->fsp;
145
0
  bool ok;
146
147
0
  ok = mark_share_mode_disconnected(lck, fsp);
148
0
  if (!ok) {
149
0
    state->status = NT_STATUS_UNSUCCESSFUL;
150
0
    return;
151
0
  }
152
153
0
  if (br_lck == NULL) {
154
0
    state->status = NT_STATUS_OK;
155
0
    return;
156
0
  }
157
158
0
  ok = brl_mark_disconnected(fsp, br_lck);
159
0
  if (!ok) {
160
0
    state->status = NT_STATUS_UNSUCCESSFUL;
161
0
    return;
162
0
  }
163
0
  state->status = NT_STATUS_OK;
164
0
}
165
166
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
167
          const DATA_BLOB old_cookie,
168
          TALLOC_CTX *mem_ctx,
169
          DATA_BLOB *new_cookie)
170
0
{
171
0
  struct connection_struct *conn = fsp->conn;
172
0
  NTSTATUS status;
173
0
  enum ndr_err_code ndr_err;
174
0
  struct vfs_default_durable_cookie cookie;
175
0
  DATA_BLOB new_cookie_blob = data_blob_null;
176
0
  struct durable_disconnect_state state;
177
178
0
  *new_cookie = data_blob_null;
179
180
0
  ZERO_STRUCT(cookie);
181
182
0
  ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
183
0
      (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
184
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
185
0
    status = ndr_map_error2ntstatus(ndr_err);
186
0
    return status;
187
0
  }
188
189
0
  if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
190
0
    DBG_DEBUG("Old cookie\n");
191
0
    NDR_PRINT_DEBUG(vfs_default_durable_cookie, &cookie);
192
0
  }
193
194
0
  if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
195
0
    return NT_STATUS_INVALID_PARAMETER;
196
0
  }
197
198
0
  if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
199
0
    return NT_STATUS_INVALID_PARAMETER;
200
0
  }
201
202
0
  if (!file_id_equal(&fsp->file_id, &cookie.id)) {
203
0
    return NT_STATUS_INVALID_PARAMETER;
204
0
  }
205
206
0
  if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
207
0
    return NT_STATUS_NOT_SUPPORTED;
208
0
  }
209
210
0
  if (fsp->current_lock_count != 0 &&
211
0
      (fsp_lease_type(fsp) & SMB2_LEASE_WRITE) == 0)
212
0
  {
213
0
    return NT_STATUS_NOT_SUPPORTED;
214
0
  }
215
216
  /*
217
   * For now let it be simple and do not keep
218
   * delete on close files durable open
219
   */
220
0
  if (fsp->fsp_flags.initial_delete_on_close) {
221
0
    return NT_STATUS_NOT_SUPPORTED;
222
0
  }
223
0
  if (fsp->fsp_flags.delete_on_close) {
224
0
    return NT_STATUS_NOT_SUPPORTED;
225
0
  }
226
227
0
  if (!VALID_STAT(fsp->fsp_name->st)) {
228
0
    return NT_STATUS_NOT_SUPPORTED;
229
0
  }
230
231
0
  if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
232
0
    return NT_STATUS_NOT_SUPPORTED;
233
0
  }
234
235
0
  state = (struct durable_disconnect_state) {
236
0
    .fsp = fsp,
237
0
  };
238
239
0
  status = share_mode_do_locked_brl(fsp,
240
0
            default_durable_disconnect_fn,
241
0
            &state);
242
0
  if (!NT_STATUS_IS_OK(status)) {
243
0
    DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n",
244
0
      fsp_str_dbg(fsp), nt_errstr(status));
245
0
    return status;
246
0
  }
247
0
  if (!NT_STATUS_IS_OK(state.status)) {
248
0
    DBG_ERR("default_durable_disconnect_fn [%s] failed: %s\n",
249
0
      fsp_str_dbg(fsp), nt_errstr(state.status));
250
0
    return state.status;
251
0
  }
252
253
0
  status = vfs_stat_fsp(fsp);
254
0
  if (!NT_STATUS_IS_OK(status)) {
255
0
    return status;
256
0
  }
257
258
0
  ZERO_STRUCT(cookie);
259
0
  cookie.allow_reconnect = true;
260
0
  cookie.id = fsp->file_id;
261
0
  cookie.servicepath = conn->connectpath;
262
0
  cookie.base_name = fsp_str_dbg(fsp);
263
0
  cookie.initial_allocation_size = fsp->initial_allocation_size;
264
0
  cookie.position_information = fh_get_position_information(fsp->fh);
265
0
  cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
266
267
0
  cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
268
0
  cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
269
0
  cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
270
0
  cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
271
0
  cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
272
0
  cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
273
0
  cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
274
0
  cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
275
0
  cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
276
0
  cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
277
0
  cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
278
0
  cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
279
0
  cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
280
0
  cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
281
0
  cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
282
0
  cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
283
284
0
  ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
285
0
      (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
286
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
287
0
    status = ndr_map_error2ntstatus(ndr_err);
288
0
    return status;
289
0
  }
290
291
0
  if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
292
0
    DBG_DEBUG("New cookie\n");
293
0
    NDR_PRINT_DEBUG(vfs_default_durable_cookie, &cookie);
294
0
  }
295
296
0
  status = fd_close(fsp);
297
0
  if (!NT_STATUS_IS_OK(status)) {
298
0
    data_blob_free(&new_cookie_blob);
299
0
    return status;
300
0
  }
301
302
0
  *new_cookie = new_cookie_blob;
303
0
  return NT_STATUS_OK;
304
0
}
305
306
307
/**
308
 * Check whether a cookie-stored struct info is the same
309
 * as a given SMB_STRUCT_STAT, as coming with the fsp.
310
 */
311
static bool vfs_default_durable_reconnect_check_stat(
312
        struct vfs_default_durable_stat *cookie_st,
313
        struct files_struct *fsp)
314
0
{
315
0
  SMB_STRUCT_STAT *fsp_st = &fsp->fsp_name->st;
316
0
  bool equal;
317
318
0
  if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
319
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
320
0
        "stat_ex.%s differs: "
321
0
        "cookie:%llu != stat:%llu, "
322
0
        "denying durable reconnect\n",
323
0
        fsp_str_dbg(fsp),
324
0
        "st_ex_mode",
325
0
        (unsigned long long)cookie_st->st_ex_mode,
326
0
        (unsigned long long)fsp_st->st_ex_mode));
327
0
    return false;
328
0
  }
329
330
0
  if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
331
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
332
0
        "stat_ex.%s differs: "
333
0
        "cookie:%llu != stat:%llu, "
334
0
        "denying durable reconnect\n",
335
0
        fsp_str_dbg(fsp),
336
0
        "st_ex_nlink",
337
0
        (unsigned long long)cookie_st->st_ex_nlink,
338
0
        (unsigned long long)fsp_st->st_ex_nlink));
339
0
    return false;
340
0
  }
341
342
0
  if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
343
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
344
0
        "stat_ex.%s differs: "
345
0
        "cookie:%llu != stat:%llu, "
346
0
        "denying durable reconnect\n",
347
0
        fsp_str_dbg(fsp),
348
0
        "st_ex_uid",
349
0
        (unsigned long long)cookie_st->st_ex_uid,
350
0
        (unsigned long long)fsp_st->st_ex_uid));
351
0
    return false;
352
0
  }
353
354
0
  if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
355
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
356
0
        "stat_ex.%s differs: "
357
0
        "cookie:%llu != stat:%llu, "
358
0
        "denying durable reconnect\n",
359
0
        fsp_str_dbg(fsp),
360
0
        "st_ex_gid",
361
0
        (unsigned long long)cookie_st->st_ex_gid,
362
0
        (unsigned long long)fsp_st->st_ex_gid));
363
0
    return false;
364
0
  }
365
366
0
  if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
367
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
368
0
        "stat_ex.%s differs: "
369
0
        "cookie:%llu != stat:%llu, "
370
0
        "denying durable reconnect\n",
371
0
        fsp_str_dbg(fsp),
372
0
        "st_ex_rdev",
373
0
        (unsigned long long)cookie_st->st_ex_rdev,
374
0
        (unsigned long long)fsp_st->st_ex_rdev));
375
0
    return false;
376
0
  }
377
378
0
  if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
379
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
380
0
        "stat_ex.%s differs: "
381
0
        "cookie:%llu != stat:%llu, "
382
0
        "denying durable reconnect\n",
383
0
        fsp_str_dbg(fsp),
384
0
        "st_ex_size",
385
0
        (unsigned long long)cookie_st->st_ex_size,
386
0
        (unsigned long long)fsp_st->st_ex_size));
387
0
    return false;
388
0
  }
389
390
0
  equal = timespec_equal(&cookie_st->st_ex_atime, &fsp_st->st_ex_atime);
391
0
  if (!equal) {
392
0
    struct timeval tc, ts;
393
0
    tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
394
0
    ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
395
396
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
397
0
        "stat_ex.%s differs: "
398
0
        "cookie:'%s' != stat:'%s', "
399
0
        "denying durable reconnect\n",
400
0
        fsp_str_dbg(fsp),
401
0
        "st_ex_atime",
402
0
        timeval_string(talloc_tos(), &tc, true),
403
0
        timeval_string(talloc_tos(), &ts, true)));
404
0
    return false;
405
0
  }
406
407
0
  equal = timespec_equal(&cookie_st->st_ex_mtime, &fsp_st->st_ex_mtime);
408
0
  if (!equal) {
409
0
    struct timeval tc, ts;
410
0
    tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
411
0
    ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
412
413
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
414
0
        "stat_ex.%s differs: "
415
0
        "cookie:'%s' != stat:'%s', "
416
0
        "denying durable reconnect\n",
417
0
        fsp_str_dbg(fsp),
418
0
        "st_ex_mtime",
419
0
        timeval_string(talloc_tos(), &tc, true),
420
0
        timeval_string(talloc_tos(), &ts, true)));
421
0
    return false;
422
0
  }
423
424
0
  equal = timespec_equal(&cookie_st->st_ex_ctime, &fsp_st->st_ex_ctime);
425
0
  if (!equal) {
426
0
    struct timeval tc, ts;
427
0
    tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
428
0
    ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
429
430
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
431
0
        "stat_ex.%s differs: "
432
0
        "cookie:'%s' != stat:'%s', "
433
0
        "denying durable reconnect\n",
434
0
        fsp_str_dbg(fsp),
435
0
        "st_ex_ctime",
436
0
        timeval_string(talloc_tos(), &tc, true),
437
0
        timeval_string(talloc_tos(), &ts, true)));
438
0
    return false;
439
0
  }
440
441
0
  equal = timespec_equal(&cookie_st->st_ex_btime, &fsp_st->st_ex_btime);
442
0
  if (!equal) {
443
0
    struct timeval tc, ts;
444
0
    tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
445
0
    ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
446
447
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
448
0
        "stat_ex.%s differs: "
449
0
        "cookie:'%s' != stat:'%s', "
450
0
        "denying durable reconnect\n",
451
0
        fsp_str_dbg(fsp),
452
0
        "st_ex_btime",
453
0
        timeval_string(talloc_tos(), &tc, true),
454
0
        timeval_string(talloc_tos(), &ts, true)));
455
0
    return false;
456
0
  }
457
458
0
  if (cookie_st->st_ex_iflags != fsp_st->st_ex_iflags) {
459
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
460
0
        "stat_ex.%s differs: "
461
0
        "cookie:%llu != stat:%llu, "
462
0
        "denying durable reconnect\n",
463
0
        fsp_str_dbg(fsp),
464
0
        "st_ex_calculated_birthtime",
465
0
        (unsigned long long)cookie_st->st_ex_iflags,
466
0
        (unsigned long long)fsp_st->st_ex_iflags));
467
0
    return false;
468
0
  }
469
470
0
  if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
471
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
472
0
        "stat_ex.%s differs: "
473
0
        "cookie:%llu != stat:%llu, "
474
0
        "denying durable reconnect\n",
475
0
        fsp_str_dbg(fsp),
476
0
        "st_ex_blksize",
477
0
        (unsigned long long)cookie_st->st_ex_blksize,
478
0
        (unsigned long long)fsp_st->st_ex_blksize));
479
0
    return false;
480
0
  }
481
482
0
  if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
483
0
    DEBUG(1, ("vfs_default_durable_reconnect (%s): "
484
0
        "stat_ex.%s differs: "
485
0
        "cookie:%llu != stat:%llu, "
486
0
        "denying durable reconnect\n",
487
0
        fsp_str_dbg(fsp),
488
0
        "st_ex_blocks",
489
0
        (unsigned long long)cookie_st->st_ex_blocks,
490
0
        (unsigned long long)fsp_st->st_ex_blocks));
491
0
    return false;
492
0
  }
493
494
0
  if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
495
0
    DBG_WARNING(" (%s): "
496
0
          "stat_ex.%s differs: "
497
0
          "cookie:%"PRIu32" != stat:%"PRIu32", "
498
0
          "denying durable reconnect\n",
499
0
          fsp_str_dbg(fsp),
500
0
          "st_ex_flags",
501
0
          cookie_st->st_ex_flags,
502
0
          fsp_st->st_ex_flags);
503
0
    return false;
504
0
  }
505
506
0
  return true;
507
0
}
508
509
struct durable_reconnect_state {
510
  struct smbXsrv_open *op;
511
  struct share_mode_entry *e;
512
};
513
514
static bool durable_reconnect_fn(
515
  struct share_mode_entry *e,
516
  bool *modified,
517
  void *private_data)
518
0
{
519
0
  struct durable_reconnect_state *state = private_data;
520
0
  uint64_t id = state->op->global->open_persistent_id;
521
522
0
  if (e->share_file_id != id) {
523
0
    return false; /* Look at potential other entries */
524
0
  }
525
526
0
  if (!server_id_is_disconnected(&e->pid)) {
527
0
    return false; /* Look at potential other entries */
528
0
  }
529
530
0
  if (state->e->share_file_id == id) {
531
0
    DBG_INFO("Found more than one entry, invalidating previous\n");
532
0
    *state->e = (struct share_mode_entry) { .pid = { .pid = 0, }};
533
0
    return true; /* end the loop through share mode entries */
534
0
  }
535
0
  *state->e = *e;
536
0
  return false;   /* Look at potential other entries */
537
0
}
538
539
struct vfs_default_durable_reconnect_state {
540
  NTSTATUS status;
541
  TALLOC_CTX *mem_ctx;
542
  struct smb_request *smb1req;
543
  struct smbXsrv_open *op;
544
  struct vfs_default_durable_cookie cookie;
545
  struct files_struct *fsp;
546
  struct files_struct *dirfsp;
547
  struct smb_filename *rel_fname;
548
  DATA_BLOB new_cookie_blob;
549
};
550
551
static void vfs_default_durable_reconnect_fn(struct share_mode_lock *lck,
552
               struct byte_range_lock *br_lck,
553
               void *private_data)
554
0
{
555
0
  struct vfs_default_durable_reconnect_state *state = private_data;
556
0
  const struct loadparm_substitution *lp_sub =
557
0
    loadparm_s3_global_substitution();
558
0
  struct files_struct *fsp = state->fsp;
559
0
  struct share_mode_entry e = { .pid = { .pid = 0, }};
560
0
  struct durable_reconnect_state rstate = { .op = state->op, .e = &e, };
561
0
  struct vfs_open_how how = { .flags = 0, };
562
0
  struct file_id file_id;
563
0
  bool have_share_mode_entry = false;
564
0
  uint32_t dosmode;
565
0
  int ret;
566
0
  bool ok;
567
568
0
  ok = share_mode_forall_entries(lck, durable_reconnect_fn, &rstate);
569
0
  if (!ok) {
570
0
    DBG_WARNING("share_mode_forall_entries failed\n");
571
0
    state->status = NT_STATUS_INTERNAL_DB_ERROR;
572
0
    goto fail;
573
0
  }
574
575
0
  if (e.pid.pid == 0) {
576
0
    DBG_WARNING("Did not find a unique valid share mode entry\n");
577
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
578
0
    goto fail;
579
0
  }
580
581
0
  if (!server_id_is_disconnected(&e.pid)) {
582
0
    DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
583
0
        "reconnect for handle that was not marked "
584
0
        "disconnected (e.g. smbd or cluster node died)\n"));
585
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
586
0
    goto fail;
587
0
  }
588
589
0
  if (e.share_file_id != state->op->global->open_persistent_id) {
590
0
    DBG_INFO("denying durable "
591
0
       "share_file_id changed %"PRIu64" != %"PRIu64" "
592
0
       "(e.g. another client had opened the file)\n",
593
0
       e.share_file_id,
594
0
       state->op->global->open_persistent_id);
595
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
596
0
    goto fail;
597
0
  }
598
599
0
  if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
600
0
      !CAN_WRITE(fsp->conn))
601
0
  {
602
0
    DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
603
0
        "share[%s] is not writeable anymore\n",
604
0
        lp_servicename(talloc_tos(), lp_sub, SNUM(fsp->conn))));
605
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
606
0
    goto fail;
607
0
  }
608
609
0
  fsp_apply_share_entry_flags(fsp, e.flags);
610
0
  fsp->open_time = e.time;
611
0
  fsp->access_mask = e.access_mask;
612
0
  fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
613
0
  fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
614
615
0
  fsp->oplock_type = e.op_type;
616
617
0
  if (fsp->oplock_type == LEASE_OPLOCK) {
618
0
    uint32_t current_state;
619
0
    uint16_t lease_version, epoch;
620
621
    /*
622
     * Ensure the existing client guid matches the
623
     * stored one in the share_mode_entry.
624
     */
625
0
    if (!GUID_equal(fsp_client_guid(fsp),
626
0
        &e.client_guid)) {
627
0
      state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
628
0
      goto fail;
629
0
    }
630
631
0
    state->status = leases_db_get(
632
0
      &e.client_guid,
633
0
      &e.lease_key,
634
0
      &fsp->file_id,
635
0
      &current_state, /* current_state */
636
0
      NULL, /* breaking */
637
0
      NULL, /* breaking_to_requested */
638
0
      NULL, /* breaking_to_required */
639
0
      &lease_version, /* lease_version */
640
0
      &epoch); /* epoch */
641
0
    if (!NT_STATUS_IS_OK(state->status)) {
642
0
      goto fail;
643
0
    }
644
645
0
    fsp->lease = find_fsp_lease(
646
0
      fsp,
647
0
      &e.lease_key,
648
0
      current_state,
649
0
      lease_version,
650
0
      epoch);
651
0
    if (fsp->lease == NULL) {
652
0
      state->status = NT_STATUS_NO_MEMORY;
653
0
      goto fail;
654
0
    }
655
0
  }
656
657
0
  fsp->initial_allocation_size = state->cookie.initial_allocation_size;
658
0
  fh_set_position_information(fsp->fh, state->cookie.position_information);
659
0
  fsp->fsp_flags.write_time_forced = state->cookie.write_time_forced;
660
661
0
  state->op->compat = fsp;
662
0
  fsp->op = state->op;
663
664
0
  ok = reset_share_mode_entry(
665
0
    lck,
666
0
    e.pid,
667
0
    e.share_file_id,
668
0
    messaging_server_id(fsp->conn->sconn->msg_ctx),
669
0
    state->smb1req->mid);
670
0
  if (!ok) {
671
0
    DBG_DEBUG("Could not set new share_mode_entry values\n");
672
0
    state->status = NT_STATUS_INTERNAL_ERROR;
673
0
    goto fail;
674
0
  }
675
0
  have_share_mode_entry = true;
676
677
0
  if (br_lck != NULL) {
678
0
    ok = brl_reconnect_disconnected(fsp, br_lck);
679
0
    if (!ok) {
680
0
      state->status = NT_STATUS_INTERNAL_ERROR;
681
0
      DBG_ERR("failed to reopen brlocks: %s\n",
682
0
        nt_errstr(state->status));
683
0
      goto fail;
684
0
    }
685
0
  }
686
687
  /*
688
   * TODO: properly calculate open flags
689
   */
690
0
  if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
691
0
    how.flags = O_RDWR;
692
0
  } else if (fsp->fsp_flags.can_write) {
693
0
    how.flags = O_WRONLY;
694
0
  } else if (fsp->fsp_flags.can_read) {
695
0
    how.flags = O_RDONLY;
696
0
  }
697
698
0
  state->status = fd_openat(state->dirfsp, state->rel_fname, fsp, &how);
699
0
  if (!NT_STATUS_IS_OK(state->status)) {
700
0
    DBG_ERR("failed to open file: %s\n", nt_errstr(state->status));
701
0
    goto fail;
702
0
  }
703
704
  /*
705
   * We now check the stat info stored in the cookie against
706
   * the current stat data from the file we just opened.
707
   * If any detail differs, we deny the durable reconnect,
708
   * because in that case it is very likely that someone
709
   * opened the file while the handle was disconnected,
710
   * which has to be interpreted as an oplock break.
711
   */
712
713
0
  ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
714
0
  if (ret == -1) {
715
0
    state->status = map_nt_error_from_unix_common(errno);
716
0
    DBG_ERR("Unable to fstat stream: %s => %s\n",
717
0
      fsp_str_dbg(fsp),
718
0
      nt_errstr(state->status));
719
0
    goto fail;
720
0
  }
721
722
0
  if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
723
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
724
0
    goto fail;
725
0
  }
726
727
0
  file_id = vfs_file_id_from_sbuf(fsp->conn, &fsp->fsp_name->st);
728
0
  if (!file_id_equal(&state->cookie.id, &file_id)) {
729
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
730
0
    goto fail;
731
0
  }
732
733
0
  dosmode = fdos_mode(fsp);
734
0
  fsp->fsp_flags.is_sparse = (dosmode & FILE_ATTRIBUTE_SPARSE);
735
0
  fsp->fsp_flags.is_sparse |= e.flags & SHARE_ENTRY_FLAG_POSIX_OPEN;
736
737
0
  ok = vfs_default_durable_reconnect_check_stat(&state->cookie.stat_info,
738
0
                  fsp);
739
0
  if (!ok) {
740
0
    state->status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
741
0
    goto fail;
742
0
  }
743
744
0
  state->status = set_file_oplock(fsp);
745
0
  if (!NT_STATUS_IS_OK(state->status)) {
746
0
    goto fail;
747
0
  }
748
749
0
  state->status = vfs_default_durable_cookie(fsp,
750
0
               state->mem_ctx,
751
0
               &state->new_cookie_blob);
752
0
  if (!NT_STATUS_IS_OK(state->status)) {
753
0
    DBG_ERR("vfs_default_durable_cookie - %s\n",
754
0
      nt_errstr(state->status));
755
0
    goto fail;
756
0
  }
757
758
0
  state->smb1req->chain_fsp = fsp;
759
0
  state->smb1req->smb2req->compat_chain_fsp = fsp;
760
761
0
  DBG_DEBUG("opened file '%s'\n", fsp_str_dbg(fsp));
762
763
0
  fsp->fsp_flags.is_fsa = true;
764
765
0
  state->status = NT_STATUS_OK;
766
0
  return;
767
768
0
fail:
769
0
  if (have_share_mode_entry) {
770
    /*
771
     * Something is screwed up, delete the sharemode entry.
772
     */
773
0
    del_share_mode(lck, fsp);
774
0
  }
775
0
  if (fsp_get_pathref_fd(fsp) != -1) {
776
0
    NTSTATUS close_status;
777
0
    close_status = fd_close(fsp);
778
0
    if (!NT_STATUS_IS_OK(close_status)) {
779
0
      DBG_ERR("fd_close failed (%s), leaking fd\n",
780
0
        nt_errstr(close_status));
781
0
    }
782
0
  }
783
0
  state->op->compat = NULL;
784
0
  fsp->op = NULL;
785
0
}
786
787
NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
788
               struct smb_request *smb1req,
789
               struct smbXsrv_open *op,
790
               const DATA_BLOB old_cookie,
791
               TALLOC_CTX *mem_ctx,
792
               files_struct **result,
793
               DATA_BLOB *new_cookie)
794
0
{
795
0
  struct vfs_default_durable_reconnect_state state;
796
0
  struct smb_filename *smb_fname = NULL;
797
0
  struct file_id file_id;
798
0
  NTSTATUS status;
799
0
  enum ndr_err_code ndr_err;
800
0
  bool ok;
801
802
0
  *result = NULL;
803
0
  *new_cookie = data_blob_null;
804
805
0
  if (!lp_durable_handles(SNUM(conn))) {
806
0
    return NT_STATUS_NOT_SUPPORTED;
807
0
  }
808
809
0
  state = (struct vfs_default_durable_reconnect_state) {
810
0
    .mem_ctx = mem_ctx,
811
0
    .smb1req = smb1req,
812
0
    .op = op,
813
0
  };
814
815
  /*
816
   * the checks for kernel oplocks
817
   * and similar things are done
818
   * in the vfs_default_durable_cookie()
819
   * call below.
820
   */
821
822
0
  ndr_err = ndr_pull_struct_blob_all(
823
0
    &old_cookie,
824
0
    talloc_tos(),
825
0
    &state.cookie,
826
0
    (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
827
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
828
0
    status = ndr_map_error2ntstatus(ndr_err);
829
0
    return status;
830
0
  }
831
832
0
  if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
833
0
    DBG_DEBUG("Cookie:\n");
834
0
    NDR_PRINT_DEBUG(vfs_default_durable_cookie, &state.cookie);
835
0
  }
836
837
0
  if (strcmp(state.cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
838
0
    return NT_STATUS_INVALID_PARAMETER;
839
0
  }
840
841
0
  if (state.cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
842
0
    return NT_STATUS_INVALID_PARAMETER;
843
0
  }
844
845
0
  if (!state.cookie.allow_reconnect) {
846
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
847
0
  }
848
849
0
  if (strcmp(state.cookie.servicepath, conn->connectpath) != 0) {
850
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
851
0
  }
852
853
0
  status = filename_convert_dirfsp_rel(talloc_tos(),
854
0
               conn,
855
0
               conn->cwd_fsp,
856
0
               state.cookie.base_name,
857
0
               UCF_LCOMP_LNK_OK,
858
0
               0,
859
0
               &state.dirfsp,
860
0
               &smb_fname,
861
0
               &state.rel_fname);
862
0
  if (!NT_STATUS_IS_OK(status)) {
863
0
    return status;
864
0
  }
865
0
  if (!VALID_STAT(smb_fname->st)) {
866
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
867
0
  }
868
0
  if (!S_ISREG(smb_fname->st.st_ex_mode)) {
869
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
870
0
  }
871
872
0
  file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
873
0
  if (!file_id_equal(&state.cookie.id, &file_id)) {
874
0
    return NT_STATUS_OBJECT_NAME_NOT_FOUND;
875
0
  }
876
877
0
  state.fsp = fsp_new(conn, conn);
878
0
  if (state.fsp == NULL) {
879
0
    DBG_ERR("failed to create new fsp\n");
880
0
    return NT_STATUS_NO_MEMORY;
881
0
  }
882
0
  state.fsp->file_id = file_id;
883
0
  state.fsp->file_pid = smb1req->smbpid;
884
0
  state.fsp->vuid = smb1req->vuid;
885
0
  state.fsp->fnum = op->local_id;
886
0
  fh_set_gen_id(state.fsp->fh, op->global->open_global_id);
887
888
0
  ok = fsp_set_smb_fname(state.fsp, smb_fname);
889
0
  if (!ok) {
890
0
    DBG_ERR("fsp_set_smb_fname failed\n");
891
0
    file_free(smb1req, state.fsp);
892
0
    return NT_STATUS_NO_MEMORY;
893
0
  }
894
895
  /*
896
   * TODO:
897
   * Do we need to store the modified flag in the DB?
898
   */
899
0
  state.fsp->fsp_flags.modified = false;
900
  /*
901
   * no durables for directories
902
   */
903
0
  state.fsp->fsp_flags.is_directory = false;
904
  /*
905
   * For normal files, can_lock == !is_directory
906
   */
907
0
  state.fsp->fsp_flags.can_lock = true;
908
  /*
909
   * We do not support aio write behind for smb2
910
   */
911
0
  state.fsp->fsp_flags.aio_write_behind = false;
912
913
0
  status = share_mode_do_locked_brl(state.fsp,
914
0
            vfs_default_durable_reconnect_fn,
915
0
            &state);
916
0
  if (!NT_STATUS_IS_OK(status)) {
917
0
    DBG_ERR("share_mode_do_locked_brl [%s] failed: %s\n",
918
0
      smb_fname_str_dbg(smb_fname), nt_errstr(status));
919
0
    file_free(smb1req, state.fsp);
920
0
    return status;
921
0
  }
922
0
  if (!NT_STATUS_IS_OK(state.status)) {
923
0
    DBG_ERR("default_durable_reconnect_fn [%s] failed: %s\n",
924
0
      smb_fname_str_dbg(smb_fname),
925
0
      nt_errstr(state.status));
926
0
    file_free(smb1req, state.fsp);
927
0
    return state.status;
928
0
  }
929
930
0
  *result = state.fsp;
931
0
  *new_cookie = state.new_cookie_blob;
932
933
0
  return NT_STATUS_OK;
934
0
}