shadow_copy2: add comment block explaining shadow_copy2_convert()
[Samba.git] / source3 / smbd / durable.c
blob5d276f38ec92a7ff8ba0f0c4cdf8319ac4365798
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 "smbd/smbd.h"
26 #include "smbd/globals.h"
27 #include "libcli/security/security.h"
28 #include "messages.h"
29 #include "librpc/gen_ndr/ndr_open_files.h"
30 #include "serverid.h"
31 #include "fake_file.h"
33 NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
34 TALLOC_CTX *mem_ctx,
35 DATA_BLOB *cookie_blob)
37 struct connection_struct *conn = fsp->conn;
38 enum ndr_err_code ndr_err;
39 struct vfs_default_durable_cookie cookie;
41 if (!lp_durable_handles(SNUM(conn))) {
42 return NT_STATUS_NOT_SUPPORTED;
45 if (lp_kernel_share_modes(SNUM(conn))) {
47 * We do not support durable handles
48 * if kernel share modes (flocks) are used
50 return NT_STATUS_NOT_SUPPORTED;
53 if (lp_kernel_oplocks(SNUM(conn))) {
55 * We do not support durable handles
56 * if kernel oplocks are used
58 return NT_STATUS_NOT_SUPPORTED;
61 if ((fsp->current_lock_count > 0) &&
62 lp_posix_locking(fsp->conn->params))
65 * We do not support durable handles
66 * if the handle has posix locks.
68 return NT_STATUS_NOT_SUPPORTED;
71 if (fsp->is_directory) {
72 return NT_STATUS_NOT_SUPPORTED;
75 if (fsp->fh->fd == -1) {
76 return NT_STATUS_NOT_SUPPORTED;
79 if (is_ntfs_stream_smb_fname(fsp->fsp_name)) {
81 * We do not support durable handles
82 * on streams for now.
84 return NT_STATUS_NOT_SUPPORTED;
87 if (is_fake_file(fsp->fsp_name)) {
89 * We do not support durable handles
90 * on fake files.
92 return NT_STATUS_NOT_SUPPORTED;
95 ZERO_STRUCT(cookie);
96 cookie.allow_reconnect = false;
97 cookie.id = fsp->file_id;
98 cookie.servicepath = conn->connectpath;
99 cookie.base_name = fsp->fsp_name->base_name;
100 cookie.initial_allocation_size = fsp->initial_allocation_size;
101 cookie.position_information = fsp->fh->position_information;
102 cookie.update_write_time_triggered = fsp->update_write_time_triggered;
103 cookie.update_write_time_on_close = fsp->update_write_time_on_close;
104 cookie.write_time_forced = fsp->write_time_forced;
105 cookie.close_write_time = fsp->close_write_time;
107 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
108 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
109 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
110 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
111 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
112 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
113 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
114 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
115 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
116 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
117 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
118 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
119 cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
120 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
121 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
122 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
123 cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;
124 cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private;
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 (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
173 return NT_STATUS_NOT_SUPPORTED;
176 if (fsp->num_pending_break_messages > 0) {
177 return NT_STATUS_NOT_SUPPORTED;
181 * For now let it be simple and do not keep
182 * delete on close files durable open
184 if (fsp->initial_delete_on_close) {
185 return NT_STATUS_NOT_SUPPORTED;
187 if (fsp->delete_on_close) {
188 return NT_STATUS_NOT_SUPPORTED;
191 if (!VALID_STAT(fsp->fsp_name->st)) {
192 return NT_STATUS_NOT_SUPPORTED;
195 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
196 return NT_STATUS_NOT_SUPPORTED;
199 /* Ensure any pending write time updates are done. */
200 if (fsp->update_write_time_event) {
201 update_write_time_handler(fsp->conn->sconn->ev_ctx,
202 fsp->update_write_time_event,
203 timeval_current(),
204 (void *)fsp);
208 * The above checks are done in mark_share_mode_disconnected() too
209 * but we want to avoid getting the lock if possible
211 lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
212 if (lck != NULL) {
213 struct smb_file_time ft;
215 ZERO_STRUCT(ft);
217 if (fsp->write_time_forced) {
218 ft.mtime = lck->data->changed_write_time;
219 } else if (fsp->update_write_time_on_close) {
220 if (null_timespec(fsp->close_write_time)) {
221 ft.mtime = timespec_current();
222 } else {
223 ft.mtime = fsp->close_write_time;
227 if (!null_timespec(ft.mtime)) {
228 round_timespec(conn->ts_res, &ft.mtime);
229 file_ntimes(conn, fsp->fsp_name, &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->fsp_name->base_name;
258 cookie.initial_allocation_size = fsp->initial_allocation_size;
259 cookie.position_information = fsp->fh->position_information;
260 cookie.update_write_time_triggered = fsp->update_write_time_triggered;
261 cookie.update_write_time_on_close = fsp->update_write_time_on_close;
262 cookie.write_time_forced = fsp->write_time_forced;
263 cookie.close_write_time = 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_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
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;
281 cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;
282 cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private;
284 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
285 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
286 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
287 status = ndr_map_error2ntstatus(ndr_err);
288 return status;
291 status = fd_close(fsp);
292 if (!NT_STATUS_IS_OK(status)) {
293 data_blob_free(&new_cookie_blob);
294 return status;
297 *new_cookie = new_cookie_blob;
298 return NT_STATUS_OK;
303 * Check whether a cookie-stored struct info is the same
304 * as a given SMB_STRUCT_STAT, as coming with the fsp.
306 static bool vfs_default_durable_reconnect_check_stat(
307 struct vfs_default_durable_stat *cookie_st,
308 SMB_STRUCT_STAT *fsp_st,
309 const char *name)
311 int ret;
313 if (cookie_st->st_ex_dev != fsp_st->st_ex_dev) {
314 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
315 "stat_ex.%s differs: "
316 "cookie:%llu != stat:%llu, "
317 "denying durable reconnect\n",
318 name,
319 "st_ex_dev",
320 (unsigned long long)cookie_st->st_ex_dev,
321 (unsigned long long)fsp_st->st_ex_dev));
322 return false;
325 if (cookie_st->st_ex_ino != fsp_st->st_ex_ino) {
326 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
327 "stat_ex.%s differs: "
328 "cookie:%llu != stat:%llu, "
329 "denying durable reconnect\n",
330 name,
331 "st_ex_ino",
332 (unsigned long long)cookie_st->st_ex_ino,
333 (unsigned long long)fsp_st->st_ex_ino));
334 return false;
337 if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
338 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
339 "stat_ex.%s differs: "
340 "cookie:%llu != stat:%llu, "
341 "denying durable reconnect\n",
342 name,
343 "st_ex_mode",
344 (unsigned long long)cookie_st->st_ex_mode,
345 (unsigned long long)fsp_st->st_ex_mode));
346 return false;
349 if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
350 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
351 "stat_ex.%s differs: "
352 "cookie:%llu != stat:%llu, "
353 "denying durable reconnect\n",
354 name,
355 "st_ex_nlink",
356 (unsigned long long)cookie_st->st_ex_nlink,
357 (unsigned long long)fsp_st->st_ex_nlink));
358 return false;
361 if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
362 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
363 "stat_ex.%s differs: "
364 "cookie:%llu != stat:%llu, "
365 "denying durable reconnect\n",
366 name,
367 "st_ex_uid",
368 (unsigned long long)cookie_st->st_ex_uid,
369 (unsigned long long)fsp_st->st_ex_uid));
370 return false;
373 if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
374 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
375 "stat_ex.%s differs: "
376 "cookie:%llu != stat:%llu, "
377 "denying durable reconnect\n",
378 name,
379 "st_ex_gid",
380 (unsigned long long)cookie_st->st_ex_gid,
381 (unsigned long long)fsp_st->st_ex_gid));
382 return false;
385 if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
386 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
387 "stat_ex.%s differs: "
388 "cookie:%llu != stat:%llu, "
389 "denying durable reconnect\n",
390 name,
391 "st_ex_rdev",
392 (unsigned long long)cookie_st->st_ex_rdev,
393 (unsigned long long)fsp_st->st_ex_rdev));
394 return false;
397 if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
398 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
399 "stat_ex.%s differs: "
400 "cookie:%llu != stat:%llu, "
401 "denying durable reconnect\n",
402 name,
403 "st_ex_size",
404 (unsigned long long)cookie_st->st_ex_size,
405 (unsigned long long)fsp_st->st_ex_size));
406 return false;
409 ret = timespec_compare(&cookie_st->st_ex_atime,
410 &fsp_st->st_ex_atime);
411 if (ret != 0) {
412 struct timeval tc, ts;
413 tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
414 ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
416 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
417 "stat_ex.%s differs: "
418 "cookie:'%s' != stat:'%s', "
419 "denying durable reconnect\n",
420 name,
421 "st_ex_atime",
422 timeval_string(talloc_tos(), &tc, true),
423 timeval_string(talloc_tos(), &ts, true)));
424 return false;
427 ret = timespec_compare(&cookie_st->st_ex_mtime,
428 &fsp_st->st_ex_mtime);
429 if (ret != 0) {
430 struct timeval tc, ts;
431 tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
432 ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
434 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
435 "stat_ex.%s differs: "
436 "cookie:'%s' != stat:'%s', "
437 "denying durable reconnect\n",
438 name,
439 "st_ex_mtime",
440 timeval_string(talloc_tos(), &tc, true),
441 timeval_string(talloc_tos(), &ts, true)));
442 return false;
445 ret = timespec_compare(&cookie_st->st_ex_ctime,
446 &fsp_st->st_ex_ctime);
447 if (ret != 0) {
448 struct timeval tc, ts;
449 tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
450 ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
452 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
453 "stat_ex.%s differs: "
454 "cookie:'%s' != stat:'%s', "
455 "denying durable reconnect\n",
456 name,
457 "st_ex_ctime",
458 timeval_string(talloc_tos(), &tc, true),
459 timeval_string(talloc_tos(), &ts, true)));
460 return false;
463 ret = timespec_compare(&cookie_st->st_ex_btime,
464 &fsp_st->st_ex_btime);
465 if (ret != 0) {
466 struct timeval tc, ts;
467 tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
468 ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
470 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
471 "stat_ex.%s differs: "
472 "cookie:'%s' != stat:'%s', "
473 "denying durable reconnect\n",
474 name,
475 "st_ex_btime",
476 timeval_string(talloc_tos(), &tc, true),
477 timeval_string(talloc_tos(), &ts, true)));
478 return false;
481 if (cookie_st->st_ex_calculated_birthtime !=
482 fsp_st->st_ex_calculated_birthtime)
484 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
485 "stat_ex.%s differs: "
486 "cookie:%llu != stat:%llu, "
487 "denying durable reconnect\n",
488 name,
489 "st_ex_calculated_birthtime",
490 (unsigned long long)cookie_st->st_ex_calculated_birthtime,
491 (unsigned long long)fsp_st->st_ex_calculated_birthtime));
492 return false;
495 if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
496 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
497 "stat_ex.%s differs: "
498 "cookie:%llu != stat:%llu, "
499 "denying durable reconnect\n",
500 name,
501 "st_ex_blksize",
502 (unsigned long long)cookie_st->st_ex_blksize,
503 (unsigned long long)fsp_st->st_ex_blksize));
504 return false;
507 if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
508 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
509 "stat_ex.%s differs: "
510 "cookie:%llu != stat:%llu, "
511 "denying durable reconnect\n",
512 name,
513 "st_ex_blocks",
514 (unsigned long long)cookie_st->st_ex_blocks,
515 (unsigned long long)fsp_st->st_ex_blocks));
516 return false;
519 if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
520 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
521 "stat_ex.%s differs: "
522 "cookie:%llu != stat:%llu, "
523 "denying durable reconnect\n",
524 name,
525 "st_ex_flags",
526 (unsigned long long)cookie_st->st_ex_flags,
527 (unsigned long long)fsp_st->st_ex_flags));
528 return false;
531 if (cookie_st->st_ex_mask != fsp_st->st_ex_mask) {
532 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
533 "stat_ex.%s differs: "
534 "cookie:%llu != stat:%llu, "
535 "denying durable reconnect\n",
536 name,
537 "st_ex_mask",
538 (unsigned long long)cookie_st->st_ex_mask,
539 (unsigned long long)fsp_st->st_ex_mask));
540 return false;
543 if (cookie_st->vfs_private != fsp_st->vfs_private) {
544 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
545 "stat_ex.%s differs: "
546 "cookie:%llu != stat:%llu, "
547 "denying durable reconnect\n",
548 name,
549 "vfs_private",
550 (unsigned long long)cookie_st->vfs_private,
551 (unsigned long long)fsp_st->vfs_private));
552 return false;
555 return true;
558 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
559 struct smb_request *smb1req,
560 struct smbXsrv_open *op,
561 const DATA_BLOB old_cookie,
562 TALLOC_CTX *mem_ctx,
563 files_struct **result,
564 DATA_BLOB *new_cookie)
566 struct share_mode_lock *lck;
567 struct share_mode_entry *e;
568 struct files_struct *fsp = NULL;
569 NTSTATUS status;
570 bool ok;
571 int ret;
572 int flags = 0;
573 struct file_id file_id;
574 struct smb_filename *smb_fname = NULL;
575 enum ndr_err_code ndr_err;
576 struct vfs_default_durable_cookie cookie;
577 DATA_BLOB new_cookie_blob = data_blob_null;
579 *result = NULL;
580 *new_cookie = data_blob_null;
582 if (!lp_durable_handles(SNUM(conn))) {
583 return NT_STATUS_NOT_SUPPORTED;
587 * the checks for kernel oplocks
588 * and similar things are done
589 * in the vfs_default_durable_cookie()
590 * call below.
593 ZERO_STRUCT(cookie);
595 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
596 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
597 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
598 status = ndr_map_error2ntstatus(ndr_err);
599 return status;
602 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
603 return NT_STATUS_INVALID_PARAMETER;
606 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
607 return NT_STATUS_INVALID_PARAMETER;
610 if (!cookie.allow_reconnect) {
611 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
614 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
615 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
618 /* Create an smb_filename with stream_name == NULL. */
619 status = create_synthetic_smb_fname(talloc_tos(),
620 cookie.base_name,
621 NULL, NULL,
622 &smb_fname);
623 if (!NT_STATUS_IS_OK(status)) {
624 return status;
627 ret = SMB_VFS_LSTAT(conn, smb_fname);
628 if (ret == -1) {
629 status = map_nt_error_from_unix_common(errno);
630 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
631 smb_fname_str_dbg(smb_fname),
632 nt_errstr(status)));
633 return status;
636 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
637 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
640 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
641 if (!file_id_equal(&cookie.id, &file_id)) {
642 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
646 * 1. check entry in locking.tdb
649 lck = get_existing_share_mode_lock(mem_ctx, file_id);
650 if (lck == NULL) {
651 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
652 "not obtained from db\n"));
653 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
656 if (lck->data->num_share_modes == 0) {
657 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
658 "entry in existing share mode lock\n"));
659 TALLOC_FREE(lck);
660 return NT_STATUS_INTERNAL_DB_ERROR;
663 if (lck->data->num_share_modes > 1) {
665 * It can't be durable if there is more than one handle
666 * on the file.
668 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
669 "share-mode entry - can not be durable\n"));
670 TALLOC_FREE(lck);
671 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
674 e = &lck->data->share_modes[0];
676 if (!server_id_is_disconnected(&e->pid)) {
677 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
678 "reconnect for handle that was not marked "
679 "disconnected (e.g. smbd or cluster node died)\n"));
680 TALLOC_FREE(lck);
681 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
684 if (e->share_file_id != op->global->open_persistent_id) {
685 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
686 "share_file_id changed %llu != %llu"
687 "(e.g. another client had opened the file)\n",
688 (unsigned long long)e->share_file_id,
689 (unsigned long long)op->global->open_persistent_id));
690 TALLOC_FREE(lck);
691 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
694 if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
695 !CAN_WRITE(conn))
697 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
698 "share[%s] is not writeable anymore\n",
699 lp_servicename(talloc_tos(), SNUM(conn))));
700 TALLOC_FREE(lck);
701 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
705 * TODO:
706 * add scavenger timer functionality
708 * For now we always allow the reconnect
710 #if 0
711 expire_time = op->global->disconnect_time;
712 expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
713 if (expire < now) {
714 //TODO reopen and close before telling the client...
716 #endif
719 * 2. proceed with opening file
722 status = fsp_new(conn, conn, &fsp);
723 if (!NT_STATUS_IS_OK(status)) {
724 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
725 "new fsp: %s\n", nt_errstr(status)));
726 TALLOC_FREE(lck);
727 return status;
730 fsp->fh->private_options = e->private_options;
731 fsp->fh->gen_id = smbXsrv_open_hash(op);
732 fsp->file_id = file_id;
733 fsp->file_pid = smb1req->smbpid;
734 fsp->vuid = smb1req->vuid;
735 fsp->open_time = e->time;
736 fsp->access_mask = e->access_mask;
737 fsp->share_access = e->share_access;
738 fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
739 fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
742 * TODO:
743 * Do we need to store the modified flag in the DB?
744 * How to handle update_write_time and friends
745 * during a disconnected client on a durable handle?
747 fsp->modified = false;
749 * no durables for directories
751 fsp->is_directory = false;
753 * For normal files, can_lock == !is_directory
755 fsp->can_lock = true;
757 * We do not support aio write behind for smb2
759 fsp->aio_write_behind = false;
760 fsp->oplock_type = e->op_type;
762 fsp->initial_allocation_size = cookie.initial_allocation_size;
763 fsp->fh->position_information = cookie.position_information;
764 fsp->update_write_time_triggered = cookie.update_write_time_triggered;
765 fsp->update_write_time_on_close = cookie.update_write_time_on_close;
766 fsp->write_time_forced = cookie.write_time_forced;
767 fsp->close_write_time = cookie.close_write_time;
769 status = fsp_set_smb_fname(fsp, smb_fname);
770 if (!NT_STATUS_IS_OK(status)) {
771 TALLOC_FREE(lck);
772 fsp_free(fsp);
773 DEBUG(0, ("vfs_default_durable_reconnect: "
774 "fsp_set_smb_fname failed: %s\n",
775 nt_errstr(status)));
776 return status;
779 op->compat = fsp;
780 fsp->op = op;
782 e->pid = messaging_server_id(conn->sconn->msg_ctx);
783 e->op_mid = smb1req->mid;
784 e->share_file_id = fsp->fh->gen_id;
786 ok = brl_reconnect_disconnected(fsp);
787 if (!ok) {
788 status = NT_STATUS_INTERNAL_ERROR;
789 DEBUG(1, ("vfs_default_durable_reconnect: "
790 "failed to reopen brlocks: %s\n",
791 nt_errstr(status)));
792 TALLOC_FREE(lck);
793 op->compat = NULL;
794 fsp_free(fsp);
795 return status;
799 * TODO: properly calculate open flags
801 if (fsp->can_write && fsp->can_read) {
802 flags = O_RDWR;
803 } else if (fsp->can_write) {
804 flags = O_WRONLY;
805 } else if (fsp->can_read) {
806 flags = O_RDONLY;
809 status = fd_open(conn, fsp, flags, 0 /* mode */);
810 if (!NT_STATUS_IS_OK(status)) {
811 TALLOC_FREE(lck);
812 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
813 "file: %s\n", nt_errstr(status)));
814 op->compat = NULL;
815 fsp_free(fsp);
816 return status;
820 * We now check the stat info stored in the cookie against
821 * the current stat data from the file we just opened.
822 * If any detail differs, we deny the durable reconnect,
823 * because in that case it is very likely that someone
824 * opened the file while the handle was disconnected,
825 * which has to be interpreted as an oplock break.
828 ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
829 if (ret == -1) {
830 status = map_nt_error_from_unix_common(errno);
831 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
832 smb_fname_str_dbg(smb_fname),
833 nt_errstr(status)));
834 ret = SMB_VFS_CLOSE(fsp);
835 if (ret == -1) {
836 DEBUG(0, ("vfs_default_durable_reconnect: "
837 "SMB_VFS_CLOSE failed (%s) - leaking file "
838 "descriptor\n", strerror(errno)));
840 TALLOC_FREE(lck);
841 op->compat = NULL;
842 fsp_free(fsp);
843 return status;
846 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
847 ret = SMB_VFS_CLOSE(fsp);
848 if (ret == -1) {
849 DEBUG(0, ("vfs_default_durable_reconnect: "
850 "SMB_VFS_CLOSE failed (%s) - leaking file "
851 "descriptor\n", strerror(errno)));
853 TALLOC_FREE(lck);
854 op->compat = NULL;
855 fsp_free(fsp);
856 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
859 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
860 if (!file_id_equal(&cookie.id, &file_id)) {
861 ret = SMB_VFS_CLOSE(fsp);
862 if (ret == -1) {
863 DEBUG(0, ("vfs_default_durable_reconnect: "
864 "SMB_VFS_CLOSE failed (%s) - leaking file "
865 "descriptor\n", strerror(errno)));
867 TALLOC_FREE(lck);
868 op->compat = NULL;
869 fsp_free(fsp);
870 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
873 ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
874 &fsp->fsp_name->st,
875 fsp_str_dbg(fsp));
876 if (!ok) {
877 ret = SMB_VFS_CLOSE(fsp);
878 if (ret == -1) {
879 DEBUG(0, ("vfs_default_durable_reconnect: "
880 "SMB_VFS_CLOSE failed (%s) - leaking file "
881 "descriptor\n", strerror(errno)));
883 TALLOC_FREE(lck);
884 op->compat = NULL;
885 fsp_free(fsp);
886 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
889 status = set_file_oplock(fsp, e->op_type);
890 if (!NT_STATUS_IS_OK(status)) {
891 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
892 "after opening file: %s\n", nt_errstr(status)));
893 ret = SMB_VFS_CLOSE(fsp);
894 if (ret == -1) {
895 DEBUG(0, ("vfs_default_durable_reconnect: "
896 "SMB_VFS_CLOSE failed (%s) - leaking file "
897 "descriptor\n", strerror(errno)));
899 TALLOC_FREE(lck);
900 op->compat = NULL;
901 fsp_free(fsp);
902 return status;
905 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
906 if (!NT_STATUS_IS_OK(status)) {
907 TALLOC_FREE(lck);
908 DEBUG(1, ("vfs_default_durable_reconnect: "
909 "vfs_default_durable_cookie - %s\n",
910 nt_errstr(status)));
911 op->compat = NULL;
912 fsp_free(fsp);
913 return status;
916 smb1req->chain_fsp = fsp;
917 smb1req->smb2req->compat_chain_fsp = fsp;
919 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
920 fsp_str_dbg(fsp)));
923 * release the sharemode lock: this writes the changes
925 lck->data->modified = true;
926 TALLOC_FREE(lck);
928 *result = fsp;
929 *new_cookie = new_cookie_blob;
931 return NT_STATUS_OK;