/* Unix SMB/CIFS implementation. Watch dbwrap record changes 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 . */ #include "includes.h" #include "system/filesys.h" #include "dbwrap/dbwrap.h" #include "dbwrap_watch.h" #include "dbwrap_open.h" #include "msg_channel.h" #include "lib/util/util_tdb.h" #include "lib/util/tevent_ntstatus.h" static struct db_context *dbwrap_record_watchers_db(void) { static struct db_context *watchers_db; if (watchers_db == NULL) { watchers_db = db_open( NULL, lock_path("dbwrap_watchers.tdb"), 0, TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_3); } return watchers_db; } static TDB_DATA dbwrap_record_watchers_key(TALLOC_CTX *mem_ctx, struct db_context *db, struct db_record *rec, TDB_DATA *rec_key) { const uint8_t *db_id; size_t db_id_len; TDB_DATA key, wkey; dbwrap_db_id(db, &db_id, &db_id_len); key = dbwrap_record_get_key(rec); wkey.dsize = sizeof(uint32_t) + db_id_len + key.dsize; wkey.dptr = talloc_array(mem_ctx, uint8_t, wkey.dsize); if (wkey.dptr == NULL) { return make_tdb_data(NULL, 0); } SIVAL(wkey.dptr, 0, db_id_len); memcpy(wkey.dptr + sizeof(uint32_t), db_id, db_id_len); memcpy(wkey.dptr + sizeof(uint32_t) + db_id_len, key.dptr, key.dsize); if (rec_key != NULL) { rec_key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len; rec_key->dsize = key.dsize; } return wkey; } static bool dbwrap_record_watchers_key_parse( TDB_DATA wkey, uint8_t **p_db_id, size_t *p_db_id_len, TDB_DATA *key) { size_t db_id_len; if (wkey.dsize < sizeof(uint32_t)) { DEBUG(1, ("Invalid watchers key\n")); return false; } db_id_len = IVAL(wkey.dptr, 0); if (db_id_len > (wkey.dsize - sizeof(uint32_t))) { DEBUG(1, ("Invalid watchers key, wkey.dsize=%d, " "db_id_len=%d\n", (int)wkey.dsize, (int)db_id_len)); return false; } *p_db_id = wkey.dptr + sizeof(uint32_t); *p_db_id_len = db_id_len; key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len; key->dsize = wkey.dsize - sizeof(uint32_t) - db_id_len; return true; } static NTSTATUS dbwrap_record_add_watcher(TDB_DATA w_key, struct server_id id) { struct TALLOC_CTX *frame = talloc_stackframe(); struct db_context *db; struct db_record *rec; TDB_DATA value; struct server_id *ids; size_t num_ids; NTSTATUS status; db = dbwrap_record_watchers_db(); if (db == NULL) { status = map_nt_error_from_unix(errno); goto fail; } rec = dbwrap_fetch_locked(db, talloc_tos(), w_key); if (rec == NULL) { status = map_nt_error_from_unix(errno); goto fail; } value = dbwrap_record_get_value(rec); if ((value.dsize % sizeof(struct server_id)) != 0) { status = NT_STATUS_INTERNAL_DB_CORRUPTION; goto fail; } ids = (struct server_id *)value.dptr; num_ids = value.dsize / sizeof(struct server_id); ids = talloc_array(talloc_tos(), struct server_id, num_ids + 1); if (ids == NULL) { status = NT_STATUS_NO_MEMORY; goto fail; } memcpy(ids, value.dptr, value.dsize); ids[num_ids] = id; num_ids += 1; status = dbwrap_record_store( rec, make_tdb_data((uint8_t *)ids, talloc_get_size(ids)), 0); fail: TALLOC_FREE(frame); return status; } static NTSTATUS dbwrap_record_del_watcher(TDB_DATA w_key, struct server_id id) { struct TALLOC_CTX *frame = talloc_stackframe(); struct db_context *db; struct db_record *rec; struct server_id *ids; size_t i, num_ids; TDB_DATA value; NTSTATUS status; db = dbwrap_record_watchers_db(); if (db == NULL) { status = map_nt_error_from_unix(errno); goto fail; } rec = dbwrap_fetch_locked(db, talloc_tos(), w_key); if (rec == NULL) { status = map_nt_error_from_unix(errno); goto fail; } value = dbwrap_record_get_value(rec); if ((value.dsize % sizeof(struct server_id)) != 0) { status = NT_STATUS_INTERNAL_DB_CORRUPTION; goto fail; } ids = (struct server_id *)value.dptr; num_ids = value.dsize / sizeof(struct server_id); for (i=0; idb = dbwrap_record_get_db(rec); state->ev = ev; state->req = req; state->msg = msg; watchers_db = dbwrap_record_watchers_db(); if (watchers_db == NULL) { tevent_req_nterror(req, map_nt_error_from_unix(errno)); return tevent_req_post(req, ev); } state->w_key = dbwrap_record_watchers_key(state, state->db, rec, &state->key); if (tevent_req_nomem(state->w_key.dptr, req)) { return tevent_req_post(req, ev); } ret = msg_channel_init(state, state->msg, MSG_DBWRAP_MODIFIED, &state->channel); if (ret != 0) { tevent_req_nterror(req, map_nt_error_from_unix(ret)); return tevent_req_post(req, ev); } status = dbwrap_record_add_watcher( state->w_key, messaging_server_id(state->msg)); if (tevent_req_nterror(req, status)) { return tevent_req_post(req, ev); } talloc_set_destructor(state, dbwrap_record_watch_state_destructor); subreq = msg_read_send(state, state->ev, state->channel); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, dbwrap_record_watch_done, req); return req; } static int dbwrap_record_watch_state_destructor( struct dbwrap_record_watch_state *s) { if (s->msg != NULL) { dbwrap_record_del_watcher( s->w_key, messaging_server_id(s->msg)); } return 0; } static void dbwrap_watch_record_stored(struct db_context *db, struct db_record *rec, void *private_data) { struct messaging_context *msg = talloc_get_type_abort( private_data, struct messaging_context); struct server_id *ids = NULL; size_t num_ids = 0; TDB_DATA w_key = { 0, }; DATA_BLOB w_blob; NTSTATUS status; uint32_t i; status = dbwrap_record_get_watchers(db, rec, talloc_tos(), &ids, &num_ids); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { goto done; } if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("dbwrap_record_get_watchers failed: %s\n", nt_errstr(status))); goto done; } w_key = dbwrap_record_watchers_key(talloc_tos(), db, rec, NULL); if (w_key.dptr == NULL) { DEBUG(1, ("dbwrap_record_watchers_key failed\n")); goto done; } w_blob.data = w_key.dptr; w_blob.length = w_key.dsize; for (i=0; ibuf.length == state->w_key.dsize) && (memcmp(rec->buf.data, state->w_key.dptr, rec->buf.length) == 0)) { tevent_req_done(req); return; } /* * Not our record, wait for the next one */ subreq = msg_read_send(state, state->ev, state->channel); if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, dbwrap_record_watch_done, req); } NTSTATUS dbwrap_record_watch_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct db_record **prec) { struct dbwrap_record_watch_state *state = tevent_req_data( req, struct dbwrap_record_watch_state); NTSTATUS status; struct db_record *rec; if (tevent_req_is_nterror(req, &status)) { return status; } if (prec == NULL) { return NT_STATUS_OK; } rec = dbwrap_fetch_locked(state->db, mem_ctx, state->key); if (rec == NULL) { return NT_STATUS_INTERNAL_DB_ERROR; } *prec = rec; return NT_STATUS_OK; } struct dbwrap_watchers_traverse_read_state { int (*fn)(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key, const struct server_id *watchers, size_t num_watchers, void *private_data); void *private_data; }; static int dbwrap_watchers_traverse_read_callback( struct db_record *rec, void *private_data) { struct dbwrap_watchers_traverse_read_state *state = (struct dbwrap_watchers_traverse_read_state *)private_data; uint8_t *db_id; size_t db_id_len; TDB_DATA w_key, key, w_data; int res; w_key = dbwrap_record_get_key(rec); w_data = dbwrap_record_get_value(rec); if (!dbwrap_record_watchers_key_parse(w_key, &db_id, &db_id_len, &key)) { return 0; } if ((w_data.dsize % sizeof(struct server_id)) != 0) { return 0; } res = state->fn(db_id, db_id_len, key, (struct server_id *)w_data.dptr, w_data.dsize / sizeof(struct server_id), state->private_data); return res; } void dbwrap_watchers_traverse_read( int (*fn)(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key, const struct server_id *watchers, size_t num_watchers, void *private_data), void *private_data) { struct dbwrap_watchers_traverse_read_state state; struct db_context *db; db = dbwrap_record_watchers_db(); if (db == NULL) { return; } state.fn = fn; state.private_data = private_data; dbwrap_traverse_read(db, dbwrap_watchers_traverse_read_callback, &state, NULL); } static int dbwrap_wakeall_cb(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key, const struct server_id *watchers, size_t num_watchers, void *private_data) { struct messaging_context *msg = talloc_get_type_abort( private_data, struct messaging_context); uint32_t i; DATA_BLOB blob; blob.data = key.dptr; blob.length = key.dsize; for (i=0; i