1/* $OpenBSD: namespace.c,v 1.20 2020/03/05 07:39:25 martijn Exp $ */ 2 3/* 4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/queue.h> 21 22#include <assert.h> 23#include <errno.h> 24#include <stdio.h> 25#include <stdlib.h> 26#include <string.h> 27#include <zlib.h> 28 29#include "ldapd.h" 30#include "log.h" 31 32extern char *datadir; 33 34/* Maximum number of requests to queue per namespace during compaction. 35 * After this many requests, we return LDAP_BUSY. 36 */ 37#define MAX_REQUEST_QUEUE 10000 38 39static struct btval *namespace_find(struct namespace *ns, char *dn); 40static void namespace_queue_replay(int fd, short event, void *arg); 41static int namespace_set_fd(struct namespace *ns, 42 struct btree **bt, int fd, unsigned int flags); 43 44int 45namespace_begin_txn(struct namespace *ns, struct btree_txn **data_txn, 46 struct btree_txn **indx_txn, int rdonly) 47{ 48 if (ns->data_db == NULL || ns->indx_db == NULL) { 49 errno = EBUSY; /* namespace is being reopened */ 50 return -1; 51 } 52 53 if ((*data_txn = btree_txn_begin(ns->data_db, rdonly)) == NULL || 54 (*indx_txn = btree_txn_begin(ns->indx_db, rdonly)) == NULL) { 55 if (errno == ESTALE) { 56 if (*data_txn == NULL) 57 namespace_reopen_data(ns); 58 else 59 namespace_reopen_indx(ns); 60 errno = EBUSY; 61 } 62 log_warn("failed to open transaction"); 63 btree_txn_abort(*data_txn); 64 *data_txn = NULL; 65 return -1; 66 } 67 68 return 0; 69} 70 71int 72namespace_begin(struct namespace *ns) 73{ 74 return namespace_begin_txn(ns, &ns->data_txn, &ns->indx_txn, 0); 75} 76 77int 78namespace_commit(struct namespace *ns) 79{ 80 if (ns->indx_txn != NULL && 81 btree_txn_commit(ns->indx_txn) != BT_SUCCESS) { 82 log_warn("%s(indx): commit failed", ns->suffix); 83 btree_txn_abort(ns->data_txn); 84 ns->indx_txn = ns->data_txn = NULL; 85 return -1; 86 } 87 ns->indx_txn = NULL; 88 89 if (ns->data_txn != NULL && 90 btree_txn_commit(ns->data_txn) != BT_SUCCESS) { 91 log_warn("%s(data): commit failed", ns->suffix); 92 ns->data_txn = NULL; 93 return -1; 94 } 95 ns->data_txn = NULL; 96 97 return 0; 98} 99 100void 101namespace_abort(struct namespace *ns) 102{ 103 btree_txn_abort(ns->data_txn); 104 ns->data_txn = NULL; 105 106 btree_txn_abort(ns->indx_txn); 107 ns->indx_txn = NULL; 108} 109 110int 111namespace_open(struct namespace *ns) 112{ 113 unsigned int db_flags = 0; 114 115 assert(ns); 116 assert(ns->suffix); 117 118 if (ns->sync == 0) 119 db_flags |= BT_NOSYNC; 120 121 if (asprintf(&ns->data_path, "%s/%s_data.db", datadir, ns->suffix) == -1) 122 return -1; 123 log_info("opening namespace %s", ns->suffix); 124 ns->data_db = btree_open(ns->data_path, db_flags | BT_REVERSEKEY, 0644); 125 if (ns->data_db == NULL) 126 return -1; 127 128 btree_set_cache_size(ns->data_db, ns->cache_size); 129 130 if (asprintf(&ns->indx_path, "%s/%s_indx.db", datadir, ns->suffix) == -1) 131 return -1; 132 ns->indx_db = btree_open(ns->indx_path, db_flags, 0644); 133 if (ns->indx_db == NULL) 134 return -1; 135 136 btree_set_cache_size(ns->indx_db, ns->index_cache_size); 137 138 /* prepare request queue scheduler */ 139 evtimer_set(&ns->ev_queue, namespace_queue_replay, ns); 140 141 return 0; 142} 143 144static int 145namespace_reopen(const char *path) 146{ 147 struct open_req req; 148 149 log_debug("asking parent to open %s", path); 150 151 memset(&req, 0, sizeof(req)); 152 if (strlcpy(req.path, path, sizeof(req.path)) >= sizeof(req.path)) { 153 log_warnx("%s: path truncated", __func__); 154 return -1; 155 } 156 157 return imsgev_compose(iev_ldapd, IMSG_LDAPD_OPEN, 0, 0, -1, &req, 158 sizeof(req)); 159} 160 161int 162namespace_reopen_data(struct namespace *ns) 163{ 164 if (ns->data_db != NULL) { 165 btree_close(ns->data_db); 166 ns->data_db = NULL; 167 return namespace_reopen(ns->data_path); 168 } 169 return 1; 170} 171 172int 173namespace_reopen_indx(struct namespace *ns) 174{ 175 if (ns->indx_db != NULL) { 176 btree_close(ns->indx_db); 177 ns->indx_db = NULL; 178 return namespace_reopen(ns->indx_path); 179 } 180 return 1; 181} 182 183static int 184namespace_set_fd(struct namespace *ns, struct btree **bt, int fd, 185 unsigned int flags) 186{ 187 log_info("reopening namespace %s (entries)", ns->suffix); 188 btree_close(*bt); 189 if (ns->sync == 0) 190 flags |= BT_NOSYNC; 191 *bt = btree_open_fd(fd, flags); 192 if (*bt == NULL) 193 return -1; 194 return 0; 195} 196 197int 198namespace_set_data_fd(struct namespace *ns, int fd) 199{ 200 return namespace_set_fd(ns, &ns->data_db, fd, BT_REVERSEKEY); 201} 202 203int 204namespace_set_indx_fd(struct namespace *ns, int fd) 205{ 206 return namespace_set_fd(ns, &ns->indx_db, fd, 0); 207} 208 209void 210namespace_close(struct namespace *ns) 211{ 212 struct conn *conn; 213 struct search *search, *next; 214 struct request *req; 215 216 /* Cancel any queued requests for this namespace. 217 */ 218 if (ns->queued_requests > 0) { 219 log_warnx("cancelling %u queued requests on namespace %s", 220 ns->queued_requests, ns->suffix); 221 while ((req = TAILQ_FIRST(&ns->request_queue)) != NULL) { 222 TAILQ_REMOVE(&ns->request_queue, req, next); 223 ldap_respond(req, LDAP_UNAVAILABLE); 224 } 225 } 226 227 /* Cancel any searches on this namespace. 228 */ 229 TAILQ_FOREACH(conn, &conn_list, next) { 230 for (search = TAILQ_FIRST(&conn->searches); search != NULL; 231 search = next) { 232 next = TAILQ_NEXT(search, next); 233 if (search->ns == ns) 234 search_close(search); 235 } 236 } 237 238 free(ns->suffix); 239 btree_close(ns->data_db); 240 btree_close(ns->indx_db); 241 if (evtimer_pending(&ns->ev_queue, NULL)) 242 evtimer_del(&ns->ev_queue); 243 free(ns->data_path); 244 free(ns->indx_path); 245 free(ns); 246} 247 248void 249namespace_remove(struct namespace *ns) 250{ 251 TAILQ_REMOVE(&conf->namespaces, ns, next); 252 namespace_close(ns); 253} 254 255static struct btval * 256namespace_find(struct namespace *ns, char *dn) 257{ 258 struct btval key; 259 static struct btval val; 260 261 if (ns->data_db == NULL) { 262 errno = EBUSY; /* namespace is being reopened */ 263 return NULL; 264 } 265 266 memset(&key, 0, sizeof(key)); 267 memset(&val, 0, sizeof(val)); 268 269 key.data = dn; 270 key.size = strlen(dn); 271 272 if (btree_txn_get(ns->data_db, ns->data_txn, &key, &val) != 0) { 273 if (errno == ENOENT) 274 log_debug("%s: dn not found", dn); 275 else 276 log_warn("%s", dn); 277 278 if (errno == ESTALE) 279 namespace_reopen_data(ns); 280 281 return NULL; 282 } 283 284 return &val; 285} 286 287struct ber_element * 288namespace_get(struct namespace *ns, char *dn) 289{ 290 struct ber_element *elm; 291 struct btval *val; 292 293 if ((val = namespace_find(ns, dn)) == NULL) 294 return NULL; 295 296 elm = namespace_db2ber(ns, val); 297 btval_reset(val); 298 return elm; 299} 300 301int 302namespace_exists(struct namespace *ns, char *dn) 303{ 304 struct btval *val; 305 306 if ((val = namespace_find(ns, dn)) == NULL) 307 return 0; 308 btval_reset(val); 309 return 1; 310} 311 312int 313namespace_ber2db(struct namespace *ns, struct ber_element *root, 314 struct btval *val) 315{ 316 return ber2db(root, val, ns->compression_level); 317} 318 319struct ber_element * 320namespace_db2ber(struct namespace *ns, struct btval *val) 321{ 322 return db2ber(val, ns->compression_level); 323} 324 325static int 326namespace_put(struct namespace *ns, char *dn, struct ber_element *root, 327 int update) 328{ 329 int rc; 330 struct btval key, val; 331 332 assert(ns != NULL); 333 assert(ns->data_txn != NULL); 334 assert(ns->indx_txn != NULL); 335 336 memset(&key, 0, sizeof(key)); 337 key.data = dn; 338 key.size = strlen(dn); 339 340 if (namespace_ber2db(ns, root, &val) != 0) 341 return BT_FAIL; 342 343 rc = btree_txn_put(NULL, ns->data_txn, &key, &val, 344 update ? 0 : BT_NOOVERWRITE); 345 if (rc != BT_SUCCESS) { 346 if (errno == EEXIST) 347 log_debug("%s: already exists", dn); 348 else 349 log_warn("%s", dn); 350 goto done; 351 } 352 353 /* FIXME: if updating, try harder to just update changed indices. 354 */ 355 if (update && (rc = unindex_entry(ns, &key, root)) != BT_SUCCESS) 356 goto done; 357 358 rc = index_entry(ns, &key, root); 359 360done: 361 btval_reset(&val); 362 return rc; 363} 364 365int 366namespace_add(struct namespace *ns, char *dn, struct ber_element *root) 367{ 368 return namespace_put(ns, dn, root, 0); 369} 370 371int 372namespace_update(struct namespace *ns, char *dn, struct ber_element *root) 373{ 374 return namespace_put(ns, dn, root, 1); 375} 376 377int 378namespace_del(struct namespace *ns, char *dn) 379{ 380 int rc; 381 struct ber_element *root; 382 struct btval key, data; 383 384 assert(ns != NULL); 385 assert(ns->indx_txn != NULL); 386 assert(ns->data_txn != NULL); 387 388 memset(&key, 0, sizeof(key)); 389 memset(&data, 0, sizeof(data)); 390 391 key.data = dn; 392 key.size = strlen(key.data); 393 394 rc = btree_txn_del(NULL, ns->data_txn, &key, &data); 395 if (rc == BT_SUCCESS && (root = namespace_db2ber(ns, &data)) != NULL) 396 rc = unindex_entry(ns, &key, root); 397 398 btval_reset(&data); 399 return rc; 400} 401 402int 403namespace_has_referrals(struct namespace *ns) 404{ 405 return !SLIST_EMPTY(&ns->referrals); 406} 407 408struct namespace * 409namespace_lookup_base(const char *basedn, int include_referrals) 410{ 411 size_t blen, slen; 412 struct namespace *ns, *matched_ns = NULL; 413 414 assert(basedn); 415 blen = strlen(basedn); 416 417 TAILQ_FOREACH(ns, &conf->namespaces, next) { 418 slen = strlen(ns->suffix); 419 if ((include_referrals || !namespace_has_referrals(ns)) && 420 blen >= slen && 421 bcmp(basedn + blen - slen, ns->suffix, slen) == 0) { 422 /* Match the longest namespace suffix. */ 423 if (matched_ns == NULL || 424 strlen(ns->suffix) > strlen(matched_ns->suffix)) 425 matched_ns = ns; 426 } 427 } 428 429 return matched_ns; 430} 431 432struct namespace * 433namespace_for_base(const char *basedn) 434{ 435 return namespace_lookup_base(basedn, 0); 436} 437 438struct referrals * 439namespace_referrals(const char *basedn) 440{ 441 struct namespace *ns; 442 443 if ((ns = namespace_lookup_base(basedn, 1)) != NULL && 444 namespace_has_referrals(ns)) 445 return &ns->referrals; 446 447 if (!SLIST_EMPTY(&conf->referrals)) 448 return &conf->referrals; 449 450 return NULL; 451} 452 453int 454namespace_has_index(struct namespace *ns, const char *attr, 455 enum index_type type) 456{ 457 struct attr_index *ai; 458 459 assert(ns); 460 assert(attr); 461 TAILQ_FOREACH(ai, &ns->indices, next) { 462 if (strcasecmp(attr, ai->attr) == 0 && ai->type == type) 463 return 1; 464 } 465 466 return 0; 467} 468 469/* Queues modification requests while the namespace is being reopened. 470 */ 471int 472namespace_queue_request(struct namespace *ns, struct request *req) 473{ 474 if (ns->queued_requests > MAX_REQUEST_QUEUE) { 475 log_warn("%u requests already queued, sorry", 476 ns->queued_requests); 477 return -1; 478 } 479 480 TAILQ_INSERT_TAIL(&ns->request_queue, req, next); 481 ns->queued_requests++; 482 483 if (!evtimer_pending(&ns->ev_queue, NULL)) 484 namespace_queue_schedule(ns, 250000); 485 486 return 0; 487} 488 489static void 490namespace_queue_replay(int fd, short event, void *data) 491{ 492 struct namespace *ns = data; 493 struct request *req; 494 495 if (ns->data_db == NULL || ns->indx_db == NULL) { 496 log_debug("%s: database is being reopened", ns->suffix); 497 return; /* Database is being reopened. */ 498 } 499 500 if ((req = TAILQ_FIRST(&ns->request_queue)) == NULL) 501 return; 502 TAILQ_REMOVE(&ns->request_queue, req, next); 503 504 log_debug("replaying queued request"); 505 req->replayed = 1; 506 request_dispatch(req); 507 ns->queued_requests--; 508 509 if (!evtimer_pending(&ns->ev_queue, NULL)) 510 namespace_queue_schedule(ns, 0); 511} 512 513void 514namespace_queue_schedule(struct namespace *ns, unsigned int usec) 515{ 516 struct timeval tv; 517 518 tv.tv_sec = 0; 519 tv.tv_usec = usec; 520 evtimer_add(&ns->ev_queue, &tv); 521} 522 523/* Cancel all queued requests from the given connection. Drops matching 524 * requests from all namespaces without sending a response. 525 */ 526void 527namespace_cancel_conn(struct conn *conn) 528{ 529 struct namespace *ns; 530 struct request *req, *next; 531 532 TAILQ_FOREACH(ns, &conf->namespaces, next) { 533 for (req = TAILQ_FIRST(&ns->request_queue); req != NULL; 534 req = next) { 535 next = TAILQ_NEXT(req, next); 536 537 if (req->conn == conn) { 538 TAILQ_REMOVE(&ns->request_queue, req, next); 539 request_free(req); 540 } 541 } 542 } 543} 544 545int 546namespace_conn_queue_count(struct conn *conn) 547{ 548 struct namespace *ns; 549 struct request *req; 550 int count = 0; 551 552 TAILQ_FOREACH(ns, &conf->namespaces, next) { 553 TAILQ_FOREACH(req, &ns->request_queue, next) { 554 if (req->conn == conn) 555 count++; 556 } 557 } 558 559 return count; 560} 561