summaryrefslogtreecommitdiff
path: root/source3/locking
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2019-09-16 10:01:36 -0700
committerJeremy Allison <jra@samba.org>2019-09-17 22:49:39 +0000
commit9c99675bcbc530f8054de77b1a5f660dde015c62 (patch)
tree98e8f7c07ec8bcf4635cdb911afa5bf2c2aa366a /source3/locking
parent15a8af075a2d7b59f4d97d9804599b26325e5640 (diff)
downloadsamba-9c99675bcbc530f8054de77b1a5f660dde015c62.tar.gz
smbd: Introduce share_entries.tdb - ADD COMMENT FIXME !!
This moves share_modes[] from "struct share_mode_data" into a separate share_entries.tdb with a sorted array of fixed-length (132 byte) "struct share_mode_entry" entries. I know it's one huge commit, but I did not see a way to keep both data structures and associated code working together without a lot of code duplication after having centralized all the code accessing the share_modes[] array into a few routines. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/locking')
-rw-r--r--source3/locking/locking.c272
-rw-r--r--source3/locking/share_mode_lock.c883
2 files changed, 883 insertions, 272 deletions
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index 32bdea1a16a..568d2002229 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -733,28 +733,6 @@ NTSTATUS remove_lease_if_stale(struct share_mode_lock *lck,
return status;
}
-/*
- * See if we need to remove a lease being referred to by a
- * share mode that is being marked stale or deleted.
- */
-
-static void remove_share_mode_lease(struct share_mode_lock *lck,
- struct share_mode_entry *e)
-{
- uint16_t op_type;
-
- op_type = e->op_type;
- e->op_type = NO_OPLOCK;
-
- lck->data->modified = true;
-
- if (op_type != LEASE_OPLOCK) {
- return;
- }
-
- remove_lease_if_stale(lck, &e->client_guid, &e->lease_key);
-}
-
bool share_entry_stale_pid(struct share_mode_entry *e)
{
struct server_id_buf buf;
@@ -779,201 +757,6 @@ bool share_entry_stale_pid(struct share_mode_entry *e)
return true;
}
-bool set_share_mode(struct share_mode_lock *lck,
- struct files_struct *fsp,
- uid_t uid,
- uint64_t mid,
- uint16_t op_type,
- uint32_t share_access,
- uint32_t access_mask)
-{
- struct share_mode_data *d = lck->data;
- struct share_mode_entry *tmp, *e;
-
- tmp = talloc_realloc(d, d->share_modes, struct share_mode_entry,
- d->num_share_modes+1);
- if (tmp == NULL) {
- return false;
- }
- d->share_modes = tmp;
- e = &d->share_modes[d->num_share_modes];
- d->num_share_modes += 1;
- d->modified = true;
-
- ZERO_STRUCTP(e);
- e->pid = messaging_server_id(fsp->conn->sconn->msg_ctx);
- e->share_access = share_access;
- e->private_options = fsp->fh->private_options;
- e->access_mask = access_mask;
- e->op_mid = mid;
- e->op_type = op_type;
-
- if (op_type == LEASE_OPLOCK) {
- const struct GUID *client_guid = fsp_client_guid(fsp);
- e->client_guid = *client_guid;
- e->lease_key = fsp->lease->lease.lease_key;
- }
-
- e->time.tv_sec = fsp->open_time.tv_sec;
- e->time.tv_usec = fsp->open_time.tv_usec;
- e->share_file_id = fsp->fh->gen_id;
- e->uid = (uint32_t)uid;
- e->flags = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) ?
- SHARE_MODE_FLAG_POSIX_OPEN : 0;
- e->name_hash = fsp->name_hash;
-
- return true;
-}
-
-static struct share_mode_entry *find_share_mode_entry(
- struct share_mode_lock *lck,
- struct server_id pid,
- uint64_t share_file_id)
-{
- struct share_mode_data *d = lck->data;
- uint32_t i;
-
- for (i=0; i<d->num_share_modes; i++) {
- struct share_mode_entry *e = &d->share_modes[i];
-
- if (!is_valid_share_mode_entry(e)) {
- continue;
- }
- if (!serverid_equal(&pid, &e->pid)) {
- continue;
- }
- if (share_file_id != e->share_file_id) {
- continue;
- }
- return e;
- }
- return NULL;
-}
-
-bool reset_share_mode_entry(
- struct share_mode_lock *lck,
- struct server_id old_pid,
- uint64_t old_share_file_id,
- struct server_id new_pid,
- uint64_t new_mid,
- uint64_t new_share_file_id)
-{
- struct share_mode_entry *e = find_share_mode_entry(
- lck, old_pid, old_share_file_id);
-
- if (e == NULL) {
- return false;
- }
-
- e->pid = new_pid;
- e->op_mid = new_mid;
- e->share_file_id = new_share_file_id;
-
- lck->data->modified = true;
- return true;
-}
-
-/*******************************************************************
- Del the share mode of a file for this process.
-********************************************************************/
-
-bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
-{
- struct share_mode_entry *e;
-
- e = find_share_mode_entry(
- lck,
- messaging_server_id(fsp->conn->sconn->msg_ctx),
- fsp->fh->gen_id);
- if (e == NULL) {
- return False;
- }
- remove_share_mode_lease(lck, e);
- *e = lck->data->share_modes[lck->data->num_share_modes-1];
- lck->data->num_share_modes -= 1;
- return True;
-}
-
-bool mark_share_mode_disconnected(struct share_mode_lock *lck,
- struct files_struct *fsp)
-{
- struct share_mode_entry *e;
-
- if (lck->data->num_share_modes != 1) {
- return false;
- }
-
- if (fsp->op == NULL) {
- return false;
- }
- if (!fsp->op->global->durable) {
- return false;
- }
-
- e = find_share_mode_entry(
- lck,
- messaging_server_id(fsp->conn->sconn->msg_ctx),
- fsp->fh->gen_id);
- if (e == NULL) {
- return false;
- }
-
- DEBUG(10, ("Marking share mode entry disconnected for durable handle\n"));
-
- server_id_set_disconnected(&e->pid);
-
- /*
- * On reopen the caller needs to check that
- * the client comes with the correct handle.
- */
- e->share_file_id = fsp->op->global->open_persistent_id;
-
- lck->data->modified = true;
- return true;
-}
-
-/*******************************************************************
- Remove an oplock mid and mode entry from a share mode.
-********************************************************************/
-
-bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
-{
- struct share_mode_entry *e;
-
- e = find_share_mode_entry(
- lck,
- messaging_server_id(fsp->conn->sconn->msg_ctx),
- fsp->fh->gen_id);
- if (e == NULL) {
- return False;
- }
-
- remove_share_mode_lease(lck, e);
- return true;
-}
-
-/*******************************************************************
- Downgrade a oplock type from exclusive to level II.
-********************************************************************/
-
-bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
-{
- struct share_mode_entry *e;
-
- e = find_share_mode_entry(
- lck,
- messaging_server_id(fsp->conn->sconn->msg_ctx),
- fsp->fh->gen_id);
- if (e == NULL) {
- return False;
- }
-
- e->op_type = LEVEL_II_OPLOCK;
- lck->data->flags |= SHARE_MODE_HAS_READ_LEASE;
- lck->data->modified = True;
- return True;
-}
-
/****************************************************************************
Adds a delete on close token.
****************************************************************************/
@@ -1306,61 +1089,6 @@ bool file_has_open_streams(files_struct *fsp)
return state.found_one;
}
-bool share_mode_forall_entries(
- struct share_mode_lock *lck,
- bool (*fn)(struct share_mode_entry *e,
- bool *modified,
- void *private_data),
- void *private_data)
-{
- struct share_mode_data *d = lck->data;
- uint32_t i;
-
- i = 0;
- while (i<d->num_share_modes) {
- struct share_mode_entry *e = &d->share_modes[i];
- struct server_id pid = e->pid;
- uint64_t share_file_id = e->share_file_id;
- bool ok, stop;
- bool modified = false;
-
- ok = is_valid_share_mode_entry(e);
- if (!ok) {
- continue;
- }
-
- stop = fn(e, &modified, private_data);
-
- if (modified || e->stale) {
- d->modified = true;
- }
-
- if (modified) {
- /*
- * In a later commit we will sort the share
- * mode array keyed by pid and
- * share_file_id. Make sure that from within
- * this routine those values don't change.
- */
- SMB_ASSERT(server_id_equal(&pid, &e->pid));
- SMB_ASSERT(share_file_id == e->share_file_id);
- }
-
- if (stop) {
- return true;
- }
-
- if (e->stale) {
- *e = d->share_modes[d->num_share_modes-1];
- d->num_share_modes -= 1;
- } else {
- i += 1;
- }
- }
-
- return true;
-}
-
/*
* Walk share mode entries, looking at every lease only once
*/
diff --git a/source3/locking/share_mode_lock.c b/source3/locking/share_mode_lock.c
index 0fbb788c533..0cf8f5a39fe 100644
--- a/source3/locking/share_mode_lock.c
+++ b/source3/locking/share_mode_lock.c
@@ -59,6 +59,7 @@
/* the locking database handle */
static struct db_context *lock_db;
+static struct db_context *share_entries_db;
static bool locking_init_internal(bool read_only)
{
@@ -97,7 +98,29 @@ static bool locking_init_internal(bool read_only)
return false;
}
+ db_path = lock_path(talloc_tos(), "share_entries.tdb");
+ if (db_path == NULL) {
+ return false;
+ }
+
+ share_entries_db = db_open(
+ NULL, db_path,
+ SMB_OPEN_DATABASE_TDB_HASH_SIZE,
+ TDB_DEFAULT|
+ TDB_VOLATILE|
+ TDB_CLEAR_IF_FIRST|
+ TDB_INCOMPATIBLE_HASH,
+ read_only?O_RDONLY:O_RDWR|O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_3, DBWRAP_FLAG_NONE);
+ TALLOC_FREE(db_path);
+
+ if (share_entries_db == NULL) {
+ TALLOC_FREE(lock_db);
+ return false;
+ }
+
if (!posix_locking_init(read_only)) {
+ TALLOC_FREE(share_entries_db);
TALLOC_FREE(lock_db);
return False;
}
@@ -1252,3 +1275,863 @@ done:
talloc_free(frame);
return ret;
}
+
+static int share_mode_entry_cmp(
+ struct server_id pid1,
+ uint64_t share_file_id1,
+ struct server_id pid2,
+ uint64_t share_file_id2)
+{
+ int cmp;
+
+ cmp = server_id_cmp(&pid1, &pid2);
+ if (cmp != 0) {
+ return cmp;
+ }
+ if (share_file_id1 != share_file_id2) {
+ return (share_file_id1 < share_file_id2) ? -1 : 1;
+ }
+ return 0;
+}
+
+/*
+ * 132 is the sizeof an ndr-encoded struct share_mode_entry_buf.
+ * Reading/writing entries will immediately error out if this
+ * size differs (push/pull is done without allocs).
+ */
+
+struct share_mode_entry_buf {
+ uint8_t buf[132];
+};
+#define SHARE_MODE_ENTRY_SIZE (sizeof(struct share_mode_entry_buf))
+
+static bool share_mode_entry_put(
+ const struct share_mode_entry *e,
+ struct share_mode_entry_buf *dst)
+{
+ DATA_BLOB blob = { .data = dst->buf, .length = sizeof(dst->buf) };
+ enum ndr_err_code ndr_err;
+
+ if (DEBUGLEVEL>=10) {
+ DBG_DEBUG("share_mode_entry:\n");
+ NDR_PRINT_DEBUG(share_mode_entry, discard_const_p(void, e));
+ }
+
+ ndr_err = ndr_push_struct_into_fixed_blob(
+ &blob,
+ e,
+ (ndr_push_flags_fn_t)ndr_push_share_mode_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("ndr_push_share_mode_entry failed: %s\n",
+ ndr_errstr(ndr_err));
+ return false;
+ }
+
+ return true;
+}
+
+static bool share_mode_entry_get(
+ DATA_BLOB blob, struct share_mode_entry *e)
+{
+ enum ndr_err_code ndr_err = NDR_ERR_SUCCESS;
+
+ ndr_err = ndr_pull_struct_blob_all_noalloc(
+ &blob, e, (ndr_pull_flags_fn_t)ndr_pull_share_mode_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("ndr_pull_share_mode_entry failed\n");
+ return false;
+ }
+ return true;
+}
+
+static size_t share_mode_entry_find(
+ uint8_t *data,
+ size_t num_share_modes,
+ struct server_id pid,
+ uint64_t share_file_id,
+ struct share_mode_entry *e,
+ bool *match)
+{
+ ssize_t left, right, middle;
+
+ if (num_share_modes == 0) {
+ *match = false;
+ return 0;
+ }
+
+ left = 0;
+ right = (num_share_modes-1);
+
+ while (left <= right) {
+ DATA_BLOB blob;
+ int cmp;
+ bool ok;
+
+ middle = left + ((right - left) / 2);
+
+ DBG_DEBUG("left=%zu, right=%zu, middle=%zu\n",
+ left,
+ right,
+ middle);
+
+ blob = (DATA_BLOB) {
+ .data = data + middle * SHARE_MODE_ENTRY_SIZE,
+ .length = SHARE_MODE_ENTRY_SIZE,
+ };
+
+ DBG_DEBUG("blob.data=%p, blob.length=%zu\n",
+ blob.data,
+ blob.length);
+
+ ok = share_mode_entry_get(blob, e);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_get failed\n");
+ return false;
+ }
+
+ cmp = share_mode_entry_cmp(
+ e->pid, e->share_file_id, pid, share_file_id);
+ if (cmp == 0) {
+ *match = true;
+ return middle;
+ }
+
+ if (cmp < 0) {
+ right = middle-1;
+ } else {
+ left = middle+1;
+ }
+ }
+
+ *match = false;
+ return left;
+}
+
+struct set_share_mode_state {
+ struct share_mode_entry e;
+ uint32_t num_share_modes;
+ NTSTATUS status;
+};
+
+static void set_share_mode_fn(struct db_record *rec, void *private_data)
+{
+ struct set_share_mode_state *state = private_data;
+ TDB_DATA data = dbwrap_record_get_value(rec);
+ size_t idx, num_share_modes;
+ struct share_mode_entry tmp;
+ struct share_mode_entry_buf buf;
+ bool ok, found;
+
+ TDB_DATA dbufs[3];
+ size_t num_dbufs = 0;
+
+ if ((data.dsize % SHARE_MODE_ENTRY_SIZE) != 0) {
+ DBG_WARNING("Got invalid record size %zu\n", data.dsize);
+ state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ return;
+ }
+ num_share_modes = data.dsize / SHARE_MODE_ENTRY_SIZE;
+
+ ok = share_mode_entry_put(&state->e, &buf);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_put failed\n");
+ state->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+
+ DBG_DEBUG("num_share_modes=%zu\n", num_share_modes);
+
+ idx = share_mode_entry_find(
+ data.dptr,
+ num_share_modes,
+ state->e.pid,
+ state->e.share_file_id,
+ &tmp,
+ &found);
+ if (found) {
+ DBG_WARNING("Found duplicate share mode\n");
+ state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ return;
+ }
+
+ DBG_DEBUG("idx=%zu, found=%d\n", idx, (int)found);
+
+ if (idx > 0) {
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = data.dptr,
+ .dsize = idx * SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+ }
+
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+
+ if (idx < num_share_modes) {
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = data.dptr + idx * SHARE_MODE_ENTRY_SIZE,
+ .dsize = (num_share_modes-idx) * SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+ }
+
+ {
+ size_t i;
+ for (i=0; i<num_dbufs; i++) {
+ DBG_DEBUG("dbufs[%zu]=(%p, %zu)\n",
+ i,
+ dbufs[i].dptr,
+ dbufs[i].dsize);
+ }
+ }
+
+ state->num_share_modes = num_share_modes+1;
+ state->status = dbwrap_record_storev(rec, dbufs, num_dbufs, 0);
+}
+
+bool set_share_mode(struct share_mode_lock *lck,
+ struct files_struct *fsp,
+ uid_t uid,
+ uint64_t mid,
+ uint16_t op_type,
+ uint32_t share_access,
+ uint32_t access_mask)
+{
+ struct share_mode_data *d = lck->data;
+ struct set_share_mode_state state = {
+ .status = NT_STATUS_OK,
+ .e.pid = messaging_server_id(fsp->conn->sconn->msg_ctx),
+ .e.share_access = share_access,
+ .e.private_options = fsp->fh->private_options,
+ .e.access_mask = access_mask,
+ .e.op_mid = mid,
+ .e.op_type = op_type,
+ .e.time.tv_sec = fsp->open_time.tv_sec,
+ .e.time.tv_usec = fsp->open_time.tv_usec,
+ .e.share_file_id = fsp->fh->gen_id,
+ .e.uid = (uint32_t)uid,
+ .e.flags = (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) ?
+ SHARE_MODE_FLAG_POSIX_OPEN : 0,
+ .e.name_hash = fsp->name_hash,
+ };
+ NTSTATUS status;
+
+ if (op_type == LEASE_OPLOCK) {
+ const struct GUID *client_guid = fsp_client_guid(fsp);
+ state.e.client_guid = *client_guid;
+ state.e.lease_key = fsp->lease->lease.lease_key;
+ }
+
+ status = dbwrap_do_locked(
+ share_entries_db,
+ locking_key(&d->id),
+ set_share_mode_fn,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ DBG_WARNING("set_share_mode_fn failed: %s\n",
+ nt_errstr(state.status));
+ return false;
+ }
+
+ d->num_share_modes = state.num_share_modes;
+ d->modified = true;
+
+ return true;
+}
+
+struct share_mode_forall_entries_state {
+ struct share_mode_lock *lck;
+ bool (*fn)(struct share_mode_entry *e,
+ bool *modified,
+ void *private_data);
+ void *private_data;
+ size_t num_share_modes;
+ bool ok;
+};
+
+static bool share_mode_for_one_entry(
+ struct share_mode_forall_entries_state *state,
+ size_t *i,
+ size_t *num_share_modes,
+ TDB_DATA data,
+ bool *writeback)
+{
+ DATA_BLOB blob = {
+ .data = data.dptr + (*i) * SHARE_MODE_ENTRY_SIZE,
+ .length = SHARE_MODE_ENTRY_SIZE,
+ };
+ struct share_mode_entry e = {.pid.pid=0};
+ enum ndr_err_code ndr_err = NDR_ERR_SUCCESS;
+ bool modified = false;
+ bool stop = false;
+ struct server_id e_pid;
+ uint64_t e_share_file_id;
+
+ ndr_err = ndr_pull_struct_blob_all_noalloc(
+ &blob,
+ &e,
+ (ndr_pull_flags_fn_t)ndr_pull_share_mode_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("ndr_pull_share_mode_entry failed\n");
+ *i += 1;
+ return false;
+ }
+ if (DEBUGLEVEL >= 10) {
+ DBG_DEBUG("entry[%zu]:\n", *i);
+ NDR_PRINT_DEBUG(share_mode_entry, &e);
+ }
+
+ e_pid = e.pid;
+ e_share_file_id = e.share_file_id;
+
+ stop = state->fn(&e, &modified, state->private_data);
+
+ DBG_DEBUG("entry[%zu]: modified=%d, e.stale=%d\n",
+ *i,
+ (int)modified,
+ (int)e.stale);
+
+ if (modified) {
+ if (DEBUGLEVEL>=10) {
+ DBG_DEBUG("share_mode_entry:\n");
+ NDR_PRINT_DEBUG(share_mode_entry, &e);
+ }
+
+ /*
+ * Make sure sorting order is kept intact
+ */
+ SMB_ASSERT(server_id_equal(&e_pid, &e.pid));
+ SMB_ASSERT(e_share_file_id == e.share_file_id);
+
+ ndr_err = ndr_push_struct_into_fixed_blob(
+ &blob,
+ &e,
+ (ndr_push_flags_fn_t)
+ ndr_push_share_mode_entry);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("ndr_push_share_mode_entry "
+ "failed: %s\n",
+ ndr_errstr(ndr_err));
+ /*
+ * Not much we can do, just ignore it
+ */
+ }
+ *i += 1;
+ *writeback = true;
+ return stop;
+ }
+
+ if (e.stale) {
+ if (*i < *num_share_modes) {
+ memmove(blob.data,
+ blob.data + SHARE_MODE_ENTRY_SIZE,
+ (*num_share_modes - *i - 1) *
+ SHARE_MODE_ENTRY_SIZE);
+ }
+ *num_share_modes -= 1;
+ *writeback = true;
+ return stop;
+ }
+
+ if (stop) {
+ return true;
+ }
+
+ *i += 1;
+ return false;
+}
+
+static void share_mode_forall_entries_fn(
+ struct db_record *rec, void *private_data)
+{
+ struct share_mode_forall_entries_state *state = private_data;
+ struct share_mode_data *d = state->lck->data;
+ struct TDB_DATA data = dbwrap_record_get_value(rec);
+ size_t num_share_modes;
+ bool writeback = false;
+ NTSTATUS status;
+ bool stop = false;
+ size_t i;
+
+ if ((data.dsize % SHARE_MODE_ENTRY_SIZE) != 0) {
+ DBG_WARNING("Invalid data size %zu\n", data.dsize);
+ return;
+ }
+ num_share_modes = data.dsize / SHARE_MODE_ENTRY_SIZE;
+
+ DBG_DEBUG("num_share_modes=%zu\n", num_share_modes);
+
+ i = 0;
+ while (i<num_share_modes) {
+ stop = share_mode_for_one_entry(
+ state, &i, &num_share_modes, data, &writeback);
+ if (stop) {
+ break;
+ }
+ }
+
+ DBG_DEBUG("num_share_modes=%zu, writeback=%d\n",
+ num_share_modes,
+ (int)writeback);
+
+ if (!writeback) {
+ state->ok = true;
+ return;
+ }
+
+ if (num_share_modes != d->num_share_modes) {
+ d->num_share_modes = num_share_modes;
+ d->modified = true;
+ }
+
+ if (num_share_modes == 0) {
+ status = dbwrap_record_delete(rec);
+ } else {
+ TDB_DATA value = {
+ .dptr = data.dptr,
+ .dsize = num_share_modes * SHARE_MODE_ENTRY_SIZE,
+ };
+ status = dbwrap_record_store(rec, value, 0);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Storing record with %zu entries failed: %s\n",
+ num_share_modes,
+ nt_errstr(status));
+ return;
+ }
+
+
+ state->ok = true;
+}
+
+bool share_mode_forall_entries(
+ struct share_mode_lock *lck,
+ bool (*fn)(struct share_mode_entry *e,
+ bool *modified,
+ void *private_data),
+ void *private_data)
+{
+ struct share_mode_forall_entries_state state = {
+ .lck = lck,
+ .fn = fn,
+ .private_data = private_data,
+ };
+ NTSTATUS status;
+
+ status = dbwrap_do_locked(
+ share_entries_db,
+ locking_key(&lck->data->id),
+ share_mode_forall_entries_fn,
+ &state);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = NT_STATUS_OK;
+ state.ok = true;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("dbwrap_parse_record returned %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ return state.ok;
+}
+
+struct share_mode_entry_do_state {
+ struct server_id pid;
+ uint64_t share_file_id;
+ void (*fn)(struct share_mode_entry *e,
+ bool *modified,
+ void *private_data);
+ void *private_data;
+ size_t num_share_modes;
+ NTSTATUS status;
+};
+
+static void share_mode_entry_do_fn(struct db_record *rec, void *private_data)
+{
+ struct share_mode_entry_do_state *state = private_data;
+ struct TDB_DATA data = dbwrap_record_get_value(rec);
+ size_t idx;
+ bool found = false;
+ bool modified;
+ struct share_mode_entry e;
+ struct share_mode_entry_buf buf;
+ TDB_DATA dbufs[3];
+ size_t num_dbufs = 0;
+ NTSTATUS status;
+
+ if ((data.dsize % SHARE_MODE_ENTRY_SIZE) != 0) {
+ DBG_WARNING("Invalid data size %zu\n", data.dsize);
+ state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ return;
+ }
+ state->num_share_modes = data.dsize / SHARE_MODE_ENTRY_SIZE;
+
+ DBG_DEBUG("state->num_share_modes=%zu\n", state->num_share_modes);
+
+ idx = share_mode_entry_find(
+ data.dptr,
+ state->num_share_modes,
+ state->pid,
+ state->share_file_id,
+ &e,
+ &found);
+ if (!found) {
+ DBG_WARNING("Did not find share mode entry for %"PRIu64"\n",
+ state->share_file_id);
+ state->status = NT_STATUS_NOT_FOUND;
+ return;
+ }
+
+ state->fn(&e, &modified, state->private_data);
+
+ if (!e.stale && !modified) {
+ state->status = NT_STATUS_OK;
+ return;
+ }
+
+ if (idx > 0) {
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = data.dptr,
+ .dsize = idx * SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+ }
+
+ if (!e.stale) {
+ bool ok = share_mode_entry_put(&e, &buf);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_put failed\n");
+ state->status = NT_STATUS_INTERNAL_ERROR;
+ return;
+ }
+
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = buf.buf, .dsize = SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+ }
+
+ idx += 1;
+
+ if (idx < state->num_share_modes) {
+ size_t behind = state->num_share_modes - idx;
+ dbufs[num_dbufs] = (TDB_DATA) {
+ .dptr = data.dptr + idx * SHARE_MODE_ENTRY_SIZE,
+ .dsize = behind * SHARE_MODE_ENTRY_SIZE,
+ };
+ num_dbufs += 1;
+ }
+
+ if (e.stale) {
+ state->num_share_modes -= 1;
+ }
+
+ state->status = dbwrap_record_storev(rec, dbufs, num_dbufs, 0);
+ if (!NT_STATUS_IS_OK(state->status)) {
+ DBG_DEBUG("dbwrap_record_storev failed: %s\n",
+ nt_errstr(status));
+ return;
+ }
+}
+
+static bool share_mode_entry_do(
+ struct share_mode_lock *lck,
+ struct server_id pid,
+ uint64_t share_file_id,
+ void (*fn)(struct share_mode_entry *e,
+ bool *modified,
+ void *private_data),
+ void *private_data)
+{
+ struct share_mode_data *d = lck->data;
+ struct share_mode_entry_do_state state = {
+ .pid = pid,
+ .share_file_id = share_file_id,
+ .fn = fn,
+ .private_data = private_data,
+ };
+ NTSTATUS status;
+
+ status = dbwrap_do_locked(
+ share_entries_db,
+ locking_key(&d->id),
+ share_mode_entry_do_fn,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("share_mode_forall_entries failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ DBG_DEBUG("share_mode_entry_do_fn failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ if (d->num_share_modes != state.num_share_modes) {
+ d->num_share_modes = state.num_share_modes;
+ d->modified = true;
+ }
+
+ return true;
+}
+
+struct del_share_mode_state {
+ bool ok;
+};
+
+static void del_share_mode_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct del_share_mode_state *state = private_data;
+ e->stale = true;
+ state->ok = true;
+}
+
+bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp)
+{
+ struct del_share_mode_state state = { .ok = false };
+ bool ok;
+
+ ok = share_mode_entry_do(
+ lck,
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
+ fsp->fh->gen_id,
+ del_share_mode_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_do failed\n");
+ return false;
+ }
+ if (!state.ok) {
+ DBG_DEBUG("del_share_mode_fn failed\n");
+ return false;
+ }
+ return true;
+}
+
+struct remove_share_oplock_state {
+ bool ok;
+};
+
+static void remove_share_oplock_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct remove_share_oplock_state *state = private_data;
+
+ e->op_type = NO_OPLOCK;
+ *modified = true;
+ state->ok = true;
+}
+
+bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
+{
+ struct remove_share_oplock_state state = { .ok = false };
+ bool ok;
+
+ ok = share_mode_entry_do(
+ lck,
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
+ fsp->fh->gen_id,
+ remove_share_oplock_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_do failed\n");
+ return false;
+ }
+ if (!state.ok) {
+ DBG_DEBUG("remove_share_oplock_fn failed\n");
+ return false;
+ }
+
+ if (fsp->oplock_type == LEASE_OPLOCK) {
+ remove_lease_if_stale(
+ lck,
+ fsp_client_guid(fsp),
+ &fsp->lease->lease.lease_key);
+ }
+
+ lck->data->modified = true; /* signal watchers */
+
+ return true;
+}
+
+struct downgrade_share_oplock_state {
+ bool ok;
+};
+
+static void downgrade_share_oplock_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct downgrade_share_oplock_state *state = private_data;
+
+ e->op_type = LEVEL_II_OPLOCK;
+ *modified = true;
+ state->ok = true;
+}
+
+bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp)
+{
+ struct downgrade_share_oplock_state state = { .ok = false };
+ bool ok;
+
+ ok = share_mode_entry_do(
+ lck,
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
+ fsp->fh->gen_id,
+ downgrade_share_oplock_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_do failed\n");
+ return false;
+ }
+ if (!state.ok) {
+ DBG_DEBUG("downgrade_share_oplock_fn failed\n");
+ return false;
+ }
+
+ lck->data->flags |= SHARE_MODE_HAS_READ_LEASE;
+ lck->data->modified = true;
+
+ return true;
+}
+
+struct mark_share_mode_disconnected_state {
+ uint64_t open_persistent_id;
+ bool ok;
+};
+
+static void mark_share_mode_disconnected_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct mark_share_mode_disconnected_state *state = private_data;
+ server_id_set_disconnected(&e->pid);
+ e->share_file_id = state->open_persistent_id;
+ *modified = true;
+ state->ok = true;
+}
+
+bool mark_share_mode_disconnected(struct share_mode_lock *lck,
+ struct files_struct *fsp)
+{
+ struct mark_share_mode_disconnected_state state = {
+ .open_persistent_id = fsp->op->global->open_persistent_id,
+ };
+ bool ok;
+
+ if (lck->data->num_share_modes != 1) {
+ return false;
+ }
+
+ if (fsp->op == NULL) {
+ return false;
+ }
+ if (!fsp->op->global->durable) {
+ return false;
+ }
+
+ ok = share_mode_entry_do(
+ lck,
+ messaging_server_id(fsp->conn->sconn->msg_ctx),
+ fsp->fh->gen_id,
+ mark_share_mode_disconnected_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_do failed\n");
+ return false;
+ }
+ if (!state.ok) {
+ DBG_DEBUG("mark_share_mode_disconnected_fn failed\n");
+ return false;
+ }
+
+ lck->data->modified = true;
+ return true;
+}
+
+static void reset_share_mode_entry_del_fn(
+ struct share_mode_entry *e,
+ bool *modified,
+ void *private_data)
+{
+ struct set_share_mode_state *state = private_data;
+
+ state->e = *e;
+ e->stale = true;
+ state->status = NT_STATUS_OK;
+}
+
+bool reset_share_mode_entry(
+ struct share_mode_lock *lck,
+ struct server_id old_pid,
+ uint64_t old_share_file_id,
+ struct server_id new_pid,
+ uint64_t new_mid,
+ uint64_t new_share_file_id)
+{
+ struct share_mode_data *d = lck->data;
+ struct set_share_mode_state state = {
+ .status = NT_STATUS_INTERNAL_ERROR,
+ };
+ NTSTATUS status;
+ bool ok;
+
+ ok = share_mode_entry_do(
+ lck,
+ old_pid,
+ old_share_file_id,
+ reset_share_mode_entry_del_fn,
+ &state);
+ if (!ok) {
+ DBG_DEBUG("share_mode_entry_do failed\n");
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ DBG_DEBUG("reset_share_mode_entry_del_fn failed: %s\n",
+ nt_errstr(state.status));
+ return false;
+ }
+
+ state.status = NT_STATUS_INTERNAL_ERROR;
+ state.e.pid = new_pid;
+ state.e.op_mid = new_mid;
+ state.e.share_file_id = new_share_file_id;
+
+ status = dbwrap_do_locked(
+ share_entries_db,
+ locking_key(&d->id),
+ set_share_mode_fn,
+ &state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("dbwrap_do_locked failed: %s\n",
+ nt_errstr(status));
+ return false;
+ }
+ if (!NT_STATUS_IS_OK(state.status)) {
+ DBG_WARNING("set_share_mode_fn failed: %s\n",
+ nt_errstr(state.status));
+ return false;
+ }
+
+ d->num_share_modes = state.num_share_modes;
+ d->modified = true;
+
+ return true;
+}