diff --git a/src/sftp/sftp.h b/src/sftp/sftp.h
index 28b0fbf0f2e14a8225435f0f9b48df0fe3409410..c9dca4c84f74547092574544f0e313cb58b0464b 100644
--- a/src/sftp/sftp.h
+++ b/src/sftp/sftp.h
@@ -100,9 +100,12 @@ typedef struct sftp_client_state {
 	void *cb_data;
 	sftp_str_t err_msg;
 	sftp_str_t err_lang;
+	pthread_mutex_t mtx;
+	uint32_t running;
 	uint32_t id;
 	uint32_t err_id;
 	uint32_t err_code;
+	bool terminating;
 } *sftpc_state_t;
 
 enum sftp_handle_type {
diff --git a/src/sftp/sftp_client.c b/src/sftp/sftp_client.c
index 8306ee2c61964977cc4b5237dbe3102c7ca8e9c5..ca3254e0613692e4c396f75a0c3216674bb01c0e 100644
--- a/src/sftp/sftp_client.c
+++ b/src/sftp/sftp_client.c
@@ -69,6 +69,9 @@ sftpc_finish(sftpc_state_t state)
 	assert(state);
 	if (state == NULL)
 		return;
+	pthread_mutex_lock(&state->mtx);
+	state->terminating = true;
+	pthread_mutex_unlock(&state->mtx);
 	// TODO: Close all open handles
 	while (!CloseEvent(state->recv_event)) {
 		assert(errno == EBUSY);
@@ -77,8 +80,16 @@ sftpc_finish(sftpc_state_t state)
 		SetEvent(state->recv_event);
 		SLEEP(1);
 	}
+	pthread_mutex_lock(&state->mtx);
+	while (state->running) {
+		pthread_mutex_unlock(&state->mtx);
+		SLEEP(1);
+		pthread_mutex_lock(&state->mtx);
+	}
 	sftp_free_rx_pkt(state->rxp);
 	sftp_free_tx_pkt(state->txp);
+	pthread_mutex_unlock(&state->mtx);
+	pthread_mutex_destroy(&state->mtx);
 	free(state);
 }
 
@@ -100,6 +111,9 @@ sftpc_begin(bool (*send_cb)(uint8_t *buf, size_t len, void *cb_data), void *cb_d
 	ret->id = 0;
 	ret->err_lang = NULL;
 	ret->err_msg = NULL;
+	ret->running = 0;
+	pthread_mutex_init(&ret->mtx, NULL);
+	ret->terminating = false;
 	return ret;
 }
 
@@ -162,8 +176,12 @@ get_result(sftpc_state_t state)
 		return false;
 	if (!state->send_cb(txbuf, txsz, state->cb_data))
 		return false;
-	if (WaitForEvent(state->recv_event, INFINITE) != WAIT_OBJECT_0)
+	pthread_mutex_unlock(&state->mtx);
+	if (WaitForEvent(state->recv_event, INFINITE) != WAIT_OBJECT_0) {
+		pthread_mutex_lock(&state->mtx);
 		return false;
+	}
+	pthread_mutex_lock(&state->mtx);
 	if (state->rxp == NULL)
 		return false;
 	if (state->rxp->type != SSH_FXP_VERSION) {
@@ -192,201 +210,224 @@ handle_error(sftpc_state_t state)
 	response_handled(state);
 }
 
+static bool
+parse_handle(sftpc_state_t state, sftp_str_t *handle)
+{
+	assert(state->rxp);
+	if (state->rxp == NULL)
+		return false;
+	assert(state->rxp->type == SSH_FXP_HANDLE);
+	if (state->rxp->type != SSH_FXP_HANDLE)
+		return false;
+	assert(handle);
+	if (handle == NULL)
+		return false;
+	*handle = getstring(state);
+	if (*handle == NULL)
+		return false;
+	return true;
+}
+
+static bool
+enter_function(sftpc_state_t state)
+{
+	if (!check_state(state))
+		return false;
+	if (pthread_mutex_lock(&state->mtx))
+		return false;
+	if (state->terminating) {
+		pthread_mutex_unlock(&state->mtx);
+		return false;
+	}
+	state->running++;
+}
+
+static bool
+exit_function(sftpc_state_t state, bool retval)
+{
+	assert(state->running > 0);
+	state->running--;
+	pthread_mutex_unlock(&state->mtx);
+	return retval;
+}
+
 bool
 sftpc_init(sftpc_state_t state)
 {
-	if (!check_state(state))
+	if (!enter_function(state))
 		return false;
 	if (!appendheader(state, SSH_FXP_INIT))
-		return false;
+		return exit_function(state, false);
 	if (!append32(state, SFTP_VERSION))
-		return false;
+		return exit_function(state, false);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	if (state->rxp->type != SSH_FXP_VERSION)
-		return false;
+		return exit_function(state, false);
 	if (get32(state) != SFTP_VERSION) {
 		response_handled(state);
-		return false;
+		return exit_function(state, false);
 	}
 	response_handled(state);
 	state->id = 0;
-	return true;
+	return exit_function(state, true);
 }
 
 bool
 sftpc_recv(sftpc_state_t state, uint8_t *buf, uint32_t sz)
 {
-	if (!check_state(state))
+	if (!enter_function(state))
 		return false;
 	if (!sftp_rx_pkt_append(&state->rxp, buf, sz))
-		return false;
+		return exit_function(state, false);
 	if (sftp_have_full_pkt(state->rxp))
 		SetEvent(state->recv_event);
-	return true;
+	return exit_function(state, true);
 }
 
 bool
 sftpc_realpath(sftpc_state_t state, char *path, sftp_str_t *ret)
 {
-	if (!check_state(state))
+	if (!enter_function(state))
 		return false;
 	assert(ret);
 	if (ret == NULL)
-		return false;
+		return exit_function(state, false);
 	if (*ret != NULL)
-		return false;
+		return exit_function(state, false);
 	if (!appendheader(state, SSH_FXP_REALPATH))
-		return false;
+		return exit_function(state, false);
 	sftp_str_t pstr = sftp_strdup(path);
 	if (!appendandfreestring(state, &pstr))
-		return false;
+		return exit_function(state, false);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	if (state->rxp->type == SSH_FXP_NAME) {
 		if (get32(state) != 1) {
 			response_handled(state);
-			return false;
+			return exit_function(state, false);
 		}
 		*ret = getstring(state);
 		response_handled(state);
-		return true;
+		return exit_function(state, true);
 	}
 	handle_error(state);
-	return false;
-}
-
-static bool
-parse_handle(sftpc_state_t state, sftp_str_t *handle)
-{
-	assert(state->rxp);
-	if (state->rxp == NULL)
-		return false;
-	assert(state->rxp->type == SSH_FXP_HANDLE);
-	if (state->rxp->type != SSH_FXP_HANDLE)
-		return false;
-	assert(handle);
-	if (handle == NULL)
-		return false;
-	*handle = getstring(state);
-	if (*handle == NULL)
-		return false;
-	return true;
+	return exit_function(state, false);
 }
 
 bool
 sftpc_open(sftpc_state_t state, char *path, uint32_t flags, sftp_file_attr_t attr, sftp_dirhandle_t *handle)
 {
-	if (!check_state(state))
-		return false;
+	if (!enter_function(state))
+		return exit_function(state, false);
 	assert(path);
 	if (path == NULL)
-		return false;
+		return exit_function(state, false);
 	assert(handle);
 	if (handle == NULL)
-		return false;
+		return exit_function(state, false);
 	assert(!*handle);
 	if (*handle != NULL)
-		return false;
+		return exit_function(state, false);
 	if (!appendheader(state, SSH_FXP_OPEN))
-		return false;
+		return exit_function(state, false);
 	sftp_str_t pstr = sftp_strdup(path);
 	if (!appendandfreestring(state, &pstr))
-		return false;
+		return exit_function(state, false);
 	if (!append32(state, flags))
-		return false;
+		return exit_function(state, false);
 	sftp_file_attr_t a = attr;
 	if (a == NULL) {
 		a = sftp_fattr_alloc();
 		if (a == NULL)
-			return false;
+			return exit_function(state, false);
 	}
 	if (!appendfattr(state, a)) {
 		if (a != attr)
 			sftp_fattr_free(a);
-		return false;
+		return exit_function(state, false);
 	}
 	if (a != attr)
 		sftp_fattr_free(a);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	if (state->rxp->type == SSH_FXP_HANDLE) {
 		if (!parse_handle(state, (sftp_str_t *)handle)) {
 			response_handled(state);
-			return false;
+			return exit_function(state, false);
 		}
 		response_handled(state);
-		return true;
+		return exit_function(state, true);
 	}
 	handle_error(state);
-	return false;
+	return exit_function(state, false);
 }
 
 bool
 sftpc_close(sftpc_state_t state, sftp_filehandle_t *handle)
 {
-	if (!check_state(state))
+	if (!enter_function(state))
 		return false;
 	if (!appendheader(state, SSH_FXP_CLOSE))
-		return false;
+		return exit_function(state, false);
 	if (!appendfhandle(state, *handle))
-		return false;
+		return exit_function(state, false);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	handle_error(state);
 	free_sftp_str(*handle);
 	*handle = NULL;
-	return state->err_code == SSH_FX_OK;
+	return exit_function(state, state->err_code == SSH_FX_OK);
 }
 
 bool
 sftpc_read(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, uint32_t len, sftp_str_t *ret)
 {
-	if (!check_state(state))
-		return false;
+	if (!enter_function(state))
+		return exit_function(state, false);
 	assert(ret);
 	if (ret == NULL)
-		return false;
+		return exit_function(state, false);
 	assert(*ret == NULL);
 	if (*ret != NULL)
-		return false;
+		return exit_function(state, false);
 	if (!appendheader(state, SSH_FXP_READ))
-		return false;
+		return exit_function(state, false);
 	if (!appendfhandle(state, handle))
-		return false;
+		return exit_function(state, false);
 	if (!append64(state, offset))
-		return false;
+		return exit_function(state, false);
 	if (!append32(state, len))
-		return false;
+		return exit_function(state, false);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	if (state->rxp->type == SSH_FXP_DATA) {
 		*ret = getstring(state);
 		response_handled(state);
-		return *ret != NULL;
+		return exit_function(state, *ret != NULL);
 	}
 	handle_error(state);
-	return false;
+	return exit_function(state, false);
 }
 
 bool
 sftpc_write(sftpc_state_t state, sftp_filehandle_t handle, uint64_t offset, sftp_str_t data)
 {
-	if (!check_state(state))
+	if (!enter_function(state))
 		return false;
 	assert(data);
 	if (data == NULL)
-		return false;
+		return exit_function(state, false);
 	if (!appendheader(state, SSH_FXP_WRITE))
-		return false;
+		return exit_function(state, false);
 	if (!appendfhandle(state, handle))
-		return false;
+		return exit_function(state, false);
 	if (!append64(state, offset))
-		return false;
+		return exit_function(state, false);
 	if (!appendstring(state, data))
-		return false;
+		return exit_function(state, false);
 	if (!get_result(state))
-		return false;
+		return exit_function(state, false);
 	handle_error(state);
-	return state->err_code == SSH_FX_OK;
+	return exit_function(state, state->err_code == SSH_FX_OK);
 }