s3/torture: use stack buffer for rbtree loop
[Samba.git] / source3 / smbd / durable.c
blob2265d7c51025f94c0fb1a06539cb55225ac54089
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 "smbd/smbd.h"
27 #include "smbd/globals.h"
28 #include "libcli/security/security.h"
29 #include "messages.h"
30 #include "librpc/gen_ndr/ndr_open_files.h"
31 #include "serverid.h"
32 #include "fake_file.h"
33 #include "locking/leases_db.h"
35 NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
36 TALLOC_CTX *mem_ctx,
37 DATA_BLOB *cookie_blob)
39 struct connection_struct *conn = fsp->conn;
40 enum ndr_err_code ndr_err;
41 struct vfs_default_durable_cookie cookie;
43 if (!lp_durable_handles(SNUM(conn))) {
44 return NT_STATUS_NOT_SUPPORTED;
47 if (lp_kernel_share_modes(SNUM(conn))) {
49 * We do not support durable handles
50 * if kernel share modes (flocks) are used
52 return NT_STATUS_NOT_SUPPORTED;
55 if (lp_kernel_oplocks(SNUM(conn))) {
57 * We do not support durable handles
58 * if kernel oplocks are used
60 return NT_STATUS_NOT_SUPPORTED;
63 if ((fsp->current_lock_count > 0) &&
64 lp_posix_locking(fsp->conn->params))
67 * We do not support durable handles
68 * if the handle has posix locks.
70 return NT_STATUS_NOT_SUPPORTED;
73 if (fsp->fsp_flags.is_directory) {
74 return NT_STATUS_NOT_SUPPORTED;
77 if (fsp->fh->fd == -1) {
78 return NT_STATUS_NOT_SUPPORTED;
81 if (is_ntfs_stream_smb_fname(fsp->fsp_name)) {
83 * We do not support durable handles
84 * on streams for now.
86 return NT_STATUS_NOT_SUPPORTED;
89 if (is_fake_file(fsp->fsp_name)) {
91 * We do not support durable handles
92 * on fake files.
94 return NT_STATUS_NOT_SUPPORTED;
97 ZERO_STRUCT(cookie);
98 cookie.allow_reconnect = false;
99 cookie.id = fsp->file_id;
100 cookie.servicepath = conn->connectpath;
101 cookie.base_name = fsp->fsp_name->base_name;
102 cookie.initial_allocation_size = fsp->initial_allocation_size;
103 cookie.position_information = fsp->fh->position_information;
104 cookie.update_write_time_triggered =
105 fsp->fsp_flags.update_write_time_triggered;
106 cookie.update_write_time_on_close =
107 fsp->fsp_flags.update_write_time_on_close;
108 cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
109 cookie.close_write_time = full_timespec_to_nt_time(
110 &fsp->close_write_time);
112 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
113 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
114 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
115 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
116 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
117 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
118 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
119 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
120 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
121 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
122 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
123 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
124 cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
125 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
126 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
127 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
129 ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
130 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
131 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
132 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
133 return status;
136 return NT_STATUS_OK;
139 NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
140 const DATA_BLOB old_cookie,
141 TALLOC_CTX *mem_ctx,
142 DATA_BLOB *new_cookie)
144 struct connection_struct *conn = fsp->conn;
145 NTSTATUS status;
146 enum ndr_err_code ndr_err;
147 struct vfs_default_durable_cookie cookie;
148 DATA_BLOB new_cookie_blob = data_blob_null;
149 struct share_mode_lock *lck;
150 bool ok;
152 *new_cookie = data_blob_null;
154 ZERO_STRUCT(cookie);
156 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
157 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
158 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
159 status = ndr_map_error2ntstatus(ndr_err);
160 return status;
163 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
164 return NT_STATUS_INVALID_PARAMETER;
167 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
168 return NT_STATUS_INVALID_PARAMETER;
171 if (!file_id_equal(&fsp->file_id, &cookie.id)) {
172 return NT_STATUS_INVALID_PARAMETER;
175 if ((fsp_lease_type(fsp) & SMB2_LEASE_HANDLE) == 0) {
176 return NT_STATUS_NOT_SUPPORTED;
180 * For now let it be simple and do not keep
181 * delete on close files durable open
183 if (fsp->fsp_flags.initial_delete_on_close) {
184 return NT_STATUS_NOT_SUPPORTED;
186 if (fsp->fsp_flags.delete_on_close) {
187 return NT_STATUS_NOT_SUPPORTED;
190 if (!VALID_STAT(fsp->fsp_name->st)) {
191 return NT_STATUS_NOT_SUPPORTED;
194 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
195 return NT_STATUS_NOT_SUPPORTED;
198 /* Ensure any pending write time updates are done. */
199 if (fsp->update_write_time_event) {
200 fsp_flush_write_time_update(fsp);
204 * The above checks are done in mark_share_mode_disconnected() too
205 * but we want to avoid getting the lock if possible
207 lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
208 if (lck != NULL) {
209 struct smb_file_time ft;
211 init_smb_file_time(&ft);
213 if (fsp->fsp_flags.write_time_forced) {
214 ft.mtime = nt_time_to_full_timespec(
215 lck->data->changed_write_time);
216 } else if (fsp->fsp_flags.update_write_time_on_close) {
217 if (is_omit_timespec(&fsp->close_write_time)) {
218 ft.mtime = timespec_current();
219 } else {
220 ft.mtime = fsp->close_write_time;
224 if (!is_omit_timespec(&ft.mtime)) {
225 round_timespec(conn->ts_res, &ft.mtime);
226 file_ntimes(conn, fsp->fsp_name, &ft);
229 ok = mark_share_mode_disconnected(lck, fsp);
230 if (!ok) {
231 TALLOC_FREE(lck);
234 if (lck != NULL) {
235 ok = brl_mark_disconnected(fsp);
236 if (!ok) {
237 TALLOC_FREE(lck);
240 if (lck == NULL) {
241 return NT_STATUS_NOT_SUPPORTED;
243 TALLOC_FREE(lck);
245 status = vfs_stat_fsp(fsp);
246 if (!NT_STATUS_IS_OK(status)) {
247 return status;
250 ZERO_STRUCT(cookie);
251 cookie.allow_reconnect = true;
252 cookie.id = fsp->file_id;
253 cookie.servicepath = conn->connectpath;
254 cookie.base_name = fsp_str_dbg(fsp);
255 cookie.initial_allocation_size = fsp->initial_allocation_size;
256 cookie.position_information = fsp->fh->position_information;
257 cookie.update_write_time_triggered =
258 fsp->fsp_flags.update_write_time_triggered;
259 cookie.update_write_time_on_close =
260 fsp->fsp_flags.update_write_time_on_close;
261 cookie.write_time_forced = fsp->fsp_flags.write_time_forced;
262 cookie.close_write_time = full_timespec_to_nt_time(
263 &fsp->close_write_time);
265 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
266 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
267 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
268 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
269 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
270 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
271 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
272 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
273 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
274 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
275 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
276 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
277 cookie.stat_info.st_ex_iflags = fsp->fsp_name->st.st_ex_iflags;
278 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
279 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
280 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
282 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
283 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
284 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
285 status = ndr_map_error2ntstatus(ndr_err);
286 return status;
289 status = fd_close(fsp);
290 if (!NT_STATUS_IS_OK(status)) {
291 data_blob_free(&new_cookie_blob);
292 return status;
295 *new_cookie = new_cookie_blob;
296 return NT_STATUS_OK;
301 * Check whether a cookie-stored struct info is the same
302 * as a given SMB_STRUCT_STAT, as coming with the fsp.
304 static bool vfs_default_durable_reconnect_check_stat(
305 struct vfs_default_durable_stat *cookie_st,
306 SMB_STRUCT_STAT *fsp_st,
307 const char *name)
309 int ret;
311 if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
312 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
313 "stat_ex.%s differs: "
314 "cookie:%llu != stat:%llu, "
315 "denying durable reconnect\n",
316 name,
317 "st_ex_mode",
318 (unsigned long long)cookie_st->st_ex_mode,
319 (unsigned long long)fsp_st->st_ex_mode));
320 return false;
323 if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
324 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
325 "stat_ex.%s differs: "
326 "cookie:%llu != stat:%llu, "
327 "denying durable reconnect\n",
328 name,
329 "st_ex_nlink",
330 (unsigned long long)cookie_st->st_ex_nlink,
331 (unsigned long long)fsp_st->st_ex_nlink));
332 return false;
335 if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
336 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
337 "stat_ex.%s differs: "
338 "cookie:%llu != stat:%llu, "
339 "denying durable reconnect\n",
340 name,
341 "st_ex_uid",
342 (unsigned long long)cookie_st->st_ex_uid,
343 (unsigned long long)fsp_st->st_ex_uid));
344 return false;
347 if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
348 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
349 "stat_ex.%s differs: "
350 "cookie:%llu != stat:%llu, "
351 "denying durable reconnect\n",
352 name,
353 "st_ex_gid",
354 (unsigned long long)cookie_st->st_ex_gid,
355 (unsigned long long)fsp_st->st_ex_gid));
356 return false;
359 if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
360 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
361 "stat_ex.%s differs: "
362 "cookie:%llu != stat:%llu, "
363 "denying durable reconnect\n",
364 name,
365 "st_ex_rdev",
366 (unsigned long long)cookie_st->st_ex_rdev,
367 (unsigned long long)fsp_st->st_ex_rdev));
368 return false;
371 if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
372 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
373 "stat_ex.%s differs: "
374 "cookie:%llu != stat:%llu, "
375 "denying durable reconnect\n",
376 name,
377 "st_ex_size",
378 (unsigned long long)cookie_st->st_ex_size,
379 (unsigned long long)fsp_st->st_ex_size));
380 return false;
383 ret = timespec_compare(&cookie_st->st_ex_atime,
384 &fsp_st->st_ex_atime);
385 if (ret != 0) {
386 struct timeval tc, ts;
387 tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
388 ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
390 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
391 "stat_ex.%s differs: "
392 "cookie:'%s' != stat:'%s', "
393 "denying durable reconnect\n",
394 name,
395 "st_ex_atime",
396 timeval_string(talloc_tos(), &tc, true),
397 timeval_string(talloc_tos(), &ts, true)));
398 return false;
401 ret = timespec_compare(&cookie_st->st_ex_mtime,
402 &fsp_st->st_ex_mtime);
403 if (ret != 0) {
404 struct timeval tc, ts;
405 tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
406 ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
408 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
409 "stat_ex.%s differs: "
410 "cookie:'%s' != stat:'%s', "
411 "denying durable reconnect\n",
412 name,
413 "st_ex_mtime",
414 timeval_string(talloc_tos(), &tc, true),
415 timeval_string(talloc_tos(), &ts, true)));
416 return false;
419 ret = timespec_compare(&cookie_st->st_ex_ctime,
420 &fsp_st->st_ex_ctime);
421 if (ret != 0) {
422 struct timeval tc, ts;
423 tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
424 ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
426 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
427 "stat_ex.%s differs: "
428 "cookie:'%s' != stat:'%s', "
429 "denying durable reconnect\n",
430 name,
431 "st_ex_ctime",
432 timeval_string(talloc_tos(), &tc, true),
433 timeval_string(talloc_tos(), &ts, true)));
434 return false;
437 ret = timespec_compare(&cookie_st->st_ex_btime,
438 &fsp_st->st_ex_btime);
439 if (ret != 0) {
440 struct timeval tc, ts;
441 tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
442 ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
444 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
445 "stat_ex.%s differs: "
446 "cookie:'%s' != stat:'%s', "
447 "denying durable reconnect\n",
448 name,
449 "st_ex_btime",
450 timeval_string(talloc_tos(), &tc, true),
451 timeval_string(talloc_tos(), &ts, true)));
452 return false;
455 if (cookie_st->st_ex_iflags != fsp_st->st_ex_iflags) {
456 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
457 "stat_ex.%s differs: "
458 "cookie:%llu != stat:%llu, "
459 "denying durable reconnect\n",
460 name,
461 "st_ex_calculated_birthtime",
462 (unsigned long long)cookie_st->st_ex_iflags,
463 (unsigned long long)fsp_st->st_ex_iflags));
464 return false;
467 if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
468 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
469 "stat_ex.%s differs: "
470 "cookie:%llu != stat:%llu, "
471 "denying durable reconnect\n",
472 name,
473 "st_ex_blksize",
474 (unsigned long long)cookie_st->st_ex_blksize,
475 (unsigned long long)fsp_st->st_ex_blksize));
476 return false;
479 if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
480 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
481 "stat_ex.%s differs: "
482 "cookie:%llu != stat:%llu, "
483 "denying durable reconnect\n",
484 name,
485 "st_ex_blocks",
486 (unsigned long long)cookie_st->st_ex_blocks,
487 (unsigned long long)fsp_st->st_ex_blocks));
488 return false;
491 if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
492 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
493 "stat_ex.%s differs: "
494 "cookie:%llu != stat:%llu, "
495 "denying durable reconnect\n",
496 name,
497 "st_ex_flags",
498 (unsigned long long)cookie_st->st_ex_flags,
499 (unsigned long long)fsp_st->st_ex_flags));
500 return false;
503 return true;
506 static bool durable_reconnect_fn(
507 struct share_mode_entry *e,
508 bool *modified,
509 void *private_data)
511 struct share_mode_entry *dst_e = private_data;
513 if (dst_e->pid.pid != 0) {
514 DBG_INFO("Found more than one entry, invalidating previous\n");
515 dst_e->pid.pid = 0;
516 return true; /* end the loop through share mode entries */
518 *dst_e = *e;
519 return false; /* Look at potential other entries */
522 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
523 struct smb_request *smb1req,
524 struct smbXsrv_open *op,
525 const DATA_BLOB old_cookie,
526 TALLOC_CTX *mem_ctx,
527 files_struct **result,
528 DATA_BLOB *new_cookie)
530 const struct loadparm_substitution *lp_sub =
531 loadparm_s3_global_substitution();
532 struct share_mode_lock *lck;
533 struct share_mode_entry e;
534 struct files_struct *fsp = NULL;
535 NTSTATUS status;
536 bool ok;
537 int ret;
538 int flags = 0;
539 struct file_id file_id;
540 struct smb_filename *smb_fname = NULL;
541 enum ndr_err_code ndr_err;
542 struct vfs_default_durable_cookie cookie;
543 DATA_BLOB new_cookie_blob = data_blob_null;
545 *result = NULL;
546 *new_cookie = data_blob_null;
548 if (!lp_durable_handles(SNUM(conn))) {
549 return NT_STATUS_NOT_SUPPORTED;
553 * the checks for kernel oplocks
554 * and similar things are done
555 * in the vfs_default_durable_cookie()
556 * call below.
559 ndr_err = ndr_pull_struct_blob_all(
560 &old_cookie,
561 talloc_tos(),
562 &cookie,
563 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
564 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
565 status = ndr_map_error2ntstatus(ndr_err);
566 return status;
569 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
570 return NT_STATUS_INVALID_PARAMETER;
573 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
574 return NT_STATUS_INVALID_PARAMETER;
577 if (!cookie.allow_reconnect) {
578 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
581 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
582 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
585 /* Create an smb_filename with stream_name == NULL. */
586 smb_fname = synthetic_smb_fname(talloc_tos(),
587 cookie.base_name,
588 NULL,
589 NULL,
592 if (smb_fname == NULL) {
593 return NT_STATUS_NO_MEMORY;
596 ret = SMB_VFS_LSTAT(conn, smb_fname);
597 if (ret == -1) {
598 status = map_nt_error_from_unix_common(errno);
599 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
600 smb_fname_str_dbg(smb_fname),
601 nt_errstr(status)));
602 return status;
605 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
606 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
609 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
610 if (!file_id_equal(&cookie.id, &file_id)) {
611 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
615 * 1. check entry in locking.tdb
618 lck = get_existing_share_mode_lock(mem_ctx, file_id);
619 if (lck == NULL) {
620 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
621 "not obtained from db\n"));
622 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
625 e = (struct share_mode_entry) { .pid.pid = 0 };
627 ok = share_mode_forall_entries(lck, durable_reconnect_fn, &e);
628 if (!ok) {
629 DBG_WARNING("share_mode_forall_entries failed\n");
630 TALLOC_FREE(lck);
631 return NT_STATUS_INTERNAL_DB_ERROR;
634 if (e.pid.pid == 0) {
635 DBG_WARNING("Did not find a unique valid share mode entry\n");
636 TALLOC_FREE(lck);
637 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
640 if (!server_id_is_disconnected(&e.pid)) {
641 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
642 "reconnect for handle that was not marked "
643 "disconnected (e.g. smbd or cluster node died)\n"));
644 TALLOC_FREE(lck);
645 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
648 if (e.share_file_id != op->global->open_persistent_id) {
649 DBG_INFO("denying durable "
650 "share_file_id changed %"PRIu64" != %"PRIu64" "
651 "(e.g. another client had opened the file)\n",
652 e.share_file_id,
653 op->global->open_persistent_id);
654 TALLOC_FREE(lck);
655 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
658 if ((e.access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
659 !CAN_WRITE(conn))
661 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
662 "share[%s] is not writeable anymore\n",
663 lp_servicename(talloc_tos(), lp_sub, SNUM(conn))));
664 TALLOC_FREE(lck);
665 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
669 * 2. proceed with opening file
672 status = fsp_new(conn, conn, &fsp);
673 if (!NT_STATUS_IS_OK(status)) {
674 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
675 "new fsp: %s\n", nt_errstr(status)));
676 TALLOC_FREE(lck);
677 return status;
680 fsp->fh->private_options = e.private_options;
681 fsp->file_id = file_id;
682 fsp->file_pid = smb1req->smbpid;
683 fsp->vuid = smb1req->vuid;
684 fsp->open_time = e.time;
685 fsp->access_mask = e.access_mask;
686 fsp->fsp_flags.can_read = ((fsp->access_mask & FILE_READ_DATA) != 0);
687 fsp->fsp_flags.can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
688 fsp->fnum = op->local_id;
689 fsp_set_gen_id(fsp);
692 * TODO:
693 * Do we need to store the modified flag in the DB?
695 fsp->fsp_flags.modified = false;
697 * no durables for directories
699 fsp->fsp_flags.is_directory = false;
701 * For normal files, can_lock == !is_directory
703 fsp->fsp_flags.can_lock = true;
705 * We do not support aio write behind for smb2
707 fsp->fsp_flags.aio_write_behind = false;
708 fsp->oplock_type = e.op_type;
710 if (fsp->oplock_type == LEASE_OPLOCK) {
711 uint32_t current_state;
712 uint16_t lease_version, epoch;
715 * Ensure the existing client guid matches the
716 * stored one in the share_mode_entry.
718 if (!GUID_equal(fsp_client_guid(fsp),
719 &e.client_guid)) {
720 TALLOC_FREE(lck);
721 fsp_free(fsp);
722 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
725 status = leases_db_get(
726 &e.client_guid,
727 &e.lease_key,
728 &file_id,
729 &current_state, /* current_state */
730 NULL, /* breaking */
731 NULL, /* breaking_to_requested */
732 NULL, /* breaking_to_required */
733 &lease_version, /* lease_version */
734 &epoch); /* epoch */
735 if (!NT_STATUS_IS_OK(status)) {
736 TALLOC_FREE(lck);
737 fsp_free(fsp);
738 return status;
741 fsp->lease = find_fsp_lease(
742 fsp,
743 &e.lease_key,
744 current_state,
745 lease_version,
746 epoch);
747 if (fsp->lease == NULL) {
748 TALLOC_FREE(lck);
749 fsp_free(fsp);
750 return NT_STATUS_NO_MEMORY;
754 fsp->initial_allocation_size = cookie.initial_allocation_size;
755 fsp->fh->position_information = cookie.position_information;
756 fsp->fsp_flags.update_write_time_triggered =
757 cookie.update_write_time_triggered;
758 fsp->fsp_flags.update_write_time_on_close =
759 cookie.update_write_time_on_close;
760 fsp->fsp_flags.write_time_forced = cookie.write_time_forced;
761 fsp->close_write_time = nt_time_to_full_timespec(
762 cookie.close_write_time);
764 /* TODO: real dirfsp... */
765 fsp->dirfsp = fsp->conn->cwd_fsp;
767 status = fsp_set_smb_fname(fsp, smb_fname);
768 if (!NT_STATUS_IS_OK(status)) {
769 TALLOC_FREE(lck);
770 fsp_free(fsp);
771 DEBUG(0, ("vfs_default_durable_reconnect: "
772 "fsp_set_smb_fname failed: %s\n",
773 nt_errstr(status)));
774 return status;
777 op->compat = fsp;
778 fsp->op = op;
780 ok = reset_share_mode_entry(
781 lck,
782 e.pid,
783 e.share_file_id,
784 messaging_server_id(conn->sconn->msg_ctx),
785 smb1req->mid,
786 fsp->fh->gen_id);
787 if (!ok) {
788 DBG_DEBUG("Could not set new share_mode_entry values\n");
789 TALLOC_FREE(lck);
790 op->compat = NULL;
791 fsp_free(fsp);
792 return NT_STATUS_INTERNAL_ERROR;
795 ok = brl_reconnect_disconnected(fsp);
796 if (!ok) {
797 status = NT_STATUS_INTERNAL_ERROR;
798 DEBUG(1, ("vfs_default_durable_reconnect: "
799 "failed to reopen brlocks: %s\n",
800 nt_errstr(status)));
801 TALLOC_FREE(lck);
802 op->compat = NULL;
803 fsp_free(fsp);
804 return status;
808 * TODO: properly calculate open flags
810 if (fsp->fsp_flags.can_write && fsp->fsp_flags.can_read) {
811 flags = O_RDWR;
812 } else if (fsp->fsp_flags.can_write) {
813 flags = O_WRONLY;
814 } else if (fsp->fsp_flags.can_read) {
815 flags = O_RDONLY;
818 status = fd_openat(fsp, flags, 0);
819 if (!NT_STATUS_IS_OK(status)) {
820 TALLOC_FREE(lck);
821 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
822 "file: %s\n", nt_errstr(status)));
823 op->compat = NULL;
824 fsp_free(fsp);
825 return status;
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 ret = SMB_VFS_CLOSE(fsp);
844 if (ret == -1) {
845 DEBUG(0, ("vfs_default_durable_reconnect: "
846 "SMB_VFS_CLOSE failed (%s) - leaking file "
847 "descriptor\n", strerror(errno)));
849 TALLOC_FREE(lck);
850 op->compat = NULL;
851 fsp_free(fsp);
852 return status;
855 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
856 ret = SMB_VFS_CLOSE(fsp);
857 if (ret == -1) {
858 DEBUG(0, ("vfs_default_durable_reconnect: "
859 "SMB_VFS_CLOSE failed (%s) - leaking file "
860 "descriptor\n", strerror(errno)));
862 TALLOC_FREE(lck);
863 op->compat = NULL;
864 fsp_free(fsp);
865 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
868 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
869 if (!file_id_equal(&cookie.id, &file_id)) {
870 ret = SMB_VFS_CLOSE(fsp);
871 if (ret == -1) {
872 DEBUG(0, ("vfs_default_durable_reconnect: "
873 "SMB_VFS_CLOSE failed (%s) - leaking file "
874 "descriptor\n", strerror(errno)));
876 TALLOC_FREE(lck);
877 op->compat = NULL;
878 fsp_free(fsp);
879 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
882 (void)dos_mode(fsp->conn, fsp->fsp_name);
884 ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
885 &fsp->fsp_name->st,
886 fsp_str_dbg(fsp));
887 if (!ok) {
888 ret = SMB_VFS_CLOSE(fsp);
889 if (ret == -1) {
890 DEBUG(0, ("vfs_default_durable_reconnect: "
891 "SMB_VFS_CLOSE failed (%s) - leaking file "
892 "descriptor\n", strerror(errno)));
894 TALLOC_FREE(lck);
895 op->compat = NULL;
896 fsp_free(fsp);
897 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
900 status = set_file_oplock(fsp);
901 if (!NT_STATUS_IS_OK(status)) {
902 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
903 "after opening file: %s\n", nt_errstr(status)));
904 ret = SMB_VFS_CLOSE(fsp);
905 if (ret == -1) {
906 DEBUG(0, ("vfs_default_durable_reconnect: "
907 "SMB_VFS_CLOSE failed (%s) - leaking file "
908 "descriptor\n", strerror(errno)));
910 TALLOC_FREE(lck);
911 op->compat = NULL;
912 fsp_free(fsp);
913 return status;
916 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
917 if (!NT_STATUS_IS_OK(status)) {
918 TALLOC_FREE(lck);
919 DEBUG(1, ("vfs_default_durable_reconnect: "
920 "vfs_default_durable_cookie - %s\n",
921 nt_errstr(status)));
922 op->compat = NULL;
923 fsp_free(fsp);
924 return status;
927 smb1req->chain_fsp = fsp;
928 smb1req->smb2req->compat_chain_fsp = fsp;
930 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
931 fsp_str_dbg(fsp)));
934 * release the sharemode lock: this writes the changes
936 lck->data->modified = true;
937 TALLOC_FREE(lck);
939 *result = fsp;
940 *new_cookie = new_cookie_blob;
942 return NT_STATUS_OK;