smbd: avoid a panic in close_directory()
[samba4-gss.git] / source3 / smbd / durable.c
blobbd0c9f58e2408bcb2a3947d62b7f12896a98810e
1 /*
2 Unix SMB/CIFS implementation.
3 Durable Handle default VFS implementation
5 Copyright (C) Stefan Metzmacher 2012
6 Copyright (C) Michael Adam 2012
7 Copyright (C) Volker Lendecke 2012
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.
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.
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/>.
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"
36 NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
37 TALLOC_CTX *mem_ctx,
38 DATA_BLOB *cookie_blob)
40 struct connection_struct *conn = fsp->conn;
41 enum ndr_err_code ndr_err;
42 struct vfs_default_durable_cookie cookie;
44 if (!lp_durable_handles(SNUM(conn))) {
45 return NT_STATUS_NOT_SUPPORTED;
48 if (lp_kernel_share_modes(SNUM(conn))) {
50 * We do not support durable handles
51 * if file system sharemodes are used
53 return NT_STATUS_NOT_SUPPORTED;
56 if (lp_kernel_oplocks(SNUM(conn))) {
58 * We do not support durable handles
59 * if kernel oplocks are used
61 return NT_STATUS_NOT_SUPPORTED;
64 if ((fsp->current_lock_count > 0) &&
65 lp_posix_locking(fsp->conn->params))
68 * We do not support durable handles
69 * if the handle has posix locks.
71 return NT_STATUS_NOT_SUPPORTED;
74 if (fsp->fsp_flags.is_directory) {
75 return NT_STATUS_NOT_SUPPORTED;
78 if (fsp_is_alternate_stream(fsp)) {
80 * We do not support durable handles
81 * on streams for now.
83 return NT_STATUS_NOT_SUPPORTED;
86 if (is_fake_file(fsp->fsp_name)) {
88 * We do not support durable handles
89 * on fake files.
91 return NT_STATUS_NOT_SUPPORTED;
94 ZERO_STRUCT(cookie);
95 cookie.allow_reconnect = false;
96 cookie.id = fsp->file_id;
97 cookie.servicepath = conn->connectpath;
98 cookie.base_name = fsp->fsp_name->base_name;
99 cookie.initial_allocation_size = fsp->initial_allocation_size;
100 cookie.position_information = fh_get_position_information(fsp->fh);
101 cookie.update_write_time_triggered =
102 fsp->fsp_flags.update_write_time_triggered;
103 cookie.update_write_time_on_close =
104 fsp->fsp_flags.update_write_time_on_close;
105 cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
106 cookie.close_write_time = full_timespec_to_nt_time(
107 &fsp->close_write_time);
109 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
110 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
111 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
112 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
113 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
114 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
115 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
116 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
117 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
118 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
119 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
120 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
121 cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
122 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
123 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
124 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
126 ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
127 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
128 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
129 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
130 return status;
133 return NT_STATUS_OK;
136 NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
137 const DATA_BLOB old_cookie,
138 TALLOC_CTX *mem_ctx,
139 DATA_BLOB *new_cookie)
141 struct connection_struct *conn = fsp->conn;
142 NTSTATUS status;
143 enum ndr_err_code ndr_err;
144 struct vfs_default_durable_cookie cookie;
145 DATA_BLOB new_cookie_blob = data_blob_null;
146 struct share_mode_lock *lck;
147 bool ok;
149 *new_cookie = data_blob_null;
151 ZERO_STRUCT(cookie);
153 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
154 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
155 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
156 status = ndr_map_error2ntstatus(ndr_err);
157 return status;
160 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
161 return NT_STATUS_INVALID_PARAMETER;
164 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
165 return NT_STATUS_INVALID_PARAMETER;
168 if (!file_id_equal(&fsp->file_id, &cookie.id)) {
169 return NT_STATUS_INVALID_PARAMETER;
172 if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
173 return NT_STATUS_NOT_SUPPORTED;
176 if (fsp->current_lock_count != 0 &&
177 (fsp_lease_type(fsp) & SMB2_LEASE_WRITE) == 0)
179 return NT_STATUS_NOT_SUPPORTED;
183 * For now let it be simple and do not keep
184 * delete on close files durable open
186 if (fsp->fsp_flags.initial_delete_on_close) {
187 return NT_STATUS_NOT_SUPPORTED;
189 if (fsp->fsp_flags.delete_on_close) {
190 return NT_STATUS_NOT_SUPPORTED;
193 if (!VALID_STAT(fsp->fsp_name->st)) {
194 return NT_STATUS_NOT_SUPPORTED;
197 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
198 return NT_STATUS_NOT_SUPPORTED;
201 /* Ensure any pending write time updates are done. */
202 if (fsp->update_write_time_event) {
203 fsp_flush_write_time_update(fsp);
207 * The above checks are done in mark_share_mode_disconnected() too
208 * but we want to avoid getting the lock if possible
210 lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
211 if (lck != NULL) {
212 struct smb_file_time ft;
214 init_smb_file_time(&ft);
216 if (fsp->fsp_flags.write_time_forced) {
217 NTTIME mtime = share_mode_changed_write_time(lck);
218 ft.mtime = nt_time_to_full_timespec(mtime);
219 } else if (fsp->fsp_flags.update_write_time_on_close) {
220 if (is_omit_timespec(&fsp->close_write_time)) {
221 ft.mtime = timespec_current();
222 } else {
223 ft.mtime = fsp->close_write_time;
227 if (!is_omit_timespec(&ft.mtime)) {
228 round_timespec(conn->ts_res, &ft.mtime);
229 file_ntimes(conn, fsp, &ft);
232 ok = mark_share_mode_disconnected(lck, fsp);
233 if (!ok) {
234 TALLOC_FREE(lck);
237 if (lck != NULL) {
238 ok = brl_mark_disconnected(fsp);
239 if (!ok) {
240 TALLOC_FREE(lck);
243 if (lck == NULL) {
244 return NT_STATUS_NOT_SUPPORTED;
246 TALLOC_FREE(lck);
248 status = vfs_stat_fsp(fsp);
249 if (!NT_STATUS_IS_OK(status)) {
250 return status;
253 ZERO_STRUCT(cookie);
254 cookie.allow_reconnect = true;
255 cookie.id = fsp->file_id;
256 cookie.servicepath = conn->connectpath;
257 cookie.base_name = fsp_str_dbg(fsp);
258 cookie.initial_allocation_size = fsp->initial_allocation_size;
259 cookie.position_information = fh_get_position_information(fsp->fh);
260 cookie.update_write_time_triggered =
261 fsp->fsp_flags.update_write_time_triggered;
262 cookie.update_write_time_on_close =
263 fsp->fsp_flags.update_write_time_on_close;
264 cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
265 cookie.close_write_time = full_timespec_to_nt_time(
266 &fsp->close_write_time);
268 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
269 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
270 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
271 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
272 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
273 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
274 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
275 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
276 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
277 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
278 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
279 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
280 cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
281 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
282 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
283 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
285 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
286 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
287 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
288 status = ndr_map_error2ntstatus(ndr_err);
289 return status;
292 status = fd_close(fsp);
293 if (!NT_STATUS_IS_OK(status)) {
294 data_blob_free(&new_cookie_blob);
295 return status;
298 *new_cookie = new_cookie_blob;
299 return NT_STATUS_OK;
304 * Check whether a cookie-stored struct info is the same
305 * as a given SMB_STRUCT_STAT, as coming with the fsp.
307 static bool vfs_default_durable_reconnect_check_stat(
308 struct vfs_default_durable_stat *cookie_st,
309 SMB_STRUCT_STAT *fsp_st,
310 const char *name)
312 int ret;
314 if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
315 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
316 "stat_ex.%s differs: "
317 "cookie:%llu != stat:%llu, "
318 "denying durable reconnect\n",
319 name,
320 "st_ex_mode",
321 (unsigned long long)cookie_st->st_ex_mode,
322 (unsigned long long)fsp_st->st_ex_mode));
323 return false;
326 if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
327 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
328 "stat_ex.%s differs: "
329 "cookie:%llu != stat:%llu, "
330 "denying durable reconnect\n",
331 name,
332 "st_ex_nlink",
333 (unsigned long long)cookie_st->st_ex_nlink,
334 (unsigned long long)fsp_st->st_ex_nlink));
335 return false;
338 if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
339 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
340 "stat_ex.%s differs: "
341 "cookie:%llu != stat:%llu, "
342 "denying durable reconnect\n",
343 name,
344 "st_ex_uid",
345 (unsigned long long)cookie_st->st_ex_uid,
346 (unsigned long long)fsp_st->st_ex_uid));
347 return false;
350 if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
351 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
352 "stat_ex.%s differs: "
353 "cookie:%llu != stat:%llu, "
354 "denying durable reconnect\n",
355 name,
356 "st_ex_gid",
357 (unsigned long long)cookie_st->st_ex_gid,
358 (unsigned long long)fsp_st->st_ex_gid));
359 return false;
362 if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
363 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
364 "stat_ex.%s differs: "
365 "cookie:%llu != stat:%llu, "
366 "denying durable reconnect\n",
367 name,
368 "st_ex_rdev",
369 (unsigned long long)cookie_st->st_ex_rdev,
370 (unsigned long long)fsp_st->st_ex_rdev));
371 return false;
374 if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
375 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
376 "stat_ex.%s differs: "
377 "cookie:%llu != stat:%llu, "
378 "denying durable reconnect\n",
379 name,
380 "st_ex_size",
381 (unsigned long long)cookie_st->st_ex_size,
382 (unsigned long long)fsp_st->st_ex_size));
383 return false;
386 ret = timespec_compare(&cookie_st->st_ex_atime,
387 &fsp_st->st_ex_atime);
388 if (ret != 0) {
389 struct timeval tc, ts;
390 tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
391 ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
393 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
394 "stat_ex.%s differs: "
395 "cookie:'%s' != stat:'%s', "
396 "denying durable reconnect\n",
397 name,
398 "st_ex_atime",
399 timeval_string(talloc_tos(), &tc, true),
400 timeval_string(talloc_tos(), &ts, true)));
401 return false;
404 ret = timespec_compare(&cookie_st->st_ex_mtime,
405 &fsp_st->st_ex_mtime);
406 if (ret != 0) {
407 struct timeval tc, ts;
408 tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
409 ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
411 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
412 "stat_ex.%s differs: "
413 "cookie:'%s' != stat:'%s', "
414 "denying durable reconnect\n",
415 name,
416 "st_ex_mtime",
417 timeval_string(talloc_tos(), &tc, true),
418 timeval_string(talloc_tos(), &ts, true)));
419 return false;
422 ret = timespec_compare(&cookie_st->st_ex_ctime,
423 &fsp_st->st_ex_ctime);
424 if (ret != 0) {
425 struct timeval tc, ts;
426 tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
427 ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
429 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
430 "stat_ex.%s differs: "
431 "cookie:'%s' != stat:'%s', "
432 "denying durable reconnect\n",
433 name,
434 "st_ex_ctime",
435 timeval_string(talloc_tos(), &tc, true),
436 timeval_string(talloc_tos(), &ts, true)));
437 return false;
440 ret = timespec_compare(&cookie_st->st_ex_btime,
441 &fsp_st->st_ex_btime);
442 if (ret != 0) {
443 struct timeval tc, ts;
444 tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
445 ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
447 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
448 "stat_ex.%s differs: "
449 "cookie:'%s' != stat:'%s', "
450 "denying durable reconnect\n",
451 name,
452 "st_ex_btime",
453 timeval_string(talloc_tos(), &tc, true),
454 timeval_string(talloc_tos(), &ts, true)));
455 return false;
458 if (cookie_st->st_ex_iflags != fsp_st->st_ex_iflags) {
459 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
460 "stat_ex.%s differs: "
461 "cookie:%llu != stat:%llu, "
462 "denying durable reconnect\n",
463 name,
464 "st_ex_calculated_birthtime",
465 (unsigned long long)cookie_st->st_ex_iflags,
466 (unsigned long long)fsp_st->st_ex_iflags));
467 return false;
470 if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
471 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
472 "stat_ex.%s differs: "
473 "cookie:%llu != stat:%llu, "
474 "denying durable reconnect\n",
475 name,
476 "st_ex_blksize",
477 (unsigned long long)cookie_st->st_ex_blksize,
478 (unsigned long long)fsp_st->st_ex_blksize));
479 return false;
482 if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
483 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
484 "stat_ex.%s differs: "
485 "cookie:%llu != stat:%llu, "
486 "denying durable reconnect\n",
487 name,
488 "st_ex_blocks",
489 (unsigned long long)cookie_st->st_ex_blocks,
490 (unsigned long long)fsp_st->st_ex_blocks));
491 return false;
494 if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
495 DBG_WARNING(" (%s): "
496 "stat_ex.%s differs: "
497 "cookie:%"PRIu32" != stat:%"PRIu32", "
498 "denying durable reconnect\n",
499 name,
500 "st_ex_flags",
501 cookie_st->st_ex_flags,
502 fsp_st->st_ex_flags);
503 return false;
506 return true;
509 struct durable_reconnect_state {
510 struct smbXsrv_open *op;
511 struct share_mode_entry *e;
514 static bool durable_reconnect_fn(
515 struct share_mode_entry *e,
516 bool *modified,
517 void *private_data)
519 struct durable_reconnect_state *state = private_data;
520 uint64_t id = state->op->global->open_persistent_id;
522 if (e->share_file_id != id) {
523 return false; /* Look at potential other entries */
526 if (!server_id_is_disconnected(&e->pid)) {
527 return false; /* Look at potential other entries */
530 if (state->e->share_file_id == id) {
531 DBG_INFO("Found more than one entry, invalidating previous\n");
532 *state->e = (struct share_mode_entry) { .pid = { .pid = 0, }};
533 return true; /* end the loop through share mode entries */
535 *state->e = *e;
536 return false; /* Look at potential other entries */
539 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
540 struct smb_request *smb1req,
541 struct smbXsrv_open *op,
542 const DATA_BLOB old_cookie,
543 TALLOC_CTX *mem_ctx,
544 files_struct **result,
545 DATA_BLOB *new_cookie)
547 const struct loadparm_substitution *lp_sub =
548 loadparm_s3_global_substitution();
549 struct share_mode_lock *lck;
550 struct share_mode_entry e = { .pid = { .pid = 0, }};
551 struct durable_reconnect_state rstate = { .op = op, .e = &e, };
552 struct files_struct *fsp = NULL;
553 NTSTATUS status;
554 bool ok;
555 int ret;
556 struct vfs_open_how how = { .flags = 0, };
557 struct file_id file_id;
558 struct smb_filename *smb_fname = NULL;
559 enum ndr_err_code ndr_err;
560 struct vfs_default_durable_cookie cookie;
561 DATA_BLOB new_cookie_blob = data_blob_null;
562 bool have_share_mode_entry = false;
564 *result = NULL;
565 *new_cookie = data_blob_null;
567 if (!lp_durable_handles(SNUM(conn))) {
568 return NT_STATUS_NOT_SUPPORTED;
572 * the checks for kernel oplocks
573 * and similar things are done
574 * in the vfs_default_durable_cookie()
575 * call below.
578 ndr_err = ndr_pull_struct_blob_all(
579 &old_cookie,
580 talloc_tos(),
581 &cookie,
582 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
583 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
584 status = ndr_map_error2ntstatus(ndr_err);
585 return status;
588 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
589 return NT_STATUS_INVALID_PARAMETER;
592 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
593 return NT_STATUS_INVALID_PARAMETER;
596 if (!cookie.allow_reconnect) {
597 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
600 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
601 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
604 /* Create an smb_filename with stream_name == NULL. */
605 smb_fname = synthetic_smb_fname(talloc_tos(),
606 cookie.base_name,
607 NULL,
608 NULL,
611 if (smb_fname == NULL) {
612 return NT_STATUS_NO_MEMORY;
615 ret = SMB_VFS_LSTAT(conn, smb_fname);
616 if (ret == -1) {
617 status = map_nt_error_from_unix_common(errno);
618 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
619 smb_fname_str_dbg(smb_fname),
620 nt_errstr(status)));
621 return status;
624 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
625 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
628 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
629 if (!file_id_equal(&cookie.id, &file_id)) {
630 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
634 * 1. check entry in locking.tdb
637 lck = get_existing_share_mode_lock(mem_ctx, file_id);
638 if (lck == NULL) {
639 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
640 "not obtained from db\n"));
641 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
644 ok = share_mode_forall_entries(lck, durable_reconnect_fn, &rstate);
645 if (!ok) {
646 DBG_WARNING("share_mode_forall_entries failed\n");
647 status = NT_STATUS_INTERNAL_DB_ERROR;
648 goto fail;
651 if (e.pid.pid == 0) {
652 DBG_WARNING("Did not find a unique valid share mode entry\n");
653 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
654 goto fail;
657 if (!server_id_is_disconnected(&e.pid)) {
658 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
659 "reconnect for handle that was not marked "
660 "disconnected (e.g. smbd or cluster node died)\n"));
661 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
662 goto fail;
665 if (e.share_file_id != op->global->open_persistent_id) {
666 DBG_INFO("denying durable "
667 "share_file_id changed %"PRIu64" != %"PRIu64" "
668 "(e.g. another client had opened the file)\n",
669 e.share_file_id,
670 op->global->open_persistent_id);
671 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
672 goto fail;
675 if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
676 !CAN_WRITE(conn))
678 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
679 "share[%s] is not writeable anymore\n",
680 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
681 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
682 goto fail;
686 * 2. proceed with opening file
689 status = fsp_new(conn, conn, &fsp);
690 if (!NT_STATUS_IS_OK(status)) {
691 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
692 "new fsp: %s\n", nt_errstr(status)));
693 goto fail;
696 fh_set_private_options(fsp->fh, e.private_options);
697 fsp->file_id = file_id;
698 fsp->file_pid = smb1req->smbpid;
699 fsp->vuid = smb1req->vuid;
700 fsp->open_time = e.time;
701 fsp->access_mask = e.access_mask;
702 fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
703 fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
704 fsp->fnum = op->local_id;
705 fsp_set_gen_id(fsp);
708 * TODO:
709 * Do we need to store the modified flag in the DB?
711 fsp->fsp_flags.modified = false;
713 * no durables for directories
715 fsp->fsp_flags.is_directory = false;
717 * For normal files, can_lock == !is_directory
719 fsp->fsp_flags.can_lock = true;
721 * We do not support aio write behind for smb2
723 fsp->fsp_flags.aio_write_behind = false;
724 fsp->oplock_type = e.op_type;
726 if (fsp->oplock_type == LEASE_OPLOCK) {
727 uint32_t current_state;
728 uint16_t lease_version, epoch;
731 * Ensure the existing client guid matches the
732 * stored one in the share_mode_entry.
734 if (!GUID_equal(fsp_client_guid(fsp),
735 &e.client_guid)) {
736 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
737 goto fail;
740 status = leases_db_get(
741 &e.client_guid,
742 &e.lease_key,
743 &file_id,
744 &current_state, /* current_state */
745 NULL, /* breaking */
746 NULL, /* breaking_to_requested */
747 NULL, /* breaking_to_required */
748 &lease_version, /* lease_version */
749 &epoch); /* epoch */
750 if (!NT_STATUS_IS_OK(status)) {
751 goto fail;
754 fsp->lease = find_fsp_lease(
755 fsp,
756 &e.lease_key,
757 current_state,
758 lease_version,
759 epoch);
760 if (fsp->lease == NULL) {
761 status = NT_STATUS_NO_MEMORY;
762 goto fail;
766 fsp->initial_allocation_size = cookie.initial_allocation_size;
767 fh_set_position_information(fsp->fh, cookie.position_information);
768 fsp->fsp_flags.update_write_time_triggered =
769 cookie.update_write_time_triggered;
770 fsp->fsp_flags.update_write_time_on_close =
771 cookie.update_write_time_on_close;
772 fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
773 fsp->close_write_time = nt_time_to_full_timespec(
774 cookie.close_write_time);
776 status = fsp_set_smb_fname(fsp, smb_fname);
777 if (!NT_STATUS_IS_OK(status)) {
778 DEBUG(0, ("vfs_default_durable_reconnect: "
779 "fsp_set_smb_fname failed: %s\n",
780 nt_errstr(status)));
781 goto fail;
784 op->compat = fsp;
785 fsp->op = op;
787 ok = reset_share_mode_entry(
788 lck,
789 e.pid,
790 e.share_file_id,
791 messaging_server_id(conn->sconn->msg_ctx),
792 smb1req->mid,
793 fh_get_gen_id(fsp->fh));
794 if (!ok) {
795 DBG_DEBUG("Could not set new share_mode_entry values\n");
796 status = NT_STATUS_INTERNAL_ERROR;
797 goto fail;
799 have_share_mode_entry = true;
801 ok = brl_reconnect_disconnected(fsp);
802 if (!ok) {
803 status = NT_STATUS_INTERNAL_ERROR;
804 DEBUG(1, ("vfs_default_durable_reconnect: "
805 "failed to reopen brlocks: %s\n",
806 nt_errstr(status)));
807 goto fail;
811 * TODO: properly calculate open flags
813 if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
814 how.flags = O_RDWR;
815 } else if (fsp->fsp_flags.can_write) {
816 how.flags = O_WRONLY;
817 } else if (fsp->fsp_flags.can_read) {
818 how.flags = O_RDONLY;
821 status = fd_openat(conn->cwd_fsp, fsp->fsp_name, fsp, &how);
822 if (!NT_STATUS_IS_OK(status)) {
823 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
824 "file: %s\n", nt_errstr(status)));
825 goto fail;
829 * We now check the stat info stored in the cookie against
830 * the current stat data from the file we just opened.
831 * If any detail differs, we deny the durable reconnect,
832 * because in that case it is very likely that someone
833 * opened the file while the handle was disconnected,
834 * which has to be interpreted as an oplock break.
837 ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
838 if (ret == -1) {
839 status = map_nt_error_from_unix_common(errno);
840 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
841 smb_fname_str_dbg(smb_fname),
842 nt_errstr(status)));
843 goto fail;
846 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
847 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
848 goto fail;
851 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
852 if (!file_id_equal(&cookie.id, &file_id)) {
853 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
854 goto fail;
857 (void)fdos_mode(fsp);
859 ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
860 &fsp->fsp_name->st,
861 fsp_str_dbg(fsp));
862 if (!ok) {
863 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
864 goto fail;
867 status = set_file_oplock(fsp);
868 if (!NT_STATUS_IS_OK(status)) {
869 goto fail;
872 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
873 if (!NT_STATUS_IS_OK(status)) {
874 DEBUG(1, ("vfs_default_durable_reconnect: "
875 "vfs_default_durable_cookie - %s\n",
876 nt_errstr(status)));
877 goto fail;
880 smb1req->chain_fsp = fsp;
881 smb1req->smb2req->compat_chain_fsp = fsp;
883 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
884 fsp_str_dbg(fsp)));
886 TALLOC_FREE(lck);
888 fsp->fsp_flags.is_fsa = true;
890 *result = fsp;
891 *new_cookie = new_cookie_blob;
893 return NT_STATUS_OK;
895 fail:
896 if (fsp != NULL && have_share_mode_entry) {
898 * Something is screwed up, delete the sharemode entry.
900 del_share_mode(lck, fsp);
902 if (fsp != NULL && fsp_get_pathref_fd(fsp) != -1) {
903 NTSTATUS close_status;
904 close_status = fd_close(fsp);
905 if (!NT_STATUS_IS_OK(close_status)) {
906 DBG_ERR("fd_close failed (%s), leaking fd\n",
907 nt_errstr(close_status));
910 TALLOC_FREE(lck);
911 if (fsp != NULL) {
912 op->compat = NULL;
913 fsp->op = NULL;
914 file_free(smb1req, fsp);
916 return status;