Beispiel #1
0
void scavenger_schedule_disconnected(struct files_struct *fsp)
{
	NTSTATUS status;
	struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
	struct timeval disconnect_time, until;
	uint64_t timeout_usec;
	struct scavenger_message msg;
	DATA_BLOB msg_blob;
	struct server_id_buf tmp;

	if (fsp->op == NULL) {
		return;
	}
	nttime_to_timeval(&disconnect_time, fsp->op->global->disconnect_time);
	timeout_usec = 1000 * fsp->op->global->durable_timeout_msec;
	until = timeval_add(&disconnect_time,
			    timeout_usec / 1000000,
			    timeout_usec % 1000000);

	ZERO_STRUCT(msg);
	msg.file_id = fsp->file_id;
	msg.open_persistent_id = fsp->op->global->open_persistent_id;
	msg.until = timeval_to_nttime(&until);

	DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout "
		   "at %s in %fs\n",
		   server_id_str_buf(self, &tmp),
		   file_id_string_tos(&fsp->file_id),
		   timeval_string(talloc_tos(), &disconnect_time, true),
		   timeval_string(talloc_tos(), &until, true),
		   fsp->op->global->durable_timeout_msec/1000.0));

	SMB_ASSERT(server_id_is_disconnected(&fsp->op->global->server_id));
	SMB_ASSERT(!server_id_equal(&self, &smbd_scavenger_state->parent_id));
	SMB_ASSERT(!smbd_scavenger_state->am_scavenger);

	msg_blob = data_blob_const(&msg, sizeof(msg));
	DEBUG(10, ("send message to scavenger\n"));

	status = messaging_send(smbd_scavenger_state->msg,
				smbd_scavenger_state->parent_id,
				MSG_SMB_SCAVENGER,
				&msg_blob);
	if (!NT_STATUS_IS_OK(status)) {
		struct server_id_buf tmp1, tmp2;
		DEBUG(2, ("Failed to send message to parent smbd %s "
			  "from %s: %s\n",
			  server_id_str_buf(smbd_scavenger_state->parent_id,
					    &tmp1),
			  server_id_str_buf(self, &tmp2),
			  nt_errstr(status)));
	}
}
Beispiel #2
0
char *server_id_str_buf(struct server_id id, struct server_id_buf *dst)
{
	if (server_id_is_disconnected(&id)) {
		strlcpy(dst->buf, "disconnected", sizeof(dst->buf));
	} else if ((id.vnn == NONCLUSTER_VNN) && (id.task_id == 0)) {
		snprintf(dst->buf, sizeof(dst->buf), "%llu",
			 (unsigned long long)id.pid);
	} else if (id.vnn == NONCLUSTER_VNN) {
		snprintf(dst->buf, sizeof(dst->buf), "%llu.%u",
			 (unsigned long long)id.pid, (unsigned)id.task_id);
	} else if (id.task_id == 0) {
		snprintf(dst->buf, sizeof(dst->buf), "%u:%llu",
			 (unsigned)id.vnn, (unsigned long long)id.pid);
	} else {
		snprintf(dst->buf, sizeof(dst->buf), "%u:%llu.%u",
			 (unsigned)id.vnn,
			 (unsigned long long)id.pid,
			 (unsigned)id.task_id);
	}
	return dst->buf;
}
Beispiel #3
0
static void smbXsrv_open_global_verify_record(struct db_record *db_rec,
					bool *is_free,
					bool *was_free,
					TALLOC_CTX *mem_ctx,
					struct smbXsrv_open_global0 **_g)
{
	TDB_DATA key;
	TDB_DATA val;
	DATA_BLOB blob;
	struct smbXsrv_open_globalB global_blob;
	enum ndr_err_code ndr_err;
	struct smbXsrv_open_global0 *global = NULL;
	bool exists;
	TALLOC_CTX *frame = talloc_stackframe();

	*is_free = false;

	if (was_free) {
		*was_free = false;
	}
	if (_g) {
		*_g = NULL;
	}

	key = dbwrap_record_get_key(db_rec);

	val = dbwrap_record_get_value(db_rec);
	if (val.dsize == 0) {
		DEBUG(10, ("%s: empty value\n", __func__));
		TALLOC_FREE(frame);
		*is_free = true;
		if (was_free) {
			*was_free = true;
		}
		return;
	}

	blob = data_blob_const(val.dptr, val.dsize);

	ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
			(ndr_pull_flags_fn_t)ndr_pull_smbXsrv_open_globalB);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
		DEBUG(1,("smbXsrv_open_global_verify_record: "
			 "key '%s' ndr_pull_struct_blob - %s\n",
			 hex_encode_talloc(frame, key.dptr, key.dsize),
			 nt_errstr(status)));
		TALLOC_FREE(frame);
		return;
	}

	DEBUG(10,("smbXsrv_open_global_verify_record\n"));
	if (CHECK_DEBUGLVL(10)) {
		NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
	}

	if (global_blob.version != SMBXSRV_VERSION_0) {
		DEBUG(0,("smbXsrv_open_global_verify_record: "
			 "key '%s' use unsupported version %u\n",
			 hex_encode_talloc(frame, key.dptr, key.dsize),
			 global_blob.version));
		NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
		TALLOC_FREE(frame);
		return;
	}

	global = global_blob.info.info0;

	if (server_id_is_disconnected(&global->server_id)) {
		exists = true;
	} else {
		exists = serverid_exists(&global->server_id);
	}
	if (!exists) {
		struct server_id_buf idbuf;
		DEBUG(2,("smbXsrv_open_global_verify_record: "
			 "key '%s' server_id %s does not exist.\n",
			 hex_encode_talloc(frame, key.dptr, key.dsize),
			 server_id_str_buf(global->server_id, &idbuf)));
		if (CHECK_DEBUGLVL(2)) {
			NDR_PRINT_DEBUG(smbXsrv_open_globalB, &global_blob);
		}
		TALLOC_FREE(frame);
		dbwrap_record_delete(db_rec);
		*is_free = true;
		return;
	}

	if (_g) {
		*_g = talloc_move(mem_ctx, &global);
	}
	TALLOC_FREE(frame);
}
Beispiel #4
0
NTSTATUS smbXsrv_open_cleanup(uint64_t persistent_id)
{
	NTSTATUS status = NT_STATUS_OK;
	TALLOC_CTX *frame = talloc_stackframe();
	struct smbXsrv_open_global0 *op = NULL;
	TDB_DATA val;
	struct db_record *rec;
	bool delete_open = false;
	uint32_t global_id = persistent_id & UINT32_MAX;

	rec = smbXsrv_open_global_fetch_locked(smbXsrv_open_global_db_ctx,
					       global_id,
					       frame);
	if (rec == NULL) {
		status = NT_STATUS_NOT_FOUND;
		goto done;
	}

	val = dbwrap_record_get_value(rec);
	if (val.dsize == 0) {
		DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
			  "empty record in %s, skipping...\n",
			   global_id, dbwrap_name(smbXsrv_open_global_db_ctx)));
		goto done;
	}

	status = smbXsrv_open_global_parse_record(talloc_tos(), rec, &op);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
			  "failed to read record: %s\n",
			  global_id, nt_errstr(status)));
		goto done;
	}

	if (server_id_is_disconnected(&op->server_id)) {
		struct timeval now, disconnect_time;
		int64_t tdiff;
		now = timeval_current();
		nttime_to_timeval(&disconnect_time, op->disconnect_time);
		tdiff = usec_time_diff(&now, &disconnect_time);
		delete_open = (tdiff >= 1000*op->durable_timeout_msec);

		DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
			   "disconnected at [%s] %us ago with "
			   "timeout of %us -%s reached\n",
			   global_id,
			   nt_time_string(frame, op->disconnect_time),
			   (unsigned)(tdiff/1000000),
			   op->durable_timeout_msec / 1000,
			   delete_open ? "" : " not"));
	} else if (!serverid_exists(&op->server_id)) {
		struct server_id_buf idbuf;
		DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
			   "server[%s] does not exist\n",
			   global_id,
			   server_id_str_buf(op->server_id, &idbuf)));
		delete_open = true;
	}

	if (!delete_open) {
		goto done;
	}

	status = dbwrap_record_delete(rec);
	if (!NT_STATUS_IS_OK(status)) {
		DEBUG(1, ("smbXsrv_open_cleanup[global: 0x%08x] "
			  "failed to delete record"
			  "from %s: %s\n", global_id,
			  dbwrap_name(smbXsrv_open_global_db_ctx),
			  nt_errstr(status)));
		goto done;
	}

	DEBUG(10, ("smbXsrv_open_cleanup[global: 0x%08x] "
		   "delete record from %s\n",
		   global_id,
		   dbwrap_name(smbXsrv_open_global_db_ctx)));

done:
	talloc_free(frame);
	return status;
}
Beispiel #5
0
bool serverids_exist(const struct server_id *ids, int num_ids, bool *results)
{
	int *todo_idx = NULL;
	struct server_id *todo_ids = NULL;
	bool *todo_results = NULL;
	int todo_num = 0;
	int *remote_idx = NULL;
	int remote_num = 0;
	int *verify_idx = NULL;
	int verify_num = 0;
	int t, idx;
	bool result = false;
	struct db_context *db;

	db = serverid_db();
	if (db == NULL) {
		return false;
	}

	todo_idx = talloc_array(talloc_tos(), int, num_ids);
	if (todo_idx == NULL) {
		goto fail;
	}
	todo_ids = talloc_array(talloc_tos(), struct server_id, num_ids);
	if (todo_ids == NULL) {
		goto fail;
	}
	todo_results = talloc_array(talloc_tos(), bool, num_ids);
	if (todo_results == NULL) {
		goto fail;
	}

	remote_idx = talloc_array(talloc_tos(), int, num_ids);
	if (remote_idx == NULL) {
		goto fail;
	}
	verify_idx = talloc_array(talloc_tos(), int, num_ids);
	if (verify_idx == NULL) {
		goto fail;
	}

	for (idx=0; idx<num_ids; idx++) {
		results[idx] = false;

		if (server_id_is_disconnected(&ids[idx])) {
			continue;
		}

		if (procid_is_me(&ids[idx])) {
			results[idx] = true;
			continue;
		}

		if (procid_is_local(&ids[idx])) {
			bool exists = process_exists_by_pid(ids[idx].pid);

			if (!exists) {
				continue;
			}

			if (ids[idx].unique_id == SERVERID_UNIQUE_ID_NOT_TO_VERIFY) {
				results[idx] = true;
				continue;
			}

			verify_idx[verify_num] = idx;
			verify_num += 1;
			continue;
		}

		if (!lp_clustering()) {
			continue;
		}

		remote_idx[remote_num] = idx;
		remote_num += 1;
	}

	if (remote_num != 0 &&
	    ctdb_serverids_exist_supported(messaging_ctdbd_connection()))
	{
		int old_remote_num = remote_num;

		remote_num = 0;
		todo_num = 0;

		for (t=0; t<old_remote_num; t++) {
			idx = remote_idx[t];

			if (ids[idx].unique_id == SERVERID_UNIQUE_ID_NOT_TO_VERIFY) {
				remote_idx[remote_num] = idx;
				remote_num += 1;
				continue;
			}

			todo_idx[todo_num] = idx;
			todo_ids[todo_num] = ids[idx];
			todo_results[todo_num] = false;
			todo_num += 1;
		}

		/*
		 * Note: this only uses CTDB_CONTROL_CHECK_SRVIDS
		 * to verify that the server_id still exists,
		 * which means only the server_id.unique_id and
		 * server_id.vnn are verified, while server_id.pid
		 * is not verified at all.
		 *
		 * TODO: do we want to verify server_id.pid somehow?
		 */
		if (!ctdb_serverids_exist(messaging_ctdbd_connection(),
					  todo_ids, todo_num, todo_results))
		{
			goto fail;
		}

		for (t=0; t<todo_num; t++) {
			idx = todo_idx[t];

			results[idx] = todo_results[t];
		}
	}

	if (remote_num != 0) {
		todo_num = 0;

		for (t=0; t<remote_num; t++) {
			idx = remote_idx[t];
			todo_idx[todo_num] = idx;
			todo_ids[todo_num] = ids[idx];
			todo_results[todo_num] = false;
			todo_num += 1;
		}

		if (!ctdb_processes_exist(messaging_ctdbd_connection(),
					  todo_ids, todo_num,
					  todo_results)) {
			goto fail;
		}

		for (t=0; t<todo_num; t++) {
			idx = todo_idx[t];

			if (!todo_results[t]) {
				continue;
			}

			if (ids[idx].unique_id == SERVERID_UNIQUE_ID_NOT_TO_VERIFY) {
				results[idx] = true;
				continue;
			}

			verify_idx[verify_num] = idx;
			verify_num += 1;
		}
	}

	for (t=0; t<verify_num; t++) {
		struct serverid_exists_state state;
		struct serverid_key key;
		TDB_DATA tdbkey;
		NTSTATUS status;

		idx = verify_idx[t];

		serverid_fill_key(&ids[idx], &key);
		tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));

		state.id = &ids[idx];
		state.exists = false;
		status = dbwrap_parse_record(db, tdbkey, server_exists_parse, &state);
		if (!NT_STATUS_IS_OK(status)) {
			results[idx] = false;
			continue;
		}
		results[idx] = state.exists;
	}

	result = true;
fail:
	TALLOC_FREE(verify_idx);
	TALLOC_FREE(remote_idx);
	TALLOC_FREE(todo_results);
	TALLOC_FREE(todo_ids);
	TALLOC_FREE(todo_idx);
	return result;
}
Beispiel #6
0
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 = 0;
	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. */
	smb_fname = synthetic_smb_fname(talloc_tos(), cookie.base_name,
					NULL, NULL);
	if (smb_fname == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	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;
	}

	/*
	 * 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);
	fsp->fnum = op->local_id;

	/*
	 * TODO:
	 * Do we need to store the modified flag in the DB?
	 */
	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;
	fsp->update_write_time_triggered = cookie.update_write_time_triggered;
	fsp->update_write_time_on_close = cookie.update_write_time_on_close;
	fsp->write_time_forced = cookie.write_time_forced;
	fsp->close_write_time = cookie.close_write_time;

	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;
	}

	/*
	 * We now check the stat info stored in the cookie against
	 * the current stat data from the file we just opened.
	 * If any detail differs, we deny the durable reconnect,
	 * because in that case it is very likely that someone
	 * opened the file while the handle was disconnected,
	 * which has to be interpreted as an oplock break.
	 */

	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;
	}

	ok = vfs_default_durable_reconnect_check_stat(&cookie.stat_info,
						      &fsp->fsp_name->st,
						      fsp_str_dbg(fsp));
	if (!ok) {
		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);
	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;
}