s3:smbd/durable: remove an extra blank line from vfs_default_durable_reconnect()
[Samba/vl.git] / source3 / smbd / durable.c
blobe4393a9511d278dcdd3e764cef176cc9435f617d
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 ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
108 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
109 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
110 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
111 return status;
114 return NT_STATUS_OK;
117 NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
118 const DATA_BLOB old_cookie,
119 TALLOC_CTX *mem_ctx,
120 DATA_BLOB *new_cookie)
122 struct connection_struct *conn = fsp->conn;
123 NTSTATUS status;
124 enum ndr_err_code ndr_err;
125 struct vfs_default_durable_cookie cookie;
126 DATA_BLOB new_cookie_blob = data_blob_null;
127 struct share_mode_lock *lck;
128 bool ok;
130 *new_cookie = data_blob_null;
132 ZERO_STRUCT(cookie);
134 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
135 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137 status = ndr_map_error2ntstatus(ndr_err);
138 return status;
141 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
142 return NT_STATUS_INVALID_PARAMETER;
145 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
146 return NT_STATUS_INVALID_PARAMETER;
149 if (!file_id_equal(&fsp->file_id, &cookie.id)) {
150 return NT_STATUS_INVALID_PARAMETER;
153 if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
154 return NT_STATUS_NOT_SUPPORTED;
157 if (fsp->num_pending_break_messages > 0) {
158 return NT_STATUS_NOT_SUPPORTED;
162 * For now let it be simple and do not keep
163 * delete on close files durable open
165 if (fsp->initial_delete_on_close) {
166 return NT_STATUS_NOT_SUPPORTED;
168 if (fsp->delete_on_close) {
169 return NT_STATUS_NOT_SUPPORTED;
172 if (!VALID_STAT(fsp->fsp_name->st)) {
173 return NT_STATUS_NOT_SUPPORTED;
176 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
177 return NT_STATUS_NOT_SUPPORTED;
180 /* Ensure any pending write time updates are done. */
181 if (fsp->update_write_time_event) {
182 update_write_time_handler(fsp->conn->sconn->ev_ctx,
183 fsp->update_write_time_event,
184 timeval_current(),
185 (void *)fsp);
189 * The above checks are done in mark_share_mode_disconnected() too
190 * but we want to avoid getting the lock if possible
192 lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
193 if (lck != NULL) {
194 struct smb_file_time ft;
196 ZERO_STRUCT(ft);
198 if (fsp->write_time_forced) {
199 ft.mtime = lck->data->changed_write_time;
200 } else if (fsp->update_write_time_on_close) {
201 if (null_timespec(fsp->close_write_time)) {
202 ft.mtime = timespec_current();
203 } else {
204 ft.mtime = fsp->close_write_time;
208 if (!null_timespec(ft.mtime)) {
209 round_timespec(conn->ts_res, &ft.mtime);
210 file_ntimes(conn, fsp->fsp_name, &ft);
213 ok = mark_share_mode_disconnected(lck, fsp);
214 if (!ok) {
215 TALLOC_FREE(lck);
218 if (lck != NULL) {
219 ok = brl_mark_disconnected(fsp);
220 if (!ok) {
221 TALLOC_FREE(lck);
224 if (lck == NULL) {
225 return NT_STATUS_NOT_SUPPORTED;
227 TALLOC_FREE(lck);
229 ZERO_STRUCT(cookie);
230 cookie.allow_reconnect = true;
231 cookie.id = fsp->file_id;
232 cookie.servicepath = conn->connectpath;
233 cookie.base_name = fsp->fsp_name->base_name;
234 cookie.initial_allocation_size = fsp->initial_allocation_size;
235 cookie.position_information = fsp->fh->position_information;
236 cookie.update_write_time_triggered = fsp->update_write_time_triggered;
237 cookie.update_write_time_on_close = fsp->update_write_time_on_close;
238 cookie.write_time_forced = fsp->write_time_forced;
239 cookie.close_write_time = fsp->close_write_time;
241 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
242 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
243 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
244 status = ndr_map_error2ntstatus(ndr_err);
245 return status;
248 status = fd_close(fsp);
249 if (!NT_STATUS_IS_OK(status)) {
250 data_blob_free(&new_cookie_blob);
251 return status;
254 *new_cookie = new_cookie_blob;
255 return NT_STATUS_OK;
258 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
259 struct smb_request *smb1req,
260 struct smbXsrv_open *op,
261 const DATA_BLOB old_cookie,
262 TALLOC_CTX *mem_ctx,
263 files_struct **result,
264 DATA_BLOB *new_cookie)
266 struct share_mode_lock *lck;
267 struct share_mode_entry *e;
268 struct files_struct *fsp = NULL;
269 NTSTATUS status;
270 bool ok;
271 int ret;
272 int flags = 0;
273 struct file_id file_id;
274 struct smb_filename *smb_fname = NULL;
275 enum ndr_err_code ndr_err;
276 struct vfs_default_durable_cookie cookie;
277 DATA_BLOB new_cookie_blob = data_blob_null;
279 *result = NULL;
280 *new_cookie = data_blob_null;
282 if (!lp_durable_handles(SNUM(conn))) {
283 return NT_STATUS_NOT_SUPPORTED;
287 * the checks for kernel oplocks
288 * and similar things are done
289 * in the vfs_default_durable_cookie()
290 * call below.
293 ZERO_STRUCT(cookie);
295 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
296 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
297 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
298 status = ndr_map_error2ntstatus(ndr_err);
299 return status;
302 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
303 return NT_STATUS_INVALID_PARAMETER;
306 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
307 return NT_STATUS_INVALID_PARAMETER;
310 if (!cookie.allow_reconnect) {
311 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
314 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
315 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
318 /* Create an smb_filename with stream_name == NULL. */
319 status = create_synthetic_smb_fname(talloc_tos(),
320 cookie.base_name,
321 NULL, NULL,
322 &smb_fname);
323 if (!NT_STATUS_IS_OK(status)) {
324 return status;
327 ret = SMB_VFS_LSTAT(conn, smb_fname);
328 if (ret == -1) {
329 status = map_nt_error_from_unix_common(errno);
330 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
331 smb_fname_str_dbg(smb_fname),
332 nt_errstr(status)));
333 return status;
336 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
337 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
340 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
341 if (!file_id_equal(&cookie.id, &file_id)) {
342 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
346 * 1. check entry in locking.tdb
349 lck = get_existing_share_mode_lock(mem_ctx, file_id);
350 if (lck == NULL) {
351 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
352 "not obtained from db\n"));
353 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
356 if (lck->data->num_share_modes == 0) {
357 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
358 "entry in existing share mode lock\n"));
359 TALLOC_FREE(lck);
360 return NT_STATUS_INTERNAL_DB_ERROR;
363 if (lck->data->num_share_modes > 1) {
365 * It can't be durable if there is more than one handle
366 * on the file.
368 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
369 "share-mode entry - can not be durable\n"));
370 TALLOC_FREE(lck);
371 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
374 e = &lck->data->share_modes[0];
376 if (!server_id_is_disconnected(&e->pid)) {
377 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
378 "reconnect for handle that was not marked "
379 "disconnected (e.g. smbd or cluster node died)\n"));
380 TALLOC_FREE(lck);
381 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
384 if (e->share_file_id != op->global->open_persistent_id) {
385 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
386 "share_file_id changed %llu != %llu"
387 "(e.g. another client had opened the file)\n",
388 (unsigned long long)e->share_file_id,
389 (unsigned long long)op->global->open_persistent_id));
390 TALLOC_FREE(lck);
391 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
394 if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
395 !CAN_WRITE(conn))
397 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
398 "share[%s] is not writeable anymore\n",
399 lp_servicename(talloc_tos(), SNUM(conn))));
400 TALLOC_FREE(lck);
401 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
405 * TODO:
406 * add scavenger timer functionality
408 * For now we always allow the reconnect
410 #if 0
411 expire_time = op->global->disconnect_time;
412 expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
413 if (expire < now) {
414 //TODO reopen and close before telling the client...
416 #endif
419 * 2. proceed with opening file
422 status = fsp_new(conn, conn, &fsp);
423 if (!NT_STATUS_IS_OK(status)) {
424 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
425 "new fsp: %s\n", nt_errstr(status)));
426 TALLOC_FREE(lck);
427 return status;
430 fsp->fh->private_options = e->private_options;
431 fsp->fh->gen_id = smbXsrv_open_hash(op);
432 fsp->file_id = file_id;
433 fsp->file_pid = smb1req->smbpid;
434 fsp->vuid = smb1req->vuid;
435 fsp->open_time = e->time;
436 fsp->access_mask = e->access_mask;
437 fsp->share_access = e->share_access;
438 fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
439 fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
442 * TODO:
443 * Do we need to store the modified flag in the DB?
444 * How to handle update_write_time and friends
445 * during a disconnected client on a durable handle?
447 fsp->modified = false;
449 * no durables for directories
451 fsp->is_directory = false;
453 * For normal files, can_lock == !is_directory
455 fsp->can_lock = true;
457 * We do not support aio write behind for smb2
459 fsp->aio_write_behind = false;
460 fsp->oplock_type = e->op_type;
462 fsp->initial_allocation_size = cookie.initial_allocation_size;
463 fsp->fh->position_information = cookie.position_information;
464 fsp->update_write_time_triggered = cookie.update_write_time_triggered;
465 fsp->update_write_time_on_close = cookie.update_write_time_on_close;
466 fsp->write_time_forced = cookie.write_time_forced;
467 fsp->close_write_time = cookie.close_write_time;
469 status = fsp_set_smb_fname(fsp, smb_fname);
470 if (!NT_STATUS_IS_OK(status)) {
471 TALLOC_FREE(lck);
472 fsp_free(fsp);
473 DEBUG(0, ("vfs_default_durable_reconnect: "
474 "fsp_set_smb_fname failed: %s\n",
475 nt_errstr(status)));
476 return status;
479 op->compat = fsp;
480 fsp->op = op;
482 e->pid = messaging_server_id(conn->sconn->msg_ctx);
483 e->op_mid = smb1req->mid;
484 e->share_file_id = fsp->fh->gen_id;
486 ok = brl_reconnect_disconnected(fsp);
487 if (!ok) {
488 status = NT_STATUS_INTERNAL_ERROR;
489 DEBUG(1, ("vfs_default_durable_reconnect: "
490 "failed to reopen brlocks: %s\n",
491 nt_errstr(status)));
492 TALLOC_FREE(lck);
493 op->compat = NULL;
494 fsp_free(fsp);
495 return status;
499 * TODO: properly calculate open flags
501 if (fsp->can_write && fsp->can_read) {
502 flags = O_RDWR;
503 } else if (fsp->can_write) {
504 flags = O_WRONLY;
505 } else if (fsp->can_read) {
506 flags = O_RDONLY;
509 status = fd_open(conn, fsp, flags, 0 /* mode */);
510 if (!NT_STATUS_IS_OK(status)) {
511 TALLOC_FREE(lck);
512 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
513 "file: %s\n", nt_errstr(status)));
514 op->compat = NULL;
515 fsp_free(fsp);
516 return status;
519 ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
520 if (ret == -1) {
521 status = map_nt_error_from_unix_common(errno);
522 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
523 smb_fname_str_dbg(smb_fname),
524 nt_errstr(status)));
525 ret = SMB_VFS_CLOSE(fsp);
526 if (ret == -1) {
527 DEBUG(0, ("vfs_default_durable_reconnect: "
528 "SMB_VFS_CLOSE failed (%s) - leaking file "
529 "descriptor\n", strerror(errno)));
531 TALLOC_FREE(lck);
532 op->compat = NULL;
533 fsp_free(fsp);
534 return status;
537 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
538 ret = SMB_VFS_CLOSE(fsp);
539 if (ret == -1) {
540 DEBUG(0, ("vfs_default_durable_reconnect: "
541 "SMB_VFS_CLOSE failed (%s) - leaking file "
542 "descriptor\n", strerror(errno)));
544 TALLOC_FREE(lck);
545 op->compat = NULL;
546 fsp_free(fsp);
547 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
550 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
551 if (!file_id_equal(&cookie.id, &file_id)) {
552 ret = SMB_VFS_CLOSE(fsp);
553 if (ret == -1) {
554 DEBUG(0, ("vfs_default_durable_reconnect: "
555 "SMB_VFS_CLOSE failed (%s) - leaking file "
556 "descriptor\n", strerror(errno)));
558 TALLOC_FREE(lck);
559 op->compat = NULL;
560 fsp_free(fsp);
561 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
564 status = set_file_oplock(fsp, e->op_type);
565 if (!NT_STATUS_IS_OK(status)) {
566 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
567 "after opening file: %s\n", nt_errstr(status)));
568 ret = SMB_VFS_CLOSE(fsp);
569 if (ret == -1) {
570 DEBUG(0, ("vfs_default_durable_reconnect: "
571 "SMB_VFS_CLOSE failed (%s) - leaking file "
572 "descriptor\n", strerror(errno)));
574 TALLOC_FREE(lck);
575 op->compat = NULL;
576 fsp_free(fsp);
577 return status;
580 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
581 if (!NT_STATUS_IS_OK(status)) {
582 TALLOC_FREE(lck);
583 DEBUG(1, ("vfs_default_durable_reconnect: "
584 "vfs_default_durable_cookie - %s\n",
585 nt_errstr(status)));
586 op->compat = NULL;
587 fsp_free(fsp);
588 return status;
591 smb1req->chain_fsp = fsp;
592 smb1req->smb2req->compat_chain_fsp = fsp;
594 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
595 fsp_str_dbg(fsp)));
598 * release the sharemode lock: this writes the changes
600 lck->data->modified = true;
601 TALLOC_FREE(lck);
603 *result = fsp;
604 *new_cookie = new_cookie_blob;
606 return NT_STATUS_OK;