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