s3:libsmb: Plumb cli_smb2_qpathinfo_basic() inside cli_qpathinfo_basic().
[Samba.git] / source3 / smbd / durable.c
blob9b05d48680e7894c238911df27635ae486fac6e2
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;
177 * For now let it be simple and do not keep
178 * delete on close files durable open
180 if (fsp->initial_delete_on_close) {
181 return NT_STATUS_NOT_SUPPORTED;
183 if (fsp->delete_on_close) {
184 return NT_STATUS_NOT_SUPPORTED;
187 if (!VALID_STAT(fsp->fsp_name->st)) {
188 return NT_STATUS_NOT_SUPPORTED;
191 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
192 return NT_STATUS_NOT_SUPPORTED;
195 /* Ensure any pending write time updates are done. */
196 if (fsp->update_write_time_event) {
197 update_write_time_handler(fsp->conn->sconn->ev_ctx,
198 fsp->update_write_time_event,
199 timeval_current(),
200 (void *)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 ZERO_STRUCT(ft);
213 if (fsp->write_time_forced) {
214 ft.mtime = lck->data->changed_write_time;
215 } else if (fsp->update_write_time_on_close) {
216 if (null_timespec(fsp->close_write_time)) {
217 ft.mtime = timespec_current();
218 } else {
219 ft.mtime = fsp->close_write_time;
223 if (!null_timespec(ft.mtime)) {
224 round_timespec(conn->ts_res, &ft.mtime);
225 file_ntimes(conn, fsp->fsp_name, &ft);
228 ok = mark_share_mode_disconnected(lck, fsp);
229 if (!ok) {
230 TALLOC_FREE(lck);
233 if (lck != NULL) {
234 ok = brl_mark_disconnected(fsp);
235 if (!ok) {
236 TALLOC_FREE(lck);
239 if (lck == NULL) {
240 return NT_STATUS_NOT_SUPPORTED;
242 TALLOC_FREE(lck);
244 status = vfs_stat_fsp(fsp);
245 if (!NT_STATUS_IS_OK(status)) {
246 return status;
249 ZERO_STRUCT(cookie);
250 cookie.allow_reconnect = true;
251 cookie.id = fsp->file_id;
252 cookie.servicepath = conn->connectpath;
253 cookie.base_name = fsp->fsp_name->base_name;
254 cookie.initial_allocation_size = fsp->initial_allocation_size;
255 cookie.position_information = fsp->fh->position_information;
256 cookie.update_write_time_triggered = fsp->update_write_time_triggered;
257 cookie.update_write_time_on_close = fsp->update_write_time_on_close;
258 cookie.write_time_forced = fsp->write_time_forced;
259 cookie.close_write_time = fsp->close_write_time;
261 cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev;
262 cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino;
263 cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode;
264 cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink;
265 cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid;
266 cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid;
267 cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev;
268 cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size;
269 cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime;
270 cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime;
271 cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime;
272 cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime;
273 cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime;
274 cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize;
275 cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks;
276 cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags;
277 cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask;
278 cookie.stat_info.vfs_private = fsp->fsp_name->st.vfs_private;
280 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
281 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
282 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
283 status = ndr_map_error2ntstatus(ndr_err);
284 return status;
287 status = fd_close(fsp);
288 if (!NT_STATUS_IS_OK(status)) {
289 data_blob_free(&new_cookie_blob);
290 return status;
293 *new_cookie = new_cookie_blob;
294 return NT_STATUS_OK;
299 * Check whether a cookie-stored struct info is the same
300 * as a given SMB_STRUCT_STAT, as coming with the fsp.
302 static bool vfs_default_durable_reconnect_check_stat(
303 struct vfs_default_durable_stat *cookie_st,
304 SMB_STRUCT_STAT *fsp_st,
305 const char *name)
307 int ret;
309 if (cookie_st->st_ex_dev != fsp_st->st_ex_dev) {
310 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
311 "stat_ex.%s differs: "
312 "cookie:%llu != stat:%llu, "
313 "denying durable reconnect\n",
314 name,
315 "st_ex_dev",
316 (unsigned long long)cookie_st->st_ex_dev,
317 (unsigned long long)fsp_st->st_ex_dev));
318 return false;
321 if (cookie_st->st_ex_ino != fsp_st->st_ex_ino) {
322 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
323 "stat_ex.%s differs: "
324 "cookie:%llu != stat:%llu, "
325 "denying durable reconnect\n",
326 name,
327 "st_ex_ino",
328 (unsigned long long)cookie_st->st_ex_ino,
329 (unsigned long long)fsp_st->st_ex_ino));
330 return false;
333 if (cookie_st->st_ex_mode != fsp_st->st_ex_mode) {
334 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
335 "stat_ex.%s differs: "
336 "cookie:%llu != stat:%llu, "
337 "denying durable reconnect\n",
338 name,
339 "st_ex_mode",
340 (unsigned long long)cookie_st->st_ex_mode,
341 (unsigned long long)fsp_st->st_ex_mode));
342 return false;
345 if (cookie_st->st_ex_nlink != fsp_st->st_ex_nlink) {
346 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
347 "stat_ex.%s differs: "
348 "cookie:%llu != stat:%llu, "
349 "denying durable reconnect\n",
350 name,
351 "st_ex_nlink",
352 (unsigned long long)cookie_st->st_ex_nlink,
353 (unsigned long long)fsp_st->st_ex_nlink));
354 return false;
357 if (cookie_st->st_ex_uid != fsp_st->st_ex_uid) {
358 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
359 "stat_ex.%s differs: "
360 "cookie:%llu != stat:%llu, "
361 "denying durable reconnect\n",
362 name,
363 "st_ex_uid",
364 (unsigned long long)cookie_st->st_ex_uid,
365 (unsigned long long)fsp_st->st_ex_uid));
366 return false;
369 if (cookie_st->st_ex_gid != fsp_st->st_ex_gid) {
370 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
371 "stat_ex.%s differs: "
372 "cookie:%llu != stat:%llu, "
373 "denying durable reconnect\n",
374 name,
375 "st_ex_gid",
376 (unsigned long long)cookie_st->st_ex_gid,
377 (unsigned long long)fsp_st->st_ex_gid));
378 return false;
381 if (cookie_st->st_ex_rdev != fsp_st->st_ex_rdev) {
382 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
383 "stat_ex.%s differs: "
384 "cookie:%llu != stat:%llu, "
385 "denying durable reconnect\n",
386 name,
387 "st_ex_rdev",
388 (unsigned long long)cookie_st->st_ex_rdev,
389 (unsigned long long)fsp_st->st_ex_rdev));
390 return false;
393 if (cookie_st->st_ex_size != fsp_st->st_ex_size) {
394 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
395 "stat_ex.%s differs: "
396 "cookie:%llu != stat:%llu, "
397 "denying durable reconnect\n",
398 name,
399 "st_ex_size",
400 (unsigned long long)cookie_st->st_ex_size,
401 (unsigned long long)fsp_st->st_ex_size));
402 return false;
405 ret = timespec_compare(&cookie_st->st_ex_atime,
406 &fsp_st->st_ex_atime);
407 if (ret != 0) {
408 struct timeval tc, ts;
409 tc = convert_timespec_to_timeval(cookie_st->st_ex_atime);
410 ts = convert_timespec_to_timeval(fsp_st->st_ex_atime);
412 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
413 "stat_ex.%s differs: "
414 "cookie:'%s' != stat:'%s', "
415 "denying durable reconnect\n",
416 name,
417 "st_ex_atime",
418 timeval_string(talloc_tos(), &tc, true),
419 timeval_string(talloc_tos(), &ts, true)));
420 return false;
423 ret = timespec_compare(&cookie_st->st_ex_mtime,
424 &fsp_st->st_ex_mtime);
425 if (ret != 0) {
426 struct timeval tc, ts;
427 tc = convert_timespec_to_timeval(cookie_st->st_ex_mtime);
428 ts = convert_timespec_to_timeval(fsp_st->st_ex_mtime);
430 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
431 "stat_ex.%s differs: "
432 "cookie:'%s' != stat:'%s', "
433 "denying durable reconnect\n",
434 name,
435 "st_ex_mtime",
436 timeval_string(talloc_tos(), &tc, true),
437 timeval_string(talloc_tos(), &ts, true)));
438 return false;
441 ret = timespec_compare(&cookie_st->st_ex_ctime,
442 &fsp_st->st_ex_ctime);
443 if (ret != 0) {
444 struct timeval tc, ts;
445 tc = convert_timespec_to_timeval(cookie_st->st_ex_ctime);
446 ts = convert_timespec_to_timeval(fsp_st->st_ex_ctime);
448 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
449 "stat_ex.%s differs: "
450 "cookie:'%s' != stat:'%s', "
451 "denying durable reconnect\n",
452 name,
453 "st_ex_ctime",
454 timeval_string(talloc_tos(), &tc, true),
455 timeval_string(talloc_tos(), &ts, true)));
456 return false;
459 ret = timespec_compare(&cookie_st->st_ex_btime,
460 &fsp_st->st_ex_btime);
461 if (ret != 0) {
462 struct timeval tc, ts;
463 tc = convert_timespec_to_timeval(cookie_st->st_ex_btime);
464 ts = convert_timespec_to_timeval(fsp_st->st_ex_btime);
466 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
467 "stat_ex.%s differs: "
468 "cookie:'%s' != stat:'%s', "
469 "denying durable reconnect\n",
470 name,
471 "st_ex_btime",
472 timeval_string(talloc_tos(), &tc, true),
473 timeval_string(talloc_tos(), &ts, true)));
474 return false;
477 if (cookie_st->st_ex_calculated_birthtime !=
478 fsp_st->st_ex_calculated_birthtime)
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_calculated_birthtime",
486 (unsigned long long)cookie_st->st_ex_calculated_birthtime,
487 (unsigned long long)fsp_st->st_ex_calculated_birthtime));
488 return false;
491 if (cookie_st->st_ex_blksize != fsp_st->st_ex_blksize) {
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_blksize",
498 (unsigned long long)cookie_st->st_ex_blksize,
499 (unsigned long long)fsp_st->st_ex_blksize));
500 return false;
503 if (cookie_st->st_ex_blocks != fsp_st->st_ex_blocks) {
504 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
505 "stat_ex.%s differs: "
506 "cookie:%llu != stat:%llu, "
507 "denying durable reconnect\n",
508 name,
509 "st_ex_blocks",
510 (unsigned long long)cookie_st->st_ex_blocks,
511 (unsigned long long)fsp_st->st_ex_blocks));
512 return false;
515 if (cookie_st->st_ex_flags != fsp_st->st_ex_flags) {
516 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
517 "stat_ex.%s differs: "
518 "cookie:%llu != stat:%llu, "
519 "denying durable reconnect\n",
520 name,
521 "st_ex_flags",
522 (unsigned long long)cookie_st->st_ex_flags,
523 (unsigned long long)fsp_st->st_ex_flags));
524 return false;
527 if (cookie_st->st_ex_mask != fsp_st->st_ex_mask) {
528 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
529 "stat_ex.%s differs: "
530 "cookie:%llu != stat:%llu, "
531 "denying durable reconnect\n",
532 name,
533 "st_ex_mask",
534 (unsigned long long)cookie_st->st_ex_mask,
535 (unsigned long long)fsp_st->st_ex_mask));
536 return false;
539 if (cookie_st->vfs_private != fsp_st->vfs_private) {
540 DEBUG(1, ("vfs_default_durable_reconnect (%s): "
541 "stat_ex.%s differs: "
542 "cookie:%llu != stat:%llu, "
543 "denying durable reconnect\n",
544 name,
545 "vfs_private",
546 (unsigned long long)cookie_st->vfs_private,
547 (unsigned long long)fsp_st->vfs_private));
548 return false;
551 return true;
554 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
555 struct smb_request *smb1req,
556 struct smbXsrv_open *op,
557 const DATA_BLOB old_cookie,
558 TALLOC_CTX *mem_ctx,
559 files_struct **result,
560 DATA_BLOB *new_cookie)
562 struct share_mode_lock *lck;
563 struct share_mode_entry *e;
564 struct files_struct *fsp = NULL;
565 NTSTATUS status;
566 bool ok;
567 int ret;
568 int flags = 0;
569 struct file_id file_id;
570 struct smb_filename *smb_fname = NULL;
571 enum ndr_err_code ndr_err;
572 struct vfs_default_durable_cookie cookie;
573 DATA_BLOB new_cookie_blob = data_blob_null;
575 *result = NULL;
576 *new_cookie = data_blob_null;
578 if (!lp_durable_handles(SNUM(conn))) {
579 return NT_STATUS_NOT_SUPPORTED;
583 * the checks for kernel oplocks
584 * and similar things are done
585 * in the vfs_default_durable_cookie()
586 * call below.
589 ZERO_STRUCT(cookie);
591 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
592 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
593 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
594 status = ndr_map_error2ntstatus(ndr_err);
595 return status;
598 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
599 return NT_STATUS_INVALID_PARAMETER;
602 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
603 return NT_STATUS_INVALID_PARAMETER;
606 if (!cookie.allow_reconnect) {
607 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
610 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
611 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
614 /* Create an smb_filename with stream_name == NULL. */
615 smb_fname = synthetic_smb_fname(talloc_tos(), cookie.base_name,
616 NULL, NULL);
617 if (smb_fname == NULL) {
618 return NT_STATUS_NO_MEMORY;
621 ret = SMB_VFS_LSTAT(conn, smb_fname);
622 if (ret == -1) {
623 status = map_nt_error_from_unix_common(errno);
624 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
625 smb_fname_str_dbg(smb_fname),
626 nt_errstr(status)));
627 return status;
630 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
631 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
634 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
635 if (!file_id_equal(&cookie.id, &file_id)) {
636 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
640 * 1. check entry in locking.tdb
643 lck = get_existing_share_mode_lock(mem_ctx, file_id);
644 if (lck == NULL) {
645 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
646 "not obtained from db\n"));
647 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
650 if (lck->data->num_share_modes == 0) {
651 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
652 "entry in existing share mode lock\n"));
653 TALLOC_FREE(lck);
654 return NT_STATUS_INTERNAL_DB_ERROR;
657 if (lck->data->num_share_modes > 1) {
659 * It can't be durable if there is more than one handle
660 * on the file.
662 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
663 "share-mode entry - can not be durable\n"));
664 TALLOC_FREE(lck);
665 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
668 e = &lck->data->share_modes[0];
670 if (!server_id_is_disconnected(&e->pid)) {
671 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
672 "reconnect for handle that was not marked "
673 "disconnected (e.g. smbd or cluster node died)\n"));
674 TALLOC_FREE(lck);
675 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
678 if (e->share_file_id != op->global->open_persistent_id) {
679 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
680 "share_file_id changed %llu != %llu"
681 "(e.g. another client had opened the file)\n",
682 (unsigned long long)e->share_file_id,
683 (unsigned long long)op->global->open_persistent_id));
684 TALLOC_FREE(lck);
685 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
688 if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
689 !CAN_WRITE(conn))
691 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
692 "share[%s] is not writeable anymore\n",
693 lp_servicename(talloc_tos(), SNUM(conn))));
694 TALLOC_FREE(lck);
695 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
699 * 2. proceed with opening file
702 status = fsp_new(conn, conn, &fsp);
703 if (!NT_STATUS_IS_OK(status)) {
704 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
705 "new fsp: %s\n", nt_errstr(status)));
706 TALLOC_FREE(lck);
707 return status;
710 fsp->fh->private_options = e->private_options;
711 fsp->fh->gen_id = smbXsrv_open_hash(op);
712 fsp->file_id = file_id;
713 fsp->file_pid = smb1req->smbpid;
714 fsp->vuid = smb1req->vuid;
715 fsp->open_time = e->time;
716 fsp->access_mask = e->access_mask;
717 fsp->share_access = e->share_access;
718 fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
719 fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
722 * TODO:
723 * Do we need to store the modified flag in the DB?
725 fsp->modified = false;
727 * no durables for directories
729 fsp->is_directory = false;
731 * For normal files, can_lock == !is_directory
733 fsp->can_lock = true;
735 * We do not support aio write behind for smb2
737 fsp->aio_write_behind = false;
738 fsp->oplock_type = e->op_type;
740 fsp->initial_allocation_size = cookie.initial_allocation_size;
741 fsp->fh->position_information = cookie.position_information;
742 fsp->update_write_time_triggered = cookie.update_write_time_triggered;
743 fsp->update_write_time_on_close = cookie.update_write_time_on_close;
744 fsp->write_time_forced = cookie.write_time_forced;
745 fsp->close_write_time = cookie.close_write_time;
747 status = fsp_set_smb_fname(fsp, smb_fname);
748 if (!NT_STATUS_IS_OK(status)) {
749 TALLOC_FREE(lck);
750 fsp_free(fsp);
751 DEBUG(0, ("vfs_default_durable_reconnect: "
752 "fsp_set_smb_fname failed: %s\n",
753 nt_errstr(status)));
754 return status;
757 op->compat = fsp;
758 fsp->op = op;
760 e->pid = messaging_server_id(conn->sconn->msg_ctx);
761 e->op_mid = smb1req->mid;
762 e->share_file_id = fsp->fh->gen_id;
764 ok = brl_reconnect_disconnected(fsp);
765 if (!ok) {
766 status = NT_STATUS_INTERNAL_ERROR;
767 DEBUG(1, ("vfs_default_durable_reconnect: "
768 "failed to reopen brlocks: %s\n",
769 nt_errstr(status)));
770 TALLOC_FREE(lck);
771 op->compat = NULL;
772 fsp_free(fsp);
773 return status;
777 * TODO: properly calculate open flags
779 if (fsp->can_write && fsp->can_read) {
780 flags = O_RDWR;
781 } else if (fsp->can_write) {
782 flags = O_WRONLY;
783 } else if (fsp->can_read) {
784 flags = O_RDONLY;
787 status = fd_open(conn, fsp, flags, 0 /* mode */);
788 if (!NT_STATUS_IS_OK(status)) {
789 TALLOC_FREE(lck);
790 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
791 "file: %s\n", nt_errstr(status)));
792 op->compat = NULL;
793 fsp_free(fsp);
794 return status;
798 * We now check the stat info stored in the cookie against
799 * the current stat data from the file we just opened.
800 * If any detail differs, we deny the durable reconnect,
801 * because in that case it is very likely that someone
802 * opened the file while the handle was disconnected,
803 * which has to be interpreted as an oplock break.
806 ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
807 if (ret == -1) {
808 status = map_nt_error_from_unix_common(errno);
809 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
810 smb_fname_str_dbg(smb_fname),
811 nt_errstr(status)));
812 ret = SMB_VFS_CLOSE(fsp);
813 if (ret == -1) {
814 DEBUG(0, ("vfs_default_durable_reconnect: "
815 "SMB_VFS_CLOSE failed (%s) - leaking file "
816 "descriptor\n", strerror(errno)));
818 TALLOC_FREE(lck);
819 op->compat = NULL;
820 fsp_free(fsp);
821 return status;
824 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
825 ret = SMB_VFS_CLOSE(fsp);
826 if (ret == -1) {
827 DEBUG(0, ("vfs_default_durable_reconnect: "
828 "SMB_VFS_CLOSE failed (%s) - leaking file "
829 "descriptor\n", strerror(errno)));
831 TALLOC_FREE(lck);
832 op->compat = NULL;
833 fsp_free(fsp);
834 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
837 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
838 if (!file_id_equal(&cookie.id, &file_id)) {
839 ret = SMB_VFS_CLOSE(fsp);
840 if (ret == -1) {
841 DEBUG(0, ("vfs_default_durable_reconnect: "
842 "SMB_VFS_CLOSE failed (%s) - leaking file "
843 "descriptor\n", strerror(errno)));
845 TALLOC_FREE(lck);
846 op->compat = NULL;
847 fsp_free(fsp);
848 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
851 ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
852 &fsp->fsp_name->st,
853 fsp_str_dbg(fsp));
854 if (!ok) {
855 ret = SMB_VFS_CLOSE(fsp);
856 if (ret == -1) {
857 DEBUG(0, ("vfs_default_durable_reconnect: "
858 "SMB_VFS_CLOSE failed (%s) - leaking file "
859 "descriptor\n", strerror(errno)));
861 TALLOC_FREE(lck);
862 op->compat = NULL;
863 fsp_free(fsp);
864 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
867 status = set_file_oplock(fsp, e->op_type);
868 if (!NT_STATUS_IS_OK(status)) {
869 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
870 "after opening file: %s\n", nt_errstr(status)));
871 ret = SMB_VFS_CLOSE(fsp);
872 if (ret == -1) {
873 DEBUG(0, ("vfs_default_durable_reconnect: "
874 "SMB_VFS_CLOSE failed (%s) - leaking file "
875 "descriptor\n", strerror(errno)));
877 TALLOC_FREE(lck);
878 op->compat = NULL;
879 fsp_free(fsp);
880 return status;
883 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
884 if (!NT_STATUS_IS_OK(status)) {
885 TALLOC_FREE(lck);
886 DEBUG(1, ("vfs_default_durable_reconnect: "
887 "vfs_default_durable_cookie - %s\n",
888 nt_errstr(status)));
889 op->compat = NULL;
890 fsp_free(fsp);
891 return status;
894 smb1req->chain_fsp = fsp;
895 smb1req->smb2req->compat_chain_fsp = fsp;
897 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
898 fsp_str_dbg(fsp)));
901 * release the sharemode lock: this writes the changes
903 lck->data->modified = true;
904 TALLOC_FREE(lck);
906 *result = fsp;
907 *new_cookie = new_cookie_blob;
909 return NT_STATUS_OK;