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
32 extern 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
39 static struct btval *namespace_find(struct namespace *ns, char *dn);
40 static void namespace_queue_replay(int fd, short event, void *arg);
41 static int namespace_set_fd(struct namespace *ns,
42 struct btree **bt, int fd, unsigned int flags);
43
44 int
namespace_begin_txn(struct namespace * ns,struct btree_txn ** data_txn,struct btree_txn ** indx_txn,int rdonly)45 namespace_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
71 int
namespace_begin(struct namespace * ns)72 namespace_begin(struct namespace *ns)
73 {
74 return namespace_begin_txn(ns, &ns->data_txn, &ns->indx_txn, 0);
75 }
76
77 int
namespace_commit(struct namespace * ns)78 namespace_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
100 void
namespace_abort(struct namespace * ns)101 namespace_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
110 int
namespace_open(struct namespace * ns)111 namespace_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
144 static int
namespace_reopen(const char * path)145 namespace_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
161 int
namespace_reopen_data(struct namespace * ns)162 namespace_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
172 int
namespace_reopen_indx(struct namespace * ns)173 namespace_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
183 static int
namespace_set_fd(struct namespace * ns,struct btree ** bt,int fd,unsigned int flags)184 namespace_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
197 int
namespace_set_data_fd(struct namespace * ns,int fd)198 namespace_set_data_fd(struct namespace *ns, int fd)
199 {
200 return namespace_set_fd(ns, &ns->data_db, fd, BT_REVERSEKEY);
201 }
202
203 int
namespace_set_indx_fd(struct namespace * ns,int fd)204 namespace_set_indx_fd(struct namespace *ns, int fd)
205 {
206 return namespace_set_fd(ns, &ns->indx_db, fd, 0);
207 }
208
209 void
namespace_close(struct namespace * ns)210 namespace_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
248 void
namespace_remove(struct namespace * ns)249 namespace_remove(struct namespace *ns)
250 {
251 TAILQ_REMOVE(&conf->namespaces, ns, next);
252 namespace_close(ns);
253 }
254
255 static struct btval *
namespace_find(struct namespace * ns,char * dn)256 namespace_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
287 struct ber_element *
namespace_get(struct namespace * ns,char * dn)288 namespace_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
301 int
namespace_exists(struct namespace * ns,char * dn)302 namespace_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
312 int
namespace_ber2db(struct namespace * ns,struct ber_element * root,struct btval * val)313 namespace_ber2db(struct namespace *ns, struct ber_element *root,
314 struct btval *val)
315 {
316 return ber2db(root, val, ns->compression_level);
317 }
318
319 struct ber_element *
namespace_db2ber(struct namespace * ns,struct btval * val)320 namespace_db2ber(struct namespace *ns, struct btval *val)
321 {
322 return db2ber(val, ns->compression_level);
323 }
324
325 static int
namespace_put(struct namespace * ns,char * dn,struct ber_element * root,int update)326 namespace_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
360 done:
361 btval_reset(&val);
362 return rc;
363 }
364
365 int
namespace_add(struct namespace * ns,char * dn,struct ber_element * root)366 namespace_add(struct namespace *ns, char *dn, struct ber_element *root)
367 {
368 return namespace_put(ns, dn, root, 0);
369 }
370
371 int
namespace_update(struct namespace * ns,char * dn,struct ber_element * root)372 namespace_update(struct namespace *ns, char *dn, struct ber_element *root)
373 {
374 return namespace_put(ns, dn, root, 1);
375 }
376
377 int
namespace_del(struct namespace * ns,char * dn)378 namespace_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
402 int
namespace_has_referrals(struct namespace * ns)403 namespace_has_referrals(struct namespace *ns)
404 {
405 return !SLIST_EMPTY(&ns->referrals);
406 }
407
408 struct namespace *
namespace_lookup_base(const char * basedn,int include_referrals)409 namespace_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
432 struct namespace *
namespace_for_base(const char * basedn)433 namespace_for_base(const char *basedn)
434 {
435 return namespace_lookup_base(basedn, 0);
436 }
437
438 struct referrals *
namespace_referrals(const char * basedn)439 namespace_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
453 int
namespace_has_index(struct namespace * ns,const char * attr,enum index_type type)454 namespace_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 */
471 int
namespace_queue_request(struct namespace * ns,struct request * req)472 namespace_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
489 static void
namespace_queue_replay(int fd,short event,void * data)490 namespace_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
513 void
namespace_queue_schedule(struct namespace * ns,unsigned int usec)514 namespace_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 */
526 void
namespace_cancel_conn(struct conn * conn)527 namespace_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
545 int
namespace_conn_queue_count(struct conn * conn)546 namespace_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