summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/Makefile.in1
-rw-r--r--source3/librpc/idl/open_files.idl16
-rw-r--r--source3/modules/vfs_default.c9
-rw-r--r--source3/smbd/durable.c569
-rw-r--r--source3/smbd/proto.h17
-rwxr-xr-xsource3/wscript_build1
6 files changed, 610 insertions, 3 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index b448c02bfb..2ad8eccdb2 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -984,6 +984,7 @@ SMBD_OBJ_SRV = smbd/server_reload.o \
smbd/smbXsrv_session.o \
smbd/smbXsrv_tcon.o \
smbd/smbXsrv_open.o \
+ smbd/durable.o \
$(MANGLE_OBJ) @VFS_STATIC@
SMBD_OBJ_BASE = $(PARAM_WITHOUT_REG_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \
diff --git a/source3/librpc/idl/open_files.idl b/source3/librpc/idl/open_files.idl
index 98e1c32db0..eb0dfa08ae 100644
--- a/source3/librpc/idl/open_files.idl
+++ b/source3/librpc/idl/open_files.idl
@@ -46,4 +46,20 @@ interface open_files
uint8 modified;
[ignore] db_record *record;
} share_mode_data;
+
+ /* these are 0x30 (48) characters */
+ const string VFS_DEFAULT_DURABLE_COOKIE_MAGIC =
+ "VFS_DEFAULT_DURABLE_COOKIE_MAGIC ";
+ const uint32 VFS_DEFAULT_DURABLE_COOKIE_VERSION = 0;
+
+ typedef [public] struct {
+ [value(VFS_DEFAULT_DURABLE_COOKIE_MAGIC),charset(DOS)] uint8 magic[0x30];
+ [value(VFS_DEFAULT_DURABLE_COOKIE_VERSION)] uint32 version;
+ boolean8 allow_reconnect;
+ file_id id;
+ [string,charset(UTF8)] char *servicepath;
+ [string,charset(UTF8)] char *base_name;
+ hyper initial_allocation_size;
+ hyper position_information;
+ } vfs_default_durable_cookie;
}
diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c
index 427e3af9ca..8392feb8c4 100644
--- a/source3/modules/vfs_default.c
+++ b/source3/modules/vfs_default.c
@@ -2232,7 +2232,7 @@ static NTSTATUS vfswrap_durable_cookie(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
DATA_BLOB *cookie)
{
- return NT_STATUS_NOT_SUPPORTED;
+ return vfs_default_durable_cookie(fsp, mem_ctx, cookie);
}
static NTSTATUS vfswrap_durable_disconnect(struct vfs_handle_struct *handle,
@@ -2241,7 +2241,8 @@ static NTSTATUS vfswrap_durable_disconnect(struct vfs_handle_struct *handle,
TALLOC_CTX *mem_ctx,
DATA_BLOB *new_cookie)
{
- return NT_STATUS_NOT_SUPPORTED;
+ return vfs_default_durable_disconnect(fsp, old_cookie, mem_ctx,
+ new_cookie);
}
static NTSTATUS vfswrap_durable_reconnect(struct vfs_handle_struct *handle,
@@ -2252,7 +2253,9 @@ static NTSTATUS vfswrap_durable_reconnect(struct vfs_handle_struct *handle,
struct files_struct **fsp,
DATA_BLOB *new_cookie)
{
- return NT_STATUS_NOT_SUPPORTED;
+ return vfs_default_durable_reconnect(handle->conn, smb1req, op,
+ old_cookie, mem_ctx,
+ fsp, new_cookie);
}
static struct vfs_fn_pointers vfs_default_fns = {
diff --git a/source3/smbd/durable.c b/source3/smbd/durable.c
new file mode 100644
index 0000000000..5953e1b43e
--- /dev/null
+++ b/source3/smbd/durable.c
@@ -0,0 +1,569 @@
+/*
+ Unix SMB/CIFS implementation.
+ Durable Handle default VFS implementation
+
+ Copyright (C) Stefan Metzmacher 2012
+ Copyright (C) Michael Adam 2012
+ Copyright (C) Volker Lendecke 2012
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "libcli/security/security.h"
+#include "messages.h"
+#include "librpc/gen_ndr/ndr_open_files.h"
+#include "serverid.h"
+#include "fake_file.h"
+
+NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *cookie_blob)
+{
+ struct connection_struct *conn = fsp->conn;
+ enum ndr_err_code ndr_err;
+ struct vfs_default_durable_cookie cookie;
+
+ if (!lp_durable_handles(SNUM(conn))) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (lp_kernel_share_modes(SNUM(conn))) {
+ /*
+ * We do not support durable handles
+ * if kernel share modes (flocks) are used
+ */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (lp_kernel_oplocks(SNUM(conn))) {
+ /*
+ * We do not support durable handles
+ * if kernel oplocks are used
+ */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if ((fsp->current_lock_count > 0) &&
+ lp_posix_locking(fsp->conn->params))
+ {
+ /*
+ * We do not support durable handles
+ * if the handle has posix locks.
+ */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (fsp->is_directory) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (fsp->fh->fd == -1) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (is_ntfs_stream_smb_fname(fsp->fsp_name)) {
+ /*
+ * We do not support durable handles
+ * on streams for now.
+ */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (is_fake_file(fsp->fsp_name)) {
+ /*
+ * We do not support durable handles
+ * on fake files.
+ */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ ZERO_STRUCT(cookie);
+ cookie.allow_reconnect = false;
+ cookie.id = fsp->file_id;
+ cookie.servicepath = conn->connectpath;
+ cookie.base_name = fsp->fsp_name->base_name;
+ cookie.initial_allocation_size = fsp->initial_allocation_size;
+ cookie.position_information = fsp->fh->position_information;
+
+ ndr_err = ndr_push_struct_blob(cookie_blob, mem_ctx, &cookie,
+ (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
+ const DATA_BLOB old_cookie,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *new_cookie)
+{
+ struct connection_struct *conn = fsp->conn;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ struct vfs_default_durable_cookie cookie;
+ DATA_BLOB new_cookie_blob = data_blob_null;
+ struct share_mode_lock *lck;
+ bool ok;
+
+ *new_cookie = data_blob_null;
+
+ ZERO_STRUCT(cookie);
+
+ ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
+ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!file_id_equal(&fsp->file_id, &cookie.id)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (fsp->num_pending_break_messages > 0) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /*
+ * For now let it be simple and do not keep
+ * delete on close files durable open
+ */
+ if (fsp->initial_delete_on_close) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ if (fsp->delete_on_close) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (!VALID_STAT(fsp->fsp_name->st)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /*
+ * The above checks are done in mark_share_mode_disconnected() too
+ * but we want to avoid getting the lock if possible
+ */
+ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id);
+ if (lck != NULL) {
+ ok = mark_share_mode_disconnected(lck, fsp);
+ if (!ok) {
+ TALLOC_FREE(lck);
+ }
+ }
+ if (lck != NULL) {
+ ok = brl_mark_disconnected(fsp);
+ if (!ok) {
+ TALLOC_FREE(lck);
+ }
+ }
+ if (lck == NULL) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ TALLOC_FREE(lck);
+
+ ZERO_STRUCT(cookie);
+ cookie.allow_reconnect = true;
+ cookie.id = fsp->file_id;
+ cookie.servicepath = conn->connectpath;
+ cookie.base_name = fsp->fsp_name->base_name;
+ cookie.initial_allocation_size = fsp->initial_allocation_size;
+ cookie.position_information = fsp->fh->position_information;
+
+ ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie,
+ (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ status = fd_close(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&new_cookie_blob);
+ return status;
+ }
+
+ *new_cookie = new_cookie_blob;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
+ struct smb_request *smb1req,
+ struct smbXsrv_open *op,
+ const DATA_BLOB old_cookie,
+ TALLOC_CTX *mem_ctx,
+ files_struct **result,
+ DATA_BLOB *new_cookie)
+{
+ struct share_mode_lock *lck;
+ struct share_mode_entry *e;
+ struct files_struct *fsp = NULL;
+ NTSTATUS status;
+ bool ok;
+ int ret;
+ int flags;
+ struct file_id file_id;
+ struct smb_filename *smb_fname = NULL;
+ enum ndr_err_code ndr_err;
+ struct vfs_default_durable_cookie cookie;
+ DATA_BLOB new_cookie_blob = data_blob_null;
+
+ *result = NULL;
+ *new_cookie = data_blob_null;
+
+ if (!lp_durable_handles(SNUM(conn))) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /*
+ * the checks for kernel oplocks
+ * and similar things are done
+ * in the vfs_default_durable_cookie()
+ * call below.
+ */
+
+ ZERO_STRUCT(cookie);
+
+ ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie,
+ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ return status;
+ }
+
+ if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!cookie.allow_reconnect) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (strcmp(cookie.servicepath, conn->connectpath) != 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* Create an smb_filename with stream_name == NULL. */
+ status = create_synthetic_smb_fname(talloc_tos(),
+ cookie.base_name,
+ NULL, NULL,
+ &smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ret = SMB_VFS_LSTAT(conn, smb_fname);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(1, ("Unable to lstat stream: %s => %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status)));
+ return status;
+ }
+
+ if (!S_ISREG(smb_fname->st.st_ex_mode)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
+ if (!file_id_equal(&cookie.id, &file_id)) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * 1. check entry in locking.tdb
+ */
+
+ lck = get_existing_share_mode_lock(mem_ctx, file_id);
+ if (lck == NULL) {
+ DEBUG(5, ("vfs_default_durable_reconnect: share-mode lock "
+ "not obtained from db\n"));
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (lck->data->num_share_modes == 0) {
+ DEBUG(1, ("vfs_default_durable_reconnect: Error: no share-mode "
+ "entry in existing share mode lock\n"));
+ TALLOC_FREE(lck);
+ return NT_STATUS_INTERNAL_DB_ERROR;
+ }
+
+ if (lck->data->num_share_modes > 1) {
+ /*
+ * It can't be durable if there is more than one handle
+ * on the file.
+ */
+ DEBUG(5, ("vfs_default_durable_reconnect: more than one "
+ "share-mode entry - can not be durable\n"));
+ TALLOC_FREE(lck);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ e = &lck->data->share_modes[0];
+
+ if (!server_id_is_disconnected(&e->pid)) {
+ DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
+ "reconnect for handle that was not marked "
+ "disconnected (e.g. smbd or cluster node died)\n"));
+ TALLOC_FREE(lck);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (e->share_file_id != op->global->open_persistent_id) {
+ DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
+ "share_file_id changed %llu != %llu"
+ "(e.g. another client had opened the file)\n",
+ (unsigned long long)e->share_file_id,
+ (unsigned long long)op->global->open_persistent_id));
+ TALLOC_FREE(lck);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if ((e->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) &&
+ !CAN_WRITE(conn))
+ {
+ DEBUG(5, ("vfs_default_durable_reconnect: denying durable "
+ "share[%s] is not writeable anymore\n",
+ lp_servicename(talloc_tos(), SNUM(conn))));
+ TALLOC_FREE(lck);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * TODO:
+ * add scavenger timer functionality
+ *
+ * For now we always allow the reconnect
+ */
+#if 0
+ expire_time = op->global->disconnect_time;
+ expire_time += NTTIME_MAGIC(op->global->durable_timeout_msec);
+ if (expire < now) {
+ //TODO reopen and close before telling the client...
+ }
+#endif
+
+ /*
+ * 2. proceed with opening file
+ */
+
+ status = fsp_new(conn, conn, &fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("vfs_default_durable_reconnect: failed to create "
+ "new fsp: %s\n", nt_errstr(status)));
+ TALLOC_FREE(lck);
+ return status;
+ }
+
+ fsp->fh->private_options = e->private_options;
+ fsp->fh->gen_id = smbXsrv_open_hash(op);
+ fsp->file_id = file_id;
+ fsp->file_pid = smb1req->smbpid;
+ fsp->vuid = smb1req->vuid;
+ fsp->open_time = e->time;
+ fsp->access_mask = e->access_mask;
+ fsp->share_access = e->share_access;
+ fsp->can_read = ((fsp->access_mask & (FILE_READ_DATA)) != 0);
+ fsp->can_write = ((fsp->access_mask & (FILE_WRITE_DATA|FILE_APPEND_DATA)) != 0);
+
+ /*
+ * TODO:
+ * Do we need to store the modified flag in the DB?
+ * How to handle update_write_time and friends
+ * during a disconnected client on a durable handle?
+ */
+ fsp->modified = false;
+ /*
+ * no durables for directories
+ */
+ fsp->is_directory = false;
+ /*
+ * For normal files, can_lock == !is_directory
+ */
+ fsp->can_lock = true;
+ /*
+ * We do not support aio write behind for smb2
+ */
+ fsp->aio_write_behind = false;
+ fsp->oplock_type = e->op_type;
+
+ fsp->initial_allocation_size = cookie.initial_allocation_size;
+ fsp->fh->position_information = cookie.position_information;
+
+ status = fsp_set_smb_fname(fsp, smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(lck);
+ fsp_free(fsp);
+ DEBUG(0, ("vfs_default_durable_reconnect: "
+ "fsp_set_smb_fname failed: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ op->compat = fsp;
+ fsp->op = op;
+
+ e->pid = messaging_server_id(conn->sconn->msg_ctx);
+ e->op_mid = smb1req->mid;
+ e->share_file_id = fsp->fh->gen_id;
+
+ ok = brl_reconnect_disconnected(fsp);
+ if (!ok) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ DEBUG(1, ("vfs_default_durable_reconnect: "
+ "failed to reopen brlocks: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(lck);
+ op->compat = NULL;
+ fsp_free(fsp);
+ return status;
+ }
+
+ /*
+ * TODO: properly calculate open flags
+ */
+ if (fsp->can_write && fsp->can_read) {
+ flags = O_RDWR;
+ } else if (fsp->can_write) {
+ flags = O_WRONLY;
+ } else if (fsp->can_read) {
+ flags = O_RDONLY;
+ }
+
+ status = fd_open(conn, fsp, flags, 0 /* mode */);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(lck);
+ DEBUG(1, ("vfs_default_durable_reconnect: failed to open "
+ "file: %s\n", nt_errstr(status)));
+ op->compat = NULL;
+ fsp_free(fsp);
+ return status;
+ }
+
+ ret = SMB_VFS_FSTAT(fsp, &fsp->fsp_name->st);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(errno);
+ DEBUG(1, ("Unable to fstat stream: %s => %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status)));
+ ret = SMB_VFS_CLOSE(fsp);
+ if (ret == -1) {
+ DEBUG(0, ("vfs_default_durable_reconnect: "
+ "SMB_VFS_CLOSE failed (%s) - leaking file "
+ "descriptor\n", strerror(errno)));
+ }
+ TALLOC_FREE(lck);
+ op->compat = NULL;
+ fsp_free(fsp);
+ return status;
+
+ }
+
+ if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
+ ret = SMB_VFS_CLOSE(fsp);
+ if (ret == -1) {
+ DEBUG(0, ("vfs_default_durable_reconnect: "
+ "SMB_VFS_CLOSE failed (%s) - leaking file "
+ "descriptor\n", strerror(errno)));
+ }
+ TALLOC_FREE(lck);
+ op->compat = NULL;
+ fsp_free(fsp);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ file_id = vfs_file_id_from_sbuf(conn, &fsp->fsp_name->st);
+ if (!file_id_equal(&cookie.id, &file_id)) {
+ ret = SMB_VFS_CLOSE(fsp);
+ if (ret == -1) {
+ DEBUG(0, ("vfs_default_durable_reconnect: "
+ "SMB_VFS_CLOSE failed (%s) - leaking file "
+ "descriptor\n", strerror(errno)));
+ }
+ TALLOC_FREE(lck);
+ op->compat = NULL;
+ fsp_free(fsp);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = set_file_oplock(fsp, e->op_type);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("vfs_default_durable_reconnect failed to set oplock "
+ "after opening file: %s\n", nt_errstr(status)));
+ ret = SMB_VFS_CLOSE(fsp);
+ if (ret == -1) {
+ DEBUG(0, ("vfs_default_durable_reconnect: "
+ "SMB_VFS_CLOSE failed (%s) - leaking file "
+ "descriptor\n", strerror(errno)));
+ }
+ TALLOC_FREE(lck);
+ op->compat = NULL;
+ fsp_free(fsp);
+ return status;
+ }
+
+ status = vfs_default_durable_cookie(fsp, mem_ctx, &new_cookie_blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(lck);
+ DEBUG(1, ("vfs_default_durable_reconnect: "
+ "vfs_default_durable_cookie - %s\n",
+ nt_errstr(status)));
+ op->compat = NULL;
+ fsp_free(fsp);
+ return status;
+ }
+
+ smb1req->chain_fsp = fsp;
+ smb1req->smb2req->compat_chain_fsp = fsp;
+
+ DEBUG(10, ("vfs_default_durable_reconnect: opened file '%s'\n",
+ fsp_str_dbg(fsp)));
+
+ /*
+ * release the sharemode lock: this writes the changes
+ */
+ lck->data->modified = true;
+ TALLOC_FREE(lck);
+
+ *result = fsp;
+ *new_cookie = new_cookie_blob;
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index ee8ebc01b8..a6aa862598 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -1162,4 +1162,21 @@ NTSTATUS vfs_streaminfo(connection_struct *conn,
void *avahi_start_register(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
uint16_t port);
+/* The following definitions come from smbd/smb2_create.c */
+
+NTSTATUS vfs_default_durable_cookie(struct files_struct *fsp,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *cookie_blob);
+NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp,
+ const DATA_BLOB old_cookie,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *new_cookie);
+NTSTATUS vfs_default_durable_reconnect(struct connection_struct *conn,
+ struct smb_request *smb1req,
+ struct smbXsrv_open *op,
+ const DATA_BLOB old_cookie,
+ TALLOC_CTX *mem_ctx,
+ files_struct **result,
+ DATA_BLOB *new_cookie);
+
#endif /* _SMBD_PROTO_H_ */
diff --git a/source3/wscript_build b/source3/wscript_build
index 88ad8ab0cb..048875d66f 100755
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -381,6 +381,7 @@ SMBD_SRC_SRV = '''smbd/server_reload.c smbd/files.c smbd/connection.c
smbd/smbXsrv_tcon.c
smbd/smbXsrv_open.c
smbd/server_exit.c
+ smbd/durable.c
${MANGLE_SRC}'''
SMBD_SRC_BASE = '''${SMBD_SRC_SRV}