Reformat spacing to be even.
[Samba/gebeck_regimport.git] / source3 / smbd / durable.c
blob42ad18e5b6578d5b85002ccfe0c82434803866b5
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;
103 ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
104 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
105 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
106 NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
107 return status;
110 return NT_STATUS_OK;
113 NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
114 const DATA_BLOB old_cookie,
115 TALLOC_CTX *mem_ctx,
116 DATA_BLOB *new_cookie)
118 struct connection_struct *conn = fsp->conn;
119 NTSTATUS status;
120 enum ndr_err_code ndr_err;
121 struct vfs_default_durable_cookie cookie;
122 DATA_BLOB new_cookie_blob = data_blob_null;
123 struct share_mode_lock *lck;
124 bool ok;
126 *new_cookie = data_blob_null;
128 ZERO_STRUCT(cookie);
130 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
131 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
132 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133 status = ndr_map_error2ntstatus(ndr_err);
134 return status;
137 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
138 return NT_STATUS_INVALID_PARAMETER;
141 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
142 return NT_STATUS_INVALID_PARAMETER;
145 if (!file_id_equal(&fsp->file_id, &cookie.id)) {
146 return NT_STATUS_INVALID_PARAMETER;
149 if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
150 return NT_STATUS_NOT_SUPPORTED;
153 if (fsp->num_pending_break_messages > 0) {
154 return NT_STATUS_NOT_SUPPORTED;
158 * For now let it be simple and do not keep
159 * delete on close files durable open
161 if (fsp->initial_delete_on_close) {
162 return NT_STATUS_NOT_SUPPORTED;
164 if (fsp->delete_on_close) {
165 return NT_STATUS_NOT_SUPPORTED;
168 if (!VALID_STAT(fsp->fsp_name->st)) {
169 return NT_STATUS_NOT_SUPPORTED;
172 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
173 return NT_STATUS_NOT_SUPPORTED;
177 * The above checks are done in mark_share_mode_disconnected() too
178 * but we want to avoid getting the lock if possible
180 lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
181 if (lck != NULL) {
182 ok = mark_share_mode_disconnected(lck, fsp);
183 if (!ok) {
184 TALLOC_FREE(lck);
187 if (lck != NULL) {
188 ok = brl_mark_disconnected(fsp);
189 if (!ok) {
190 TALLOC_FREE(lck);
193 if (lck == NULL) {
194 return NT_STATUS_NOT_SUPPORTED;
196 TALLOC_FREE(lck);
198 ZERO_STRUCT(cookie);
199 cookie.allow_reconnect = true;
200 cookie.id = fsp->file_id;
201 cookie.servicepath = conn->connectpath;
202 cookie.base_name = fsp->fsp_name->base_name;
203 cookie.initial_allocation_size = fsp->initial_allocation_size;
204 cookie.position_information = fsp->fh->position_information;
206 ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
207 (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
208 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
209 status = ndr_map_error2ntstatus(ndr_err);
210 return status;
213 status = fd_close(fsp);
214 if (!NT_STATUS_IS_OK(status)) {
215 data_blob_free(&new_cookie_blob);
216 return status;
219 *new_cookie = new_cookie_blob;
220 return NT_STATUS_OK;
223 NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
224 struct smb_request *smb1req,
225 struct smbXsrv_open *op,
226 const DATA_BLOB old_cookie,
227 TALLOC_CTX *mem_ctx,
228 files_struct **result,
229 DATA_BLOB *new_cookie)
231 struct share_mode_lock *lck;
232 struct share_mode_entry *e;
233 struct files_struct *fsp = NULL;
234 NTSTATUS status;
235 bool ok;
236 int ret;
237 int flags = 0;
238 struct file_id file_id;
239 struct smb_filename *smb_fname = NULL;
240 enum ndr_err_code ndr_err;
241 struct vfs_default_durable_cookie cookie;
242 DATA_BLOB new_cookie_blob = data_blob_null;
244 *result = NULL;
245 *new_cookie = data_blob_null;
247 if (!lp_durable_handles(SNUM(conn))) {
248 return NT_STATUS_NOT_SUPPORTED;
252 * the checks for kernel oplocks
253 * and similar things are done
254 * in the vfs_default_durable_cookie()
255 * call below.
258 ZERO_STRUCT(cookie);
260 ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
261 (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
262 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
263 status = ndr_map_error2ntstatus(ndr_err);
264 return status;
267 if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
268 return NT_STATUS_INVALID_PARAMETER;
271 if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
272 return NT_STATUS_INVALID_PARAMETER;
275 if (!cookie.allow_reconnect) {
276 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
279 if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
280 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
283 /* Create an smb_filename with stream_name == NULL. */
284 status = create_synthetic_smb_fname(talloc_tos(),
285 cookie.base_name,
286 NULL, NULL,
287 &smb_fname);
288 if (!NT_STATUS_IS_OK(status)) {
289 return status;
292 ret = SMB_VFS_LSTAT(conn, smb_fname);
293 if (ret == -1) {
294 status = map_nt_error_from_unix_common(errno);
295 DEBUG(1, ("Unable to lstat stream: %s => %s\n",
296 smb_fname_str_dbg(smb_fname),
297 nt_errstr(status)));
298 return status;
301 if (!S_ISREG(smb_fname->st.st_ex_mode)) {
302 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
305 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
306 if (!file_id_equal(&cookie.id, &file_id)) {
307 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
311 * 1. check entry in locking.tdb
314 lck = get_existing_share_mode_lock(mem_ctx, file_id);
315 if (lck == NULL) {
316 DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
317 "not obtained from db\n"));
318 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
321 if (lck->data->num_share_modes == 0) {
322 DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
323 "entry in existing share mode lock\n"));
324 TALLOC_FREE(lck);
325 return NT_STATUS_INTERNAL_DB_ERROR;
328 if (lck->data->num_share_modes > 1) {
330 * It can't be durable if there is more than one handle
331 * on the file.
333 DEBUG(5, ("vfs_default_durable_reconnect: more than one "
334 "share-mode entry - can not be durable\n"));
335 TALLOC_FREE(lck);
336 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
339 e = &lck->data->share_modes[0];
341 if (!server_id_is_disconnected(&e->pid)) {
342 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
343 "reconnect for handle that was not marked "
344 "disconnected (e.g. smbd or cluster node died)\n"));
345 TALLOC_FREE(lck);
346 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
349 if (e->share_file_id != op->global->open_persistent_id) {
350 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
351 "share_file_id changed %llu != %llu"
352 "(e.g. another client had opened the file)\n",
353 (unsigned long long)e->share_file_id,
354 (unsigned long long)op->global->open_persistent_id));
355 TALLOC_FREE(lck);
356 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
359 if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
360 !CAN_WRITE(conn))
362 DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
363 "share[%s] is not writeable anymore\n",
364 lp_servicename(talloc_tos(), SNUM(conn))));
365 TALLOC_FREE(lck);
366 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
370 * TODO:
371 * add scavenger timer functionality
373 * For now we always allow the reconnect
375 #if 0
376 expire_time = op->global->disconnect_time;
377 expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
378 if (expire < now) {
379 //TODO reopen and close before telling the client...
381 #endif
384 * 2. proceed with opening file
387 status = fsp_new(conn, conn, &fsp);
388 if (!NT_STATUS_IS_OK(status)) {
389 DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
390 "new fsp: %s\n", nt_errstr(status)));
391 TALLOC_FREE(lck);
392 return status;
395 fsp->fh->private_options = e->private_options;
396 fsp->fh->gen_id = smbXsrv_open_hash(op);
397 fsp->file_id = file_id;
398 fsp->file_pid = smb1req->smbpid;
399 fsp->vuid = smb1req->vuid;
400 fsp->open_time = e->time;
401 fsp->access_mask = e->access_mask;
402 fsp->share_access = e->share_access;
403 fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
404 fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
407 * TODO:
408 * Do we need to store the modified flag in the DB?
409 * How to handle update_write_time and friends
410 * during a disconnected client on a durable handle?
412 fsp->modified = false;
414 * no durables for directories
416 fsp->is_directory = false;
418 * For normal files, can_lock == !is_directory
420 fsp->can_lock = true;
422 * We do not support aio write behind for smb2
424 fsp->aio_write_behind = false;
425 fsp->oplock_type = e->op_type;
427 fsp->initial_allocation_size = cookie.initial_allocation_size;
428 fsp->fh->position_information = cookie.position_information;
430 status = fsp_set_smb_fname(fsp, smb_fname);
431 if (!NT_STATUS_IS_OK(status)) {
432 TALLOC_FREE(lck);
433 fsp_free(fsp);
434 DEBUG(0, ("vfs_default_durable_reconnect: "
435 "fsp_set_smb_fname failed: %s\n",
436 nt_errstr(status)));
437 return status;
440 op->compat = fsp;
441 fsp->op = op;
443 e->pid = messaging_server_id(conn->sconn->msg_ctx);
444 e->op_mid = smb1req->mid;
445 e->share_file_id = fsp->fh->gen_id;
447 ok = brl_reconnect_disconnected(fsp);
448 if (!ok) {
449 status = NT_STATUS_INTERNAL_ERROR;
450 DEBUG(1, ("vfs_default_durable_reconnect: "
451 "failed to reopen brlocks: %s\n",
452 nt_errstr(status)));
453 TALLOC_FREE(lck);
454 op->compat = NULL;
455 fsp_free(fsp);
456 return status;
460 * TODO: properly calculate open flags
462 if (fsp->can_write && fsp->can_read) {
463 flags = O_RDWR;
464 } else if (fsp->can_write) {
465 flags = O_WRONLY;
466 } else if (fsp->can_read) {
467 flags = O_RDONLY;
470 status = fd_open(conn, fsp, flags, 0 /* mode */);
471 if (!NT_STATUS_IS_OK(status)) {
472 TALLOC_FREE(lck);
473 DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
474 "file: %s\n", nt_errstr(status)));
475 op->compat = NULL;
476 fsp_free(fsp);
477 return status;
480 ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
481 if (ret == -1) {
482 status = map_nt_error_from_unix_common(errno);
483 DEBUG(1, ("Unable to fstat stream: %s => %s\n",
484 smb_fname_str_dbg(smb_fname),
485 nt_errstr(status)));
486 ret = SMB_VFS_CLOSE(fsp);
487 if (ret == -1) {
488 DEBUG(0, ("vfs_default_durable_reconnect: "
489 "SMB_VFS_CLOSE failed (%s) - leaking file "
490 "descriptor\n", strerror(errno)));
492 TALLOC_FREE(lck);
493 op->compat = NULL;
494 fsp_free(fsp);
495 return status;
499 if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
500 ret = SMB_VFS_CLOSE(fsp);
501 if (ret == -1) {
502 DEBUG(0, ("vfs_default_durable_reconnect: "
503 "SMB_VFS_CLOSE failed (%s) - leaking file "
504 "descriptor\n", strerror(errno)));
506 TALLOC_FREE(lck);
507 op->compat = NULL;
508 fsp_free(fsp);
509 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
512 file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
513 if (!file_id_equal(&cookie.id, &file_id)) {
514 ret = SMB_VFS_CLOSE(fsp);
515 if (ret == -1) {
516 DEBUG(0, ("vfs_default_durable_reconnect: "
517 "SMB_VFS_CLOSE failed (%s) - leaking file "
518 "descriptor\n", strerror(errno)));
520 TALLOC_FREE(lck);
521 op->compat = NULL;
522 fsp_free(fsp);
523 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
526 status = set_file_oplock(fsp, e->op_type);
527 if (!NT_STATUS_IS_OK(status)) {
528 DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
529 "after opening file: %s\n", nt_errstr(status)));
530 ret = SMB_VFS_CLOSE(fsp);
531 if (ret == -1) {
532 DEBUG(0, ("vfs_default_durable_reconnect: "
533 "SMB_VFS_CLOSE failed (%s) - leaking file "
534 "descriptor\n", strerror(errno)));
536 TALLOC_FREE(lck);
537 op->compat = NULL;
538 fsp_free(fsp);
539 return status;
542 status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
543 if (!NT_STATUS_IS_OK(status)) {
544 TALLOC_FREE(lck);
545 DEBUG(1, ("vfs_default_durable_reconnect: "
546 "vfs_default_durable_cookie - %s\n",
547 nt_errstr(status)));
548 op->compat = NULL;
549 fsp_free(fsp);
550 return status;
553 smb1req->chain_fsp = fsp;
554 smb1req->smb2req->compat_chain_fsp = fsp;
556 DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
557 fsp_str_dbg(fsp)));
560 * release the sharemode lock: this writes the changes
562 lck->data->modified = true;
563 TALLOC_FREE(lck);
565 *result = fsp;
566 *new_cookie = new_cookie_blob;
568 return NT_STATUS_OK;