xref: /illumos-gate/usr/src/lib/libnisdb/nis_db.cc (revision d48be21240dfd051b689384ce2b23479d757f2d8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  *	nis_db.cc
23  *
24  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  *
27  * Copyright 2015 RackTop Systems.
28  */
29 
30 
31 #include <sys/param.h>
32 #include <strings.h>
33 #include <syslog.h>
34 #include "nisdb_mt.h"
35 #include "db_headers.h"
36 #include "db_entry.h"
37 #include "db.h"
38 #include "db_dictionary.h"
39 #include "db_pickle.h"
40 #include "nis_db.h"
41 #include "nis_ldap.h"
42 #include "ldap_util.h"
43 #include "ldap_parse.h"
44 #include "ldap_glob.h"
45 #include "ldap_xdr.h"
46 #include "ldap_glob.h"
47 
48 db_dictionary	curdict;
49 db_dictionary	tempdict; /* a temporary one */
50 
51 db_dictionary *InUseDictionary = &curdict;
52 db_dictionary *FreeDictionary = &tempdict;
53 
54 extern "C" {
55 static db_result	*db_add_entry_x(char *tab, int numattrs,
56 					nis_attr *attrname, entry_obj * newobj,
57 					int skiplog, int nosync);
58 db_status		db_table_exists(char *table_name);
59 
60 /*
61  * (Imported from rpc.nisd/nis_xx_proc.c)
62  *
63  * 'tbl_prototype' is used to create a table that holds a directory.
64  */
65 static table_col cols[2] = {
66 	{(char *)"object", TA_BINARY+TA_XDR, 0},
67 	{(char *)"name", TA_CASE+TA_SEARCHABLE, 0}
68 };
69 
70 table_obj tbl_prototype = { (char *)"DIRECTORY", 2, ' ', {2, &cols[0]}, NULL };
71 }
72 
73 /*
74  * Free resources associated with a db_result structure
75  */
76 void
77 db_free_result(db_result *dr)
78 {
79 	int	i;
80 
81 	if (dr == 0)
82 		return;
83 
84 	/* Can't have valid objects */
85 	if (dr->status != DB_SUCCESS) {
86 		free(dr);
87 		return;
88 	}
89 
90 	for (i = 0; i < dr->objects.objects_len; i++)
91 		free_entry(dr->objects.objects_val[i]);
92 	free(dr->objects.objects_val);
93 	free(dr);
94 }
95 
96 
97 /* Return an empty db_result structure with its status field set to 's'. */
98 db_result*
99 empty_result(db_status s)
100 {
101 	db_result * res = new db_result;
102 	if (res != NULL)  {
103 		res->status = s;
104 		res->nextinfo.db_next_desc_len = 0;
105 		res->nextinfo.db_next_desc_val = NULL;
106 		res->objects.objects_len = 0;
107 		res->objects.objects_val = NULL;
108 	} else {
109 		WARNING("nis_db::empty_result: cannot allocate space");
110 	}
111 	return (res);
112 }
113 
114 static db_result*
115 set_result(db_result* res, db_status s)
116 {
117 	if (res != NULL)  {
118 		res->status = s;
119 	}
120 	return (res);
121 }
122 
123 /*
124  * Given a FQ object name for a table or directory, return the (db *)
125  * corresponding to the object.
126  */
127 db *
128 tableDB(char *tableName) {
129 	db_table_desc	*tbl = 0;
130 	char		*intName;
131 	db		*dbase;
132 
133 	intName = internalTableName(tableName);
134 	if (intName == 0)
135 		return (0);
136 
137 	dbase = InUseDictionary->find_table(intName, &tbl);
138 
139 	sfree(intName);
140 
141 	return (dbase);
142 }
143 
144 extern "C" {
145 
146 bool_t
147 db_in_dict_file(char *name)
148 {
149 	return (InUseDictionary->find_table_desc(name) != NULL);
150 
151 }
152 
153 const char
154 *db_perror(db_status dbstat)
155 {
156 	const char *str = NULL;
157 
158 	switch (dbstat) {
159 		case DB_SUCCESS:
160 			str = "Success";
161 			break;
162 		case DB_NOTFOUND:
163 			str = "Not Found";
164 			break;
165 		case DB_BADTABLE:
166 			str = "Bad Table";
167 			break;
168 		case DB_BADQUERY:
169 			str = "Bad Query";
170 			break;
171 		case DB_BADOBJECT:
172 			str = "Bad Object";
173 			break;
174 		case DB_MEMORY_LIMIT:
175 			str = "Memory limit exceeded";
176 			break;
177 		case DB_STORAGE_LIMIT:
178 			str = "Database storage limit exceeded";
179 			break;
180 		case DB_INTERNAL_ERROR:
181 			str = "Database internal error";
182 			break;
183 		case DB_SYNC_FAILED:
184 			str = "Sync of log file failed";
185 			break;
186 		default:
187 			str = "Unknown Error";
188 			break;
189 	}
190 	return (str);
191 }
192 
193 bool_t
194 db_extract_dict_entries(char *newdict, char **fs, int fscnt)
195 {
196 	/*
197 	 * Use the "FreeDictionary" ptr for the backup
198 	 * dictionary.
199 	 */
200 	if (!FreeDictionary->inittemp(newdict, *InUseDictionary))
201 		return (FALSE);
202 	return (InUseDictionary->extract_entries (*FreeDictionary,
203 		fs, fscnt));
204 }
205 
206 bool_t
207 db_copy_file(char *infile, char *outfile)
208 {
209 	return (InUseDictionary->copyfile(infile, outfile));
210 
211 }
212 
213 
214 /*
215  * The tok and repl parameters will allow us to merge two dictionaries
216  * that reference tables from different domains (master/replica in live
217  * in different domains). If set to NULL, then the dictionary merge is
218  * done as normal (no name changing).
219  */
220 db_status
221 db_begin_merge_dict(char *newdict, char *tok, char *repl)
222 {
223 	db_status dbstat;
224 
225 	/*
226 	 * It is assumed that InUseDictionary has already been initialized.
227 	 */
228 	dbstat = InUseDictionary->checkpoint();
229 	if (dbstat != DB_SUCCESS)
230 		return (dbstat);
231 
232 	/*
233 	 * Use the "FreeDictionary" ptr for the backup
234 	 * dictionary.
235 	 */
236 	if (!FreeDictionary->init(newdict))
237 		return (DB_INTERNAL_ERROR);
238 
239 	return (InUseDictionary->merge_dict(*FreeDictionary,
240 		tok, repl));
241 }
242 
243 
244 db_status
245 db_end_merge_dict()
246 {
247 	db_status	dbstat;
248 
249 	dbstat = InUseDictionary->checkpoint();
250 	if (dbstat != DB_SUCCESS) {
251 		return (dbstat);
252 	}
253 	dbstat = InUseDictionary->db_shutdown();
254 	if (dbstat != DB_SUCCESS) {
255 		return (dbstat);
256 	}
257 	dbstat = FreeDictionary->db_shutdown();
258 	if (dbstat != DB_SUCCESS) {
259 		return (dbstat);
260 	}
261 	return (dbstat);
262 }
263 
264 
265 
266 db_status
267 db_abort_merge_dict()
268 {
269 	db_status	dbstat;
270 
271 	dbstat = InUseDictionary->db_shutdown();
272 	if (dbstat != DB_SUCCESS)
273 		return (dbstat);
274 	dbstat = FreeDictionary->db_shutdown();
275 	return (dbstat);
276 }
277 
278 
279 /*
280  * Initialize system (dictionary) using file 'filename'.  If system cannot
281  * be read from file, it is initialized to be empty. Returns TRUE if
282  * initialization succeeds, FALSE otherwise.
283  * This function must be called before any other.
284 */
285 bool_t
286 db_initialize(char * filename)
287 {
288 	return (InUseDictionary->init(filename));
289 }
290 
291 
292 /*
293  * Massage the dictionary file by replacing the specified token with the
294  * the replacement string. This function is needed to provide backwards
295  * compatibility for providing a transportable dictionary file. The idea
296  * is that rpc.nisd will call this function when it wants to change the
297  * /var/nis/<hostname> strings with something like /var/nis/data.
298  *
299  */
300 db_status
301 db_massage_dict(char *newdictname, char *tok, char *repl)
302 {
303 	return (InUseDictionary->massage_dict(newdictname, tok, repl));
304 }
305 
306 
307 
308 /*
309  * Create new table using given table name and table descriptor.
310  * Returns DB_SUCCESS if successful; appropriate error code otherwise.
311 */
312 db_status
313 db_create_table(char * table_name, table_obj * table_desc)
314 {
315 	return (InUseDictionary->add_table(table_name, table_desc));
316 }
317 
318 /*
319  * Destroys table named by 'table_name.'  Returns DB_SUCCESS if successful,
320  * error code otherwise.  Note that currently, the removed table is no
321  * longer accessible from this interface and all files associated with it
322  * are removed from stable storage.
323 */
324 db_status
325 db_destroy_table(char * table_name)
326 {
327 	return (InUseDictionary->delete_table(table_name));
328 }
329 
330 
331 /*
332 * Return a copy of the first entry in the specified table, that satisfies
333 * the given attributes.  The returned structure 'db_result' contains the status,
334 * the  copy of the object, and a 'db_next_desc' to be used for the 'next'
335 * operation.
336  */
337 db_result *
338 db_first_entry(char * table_name, int numattrs, nis_attr * attrname)
339 {
340 	db_result * safety = empty_result(DB_SUCCESS);
341 	db_table_desc * tbl = NULL;
342 	db * dbase = InUseDictionary->find_table(table_name, &tbl);
343 
344 	if (tbl == NULL || dbase == NULL)
345 		return (set_result(safety, DB_BADTABLE));
346 	else {
347 		db_result * res = NULL;
348 		db_query *query = NULL;
349 
350 		if (numattrs != 0) {
351 			query = InUseDictionary->translate_to_query(tbl,
352 					numattrs, attrname);
353 			if (query == NULL)
354 				return (set_result(safety,
355 						DB_BADQUERY));
356 		}
357 		res = dbase->execute(DB_FIRST, query, NULL, NULL);
358 		if (query) delete query;
359 		if (safety) delete safety;
360 		return (res);
361 	}
362 }
363 
364 /*
365  * Return a copy of the next entry in the specified table as specified by
366  * the 'next_desc'.  The returned structure 'db_result' contains the status,
367  * a copy of the object, and a db_next_desc to be used for a subsequent
368  * 'next' operation.
369 */
370 db_result *
371 db_next_entry(char * table_name, db_next_desc * next_desc)
372 {
373 	db_result * safety = empty_result(DB_SUCCESS);
374 	db * dbase = InUseDictionary->find_table(table_name);
375 
376 	if (dbase != NULL) {
377 		if (safety) delete safety;
378 		return (dbase->execute(DB_NEXT, NULL, NULL, next_desc));
379 	} else
380 		return (set_result(safety, DB_BADTABLE));
381 }
382 
383 /*
384  * Indicate to the system that you are no longer interested in the rest of the
385  * results identified by [next_desc].  After executing this operation, the
386  * [next_desc] is no longer valid (cannot  be used as an argument for next).
387 */
388 
389 db_result *
390 db_reset_next_entry(char * table_name, db_next_desc * next_desc)
391 {
392 	db_result * safety = empty_result(DB_SUCCESS);
393 	db * dbase = InUseDictionary->find_table(table_name);
394 
395 	if (dbase != NULL) {
396 		if (safety) delete safety;
397 		return (dbase->execute(DB_RESET_NEXT,
398 					NULL, NULL, next_desc));
399 	} else
400 		return (set_result(safety, DB_BADTABLE));
401 }
402 
403 /*
404  * Returns copies of entries that satisfy the given attributes from table.
405  * Returns the status and entries in a db_result structure.
406  * If no attributes are specified, DB_BADQUERY is returned.
407 */
408 db_result *
409 __db_list_entries(char * table_name, int numattrs, nis_attr * attrname,
410 			bool_t useDeferred)
411 {
412 	db_result * safety = empty_result(DB_SUCCESS);
413 	db_table_desc * tbl = NULL;
414 	db * dbase = InUseDictionary->find_table(table_name, &tbl,
415 							useDeferred);
416 
417 	if (tbl == NULL || dbase == NULL)
418 		return (set_result(safety, DB_BADTABLE));
419 	else {
420 		db_result * res = NULL;
421 		if (numattrs != 0) {
422 			db_query *query;
423 			query = InUseDictionary->translate_to_query(tbl,
424 						    numattrs, attrname);
425 			if (query == NULL)
426 				return (set_result(safety,
427 							DB_BADQUERY));
428 			res = dbase->execute(DB_LOOKUP, query,
429 							NULL, NULL);
430 			delete query;
431 		} else {
432 			res = dbase->execute(DB_ALL, NULL, NULL, NULL);
433 		}
434 		if (safety) delete safety;
435 		return (res);
436 	}
437 }
438 
439 db_result *
440 db_list_entries(char *table_name, int numattrs, nis_attr *attrname) {
441 	return (__db_list_entries(table_name, numattrs, attrname, TRUE));
442 }
443 
444 /*
445  * Input:	A fully qualified object name (example: "x.y.z").
446  * Output:	Returns the first level of the object name ("x").
447  *		If 'tableP' is non-NULL, '*tableP' will contain
448  *		the internal table name for "y.z".
449  *
450  * Both the return value and '*tableP' must be freed by the caller.
451  */
452 char *
453 entryName(const char *msg, char *objName, char **tableP) {
454 	char		*name, *table, *dir;
455 	const char	*myself = "entryName";
456 
457 	if (msg == 0)
458 		msg = myself;
459 
460 	name = sdup(msg, T, objName);
461 	if (name == 0)
462 		return (0);
463 
464 	dir = strchr(name, '.');
465 	if (dir == 0) {
466 		sfree(name);
467 		return (0);
468 	}
469 	*(dir++) = '\0';
470 
471 	if (tableP == 0)
472 		return (name);
473 
474 	table = internalTableName(dir);
475 	if (table == 0) {
476 		sfree(name);
477 		return (0);
478 	}
479 
480 	*tableP = table;
481 
482 	return (name);
483 }
484 
485 #define	RETSTAT(obj, status) \
486 	{ \
487 		if (statP != 0) \
488 			*statP = status; \
489 		return (obj); \
490 	}
491 
492 /*
493  * Given a fully qualified object name, retrive a copy of the object,
494  * using the NIS+ DB only (i.e., no LDAP). Avoids using nis_leaf_of()
495  * etc., since they aren't re-entrant.
496  */
497 nis_object *
498 dbFindObject(char *objName, db_status *statP) {
499 	char		buf[MAXPATHLEN+NIS_MAXNAMELEN+1];
500 	char		*name, *table = 0;
501 	nis_attr	attr;
502 	db		*dbase;
503 	db_result	*res;
504 	db_table_desc	*tbl = 0;
505 	db_query	*query;
506 	db_mindex	*mindex;
507 	nis_object	*o;
508 	int		lstat;
509 	const char	*myself = "dbFindObject";
510 
511 	if (objName == 0)
512 		RETSTAT(0, DB_BADQUERY);
513 
514 	/* The root dir is treated specially */
515 	table = internalTableName(objName);
516 	if (table == 0)
517 		RETSTAT(0, DB_BADQUERY);
518 	if (strcmp(ROOTDIRFILE, table) == 0) {
519 		sfree(table);
520 
521 		o = get_root_object();
522 		if (o == 0)
523 			RETSTAT(0, DB_NOTFOUND);
524 
525 		RETSTAT(o, DB_SUCCESS);
526 	}
527 
528 	/* If not the root dir, find the directory where the entry lives */
529 
530 	sfree(table);
531 	name = entryName(myself, objName, &table);
532 	if (name == 0 || table == 0) {
533 		sfree(name);
534 		RETSTAT(0, DB_MEMORY_LIMIT);
535 	}
536 
537 	dbase = InUseDictionary->find_table_noLDAP(table, &tbl, TRUE, TRUE);
538 	sfree(table);
539 	if (dbase != 0)
540 		mindex = dbase->mindex();
541 	if (dbase == 0 || tbl == 0 || mindex == 0) {
542 		sfree(name);
543 		RETSTAT(0, DB_BADTABLE);
544 	}
545 
546 	WRITELOCKNR(mindex, lstat, "mindex w dbFindObject");
547 	if (lstat != 0) {
548 		sfree(name);
549 		RETSTAT(0, DB_LOCK_ERROR);
550 	}
551 
552 	attr.zattr_ndx = (char *)"name";
553 	attr.zattr_val.zattr_val_val = name;
554 	attr.zattr_val.zattr_val_len = slen(name) + 1;
555 
556 	query = InUseDictionary->translate_to_query(tbl, 1, &attr);
557 	if (query == 0) {
558 		sfree(name);
559 		WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject");
560 		RETSTAT(0, DB_BADQUERY);
561 	}
562 
563 	/* Only want to look in the local DB */
564 	mindex->setNoLDAPquery();
565 
566 	res = dbase->execute(DB_LOOKUP, query, 0, 0);
567 
568 	mindex->clearNoLDAPquery();
569 
570 	delete query;
571 
572 	sfree(name);
573 
574 	WRITEUNLOCKNR(mindex, lstat, "mindex wu dbFindObject");
575 	if (lstat != 0) {
576 		db_free_result(res);
577 		RETSTAT(0, DB_LOCK_ERROR);
578 	}
579 
580 	if (res == 0)
581 		RETSTAT(0, DB_MEMORY_LIMIT);
582 
583 	if (res->status != DB_SUCCESS) {
584 		db_status	st = res->status;
585 
586 		db_free_result(res);
587 		RETSTAT(0, st);
588 	}
589 
590 	if (res->objects.objects_len != 1 || res->objects.objects_val == 0 ||
591 			res->objects.objects_val[0] == 0) {
592 		db_free_result(res);
593 		RETSTAT(0, DB_BADOBJECT);
594 	}
595 
596 	o = unmakePseudoEntryObj(res->objects.objects_val[0], 0);
597 
598 	db_free_result(res);
599 
600 	if (o == 0) {
601 		RETSTAT(0, DB_BADOBJECT);
602 	}
603 
604 	RETSTAT(o, DB_SUCCESS);
605 }
606 
607 /*
608  * Return the object specified by 't' or 'objName' from LDAP. Set
609  * the LDAP status in '*statP'.
610  */
611 nis_object *
612 ldapFindObj(__nis_table_mapping_t *t, char *objName, int *statP) {
613 	nis_object	*o;
614 	int		stat;
615 	const char	*myself = "ldapFindObj";
616 
617 	if (t == 0) {
618 		char	*table, tbuf[MAXPATHLEN + NIS_MAXNAMELEN + 1];
619 
620 		if (objName == 0) {
621 			if (statP != 0)
622 				*statP = LDAP_PARAM_ERROR;
623 			return (0);
624 		}
625 
626 		/* Look for mapping */
627 		table = internal_table_name(objName, tbuf);
628 		if (table == 0) {
629 			if (statP != 0)
630 				*statP = LDAP_PARAM_ERROR;
631 			return (0);
632 		}
633 
634 		t = (__nis_table_mapping_t *)__nis_find_item_mt(table,
635 						&ldapMappingList, 0, 0);
636 		if (t == 0) {
637 			/* Not really an error; just not mapped */
638 			*statP = LDAP_SUCCESS;
639 			return (0);
640 		}
641 	}
642 
643 	o = 0;
644 	stat = objFromLDAP(t, &o, 0, 0);
645 
646 	if (statP != 0)
647 		*statP = stat;
648 
649 	return (o);
650 }
651 
652 /*
653  * Look for the specified object, first locally, then in LDAP.
654  */
655 nis_object *
656 findObj(char *name, db_status *statP, int *lstatP) {
657 	nis_object	*o;
658 	db_status	stat = DB_SUCCESS;
659 	int		lstat = LDAP_SUCCESS;
660 	const char	*myself = "findObj";
661 
662 	o = dbFindObject(name, &stat);
663 
664 	if (o == 0) {
665 		if (stat != DB_NOTFOUND)
666 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
667 				"%s: DB error %d looking for \"%s\"",
668 				myself, stat, NIL(name));
669 
670 		o = ldapFindObj(0, name, &lstat);
671 		if (o == 0) {
672 			if (lstat != LDAP_SUCCESS &&
673 					lstat != LDAP_NO_SUCH_OBJECT)
674 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
675 				"%s: LDAP error looking for \"%s\": %s",
676 					myself, NIL(name),
677 					ldap_err2string(lstat));
678 		}
679 	}
680 
681 	if (statP != 0)
682 		*statP = stat;
683 	if (lstatP != 0)
684 		*lstatP = lstat;
685 
686 	return (o);
687 }
688 
689 /*
690  * Delete the specified object from the local DB.
691  */
692 db_status
693 dbDeleteObj(char *objName) {
694 	nisdb_tsd_t	*tsd = __nisdb_get_tsd();
695 	nis_object	*o;
696 	db_status	stat;
697 	nisdb_obj_del_t	*nod, *tmp;
698 	int		xid;
699 	const char	*myself = "dbDeleteObj";
700 
701 	if (objName == 0)
702 		return (DB_SUCCESS);
703 
704 	/*
705 	 * Since in-structure locks can't completely protect
706 	 * during structure deletion, we just note that the
707 	 * object should be deleted, and leave that for a
708 	 * (slightly) later time in rpc.nisd, where we can
709 	 * use the rpc.nisd's table/directory locks for
710 	 * protection.
711 	 */
712 
713 	if (tsd == 0)
714 		return (DB_INTERNAL_ERROR);
715 
716 	o = dbFindObject(objName, &stat);
717 	if (o == 0) {
718 		if (stat == DB_NOTFOUND)
719 			return (DB_SUCCESS);
720 		else
721 			return (stat);
722 	}
723 
724 	/*
725 	 * In order to prevent a chicken-and-egg problem (if the
726 	 * object doesn't exist in LDAP, is that because we just
727 	 * haven't written it to LDAP yet, or because it's been
728 	 * removed), we only allow object deletion if we're the
729 	 * master for it.
730 	 */
731 
732 	nod = (nisdb_obj_del_t *)am(myself, sizeof (*nod));
733 	if (nod == 0) {
734 		nis_destroy_object(o);
735 		return (DB_MEMORY_LIMIT);
736 	}
737 
738 	nod->objType = o->zo_data.zo_type;
739 	nis_destroy_object(o);
740 
741 	nod->objName = sdup(myself, T, objName);
742 	if (nod->objName == 0) {
743 		sfree(nod);
744 		return (DB_MEMORY_LIMIT);
745 	}
746 
747 	/* Check for a dup */
748 	for (tmp = tsd->objDelList; tmp != 0;
749 			tmp = (nisdb_obj_del_t *)tmp->next) {
750 		if (strcmp(nod->objName, tmp->objName) == 0) {
751 			sfree(nod->objName);
752 			sfree(nod);
753 			return (DB_SUCCESS);
754 		}
755 	}
756 
757 	/* Insert at start of list */
758 	nod->next = tsd->objDelList;
759 	tsd->objDelList = nod;
760 
761 	return (DB_SUCCESS);
762 }
763 
764 /*
765  * Touch (i.e., update the expiration time for) the specified object.
766  */
767 db_status
768 dbTouchObj(char *objName) {
769 	char		*ent, *table;
770 	db		*dbase;
771 	db_table_desc	*tbl = 0;
772 	db_mindex	*mindex;
773 	nis_attr	attr;
774 	db_query	*query;
775 	db_status	stat;
776 	const char	*myself = "dbTouchObj";
777 
778 	table = internalTableName(objName);
779 	if (table == 0)
780 		return (DB_BADQUERY);
781 
782 	if (strcmp(ROOTDIRFILE, table) == 0) {
783 		sfree(table);
784 
785 		if (touchRootDir() == 0)
786 			return (DB_SUCCESS);
787 		else
788 			return (DB_INTERNAL_ERROR);
789 	}
790 
791 	sfree(table);
792 	table = 0;
793 	ent = entryName(myself, objName, &table);
794 	if (ent == 0 || table == 0) {
795 		sfree(ent);
796 		return (DB_MEMORY_LIMIT);
797 	}
798 
799 	dbase = InUseDictionary->find_table(table, &tbl, TRUE);
800 	if (dbase != 0)
801 		mindex = dbase->mindex();
802 	if (dbase == 0 || tbl == 0 || mindex == 0) {
803 		sfree(ent);
804 		sfree(table);
805 		return (DB_BADTABLE);
806 	}
807 
808 	attr.zattr_ndx = (char *)"name";
809 	attr.zattr_val.zattr_val_val = ent;
810 	attr.zattr_val.zattr_val_len = slen(ent) + 1;
811 
812 	query = InUseDictionary->translate_to_query(tbl, 1, &attr);
813 	if (query == 0) {
814 		sfree(ent);
815 		sfree(table);
816 		return (DB_BADQUERY);
817 	}
818 
819 	mindex->touchEntry(query);
820 
821 	sfree(ent);
822 	sfree(table);
823 	delete query;
824 
825 	return (DB_SUCCESS);
826 }
827 
828 /*
829  * Create a NIS_TABLE_OBJ.
830  * Borrows heavily from rpc.nisd/nis_db.c:__create_table().
831  */
832 db_status
833 dbCreateTable(char *intName, nis_object *obj) {
834 	table_col	tc[NIS_MAXCOLUMNS+1];
835 	table_obj	tobj, *t;
836 	int		i;
837 	const char	*myself = "dbCreateTable";
838 
839 	if (intName == 0 || obj == 0)
840 		return (DB_BADTABLE);
841 
842 	t = &(obj->TA_data);
843 
844 	/* Make sure there are searchable columns */
845 	for (i = 0; i < t->ta_cols.ta_cols_len; i++) {
846 		if (t->ta_cols.ta_cols_val[i].tc_flags & TA_SEARCHABLE)
847 			break;
848 	}
849 	if (i >= t->ta_cols.ta_cols_len) {
850 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
851 			"%s: No searchable columns in \"%s\" (\"%s\")",
852 			myself, NIL(obj->zo_name), NIL(intName));
853 		return (DB_BADTABLE);
854 	}
855 
856 	tobj = *t;
857 	/* Shift columns one step right */
858 	for (i = 0; i < tobj.ta_cols.ta_cols_len; i++) {
859 		tc[i+1] = tobj.ta_cols.ta_cols_val[i];
860 	}
861 	tc[0].tc_name = 0;
862 	tc[0].tc_flags = TA_XDR | TA_BINARY;
863 	tc[0].tc_rights = 0;
864 	tobj.ta_cols.ta_cols_len += 1;
865 	tobj.ta_cols.ta_cols_val = tc;
866 
867 	return (db_create_table(intName, &tobj));
868 }
869 
870 #define	TABLE_COL(o, n)	o->TA_data.ta_cols.ta_cols_val[n]
871 
872 /*
873  * Refresh (if necessary, create), the specified object in the local DB.
874  */
875 db_status
876 dbRefreshObj(char *name, nis_object *o) {
877 	char		*objName;
878 	__nis_buffer_t	b = {0, 0};
879 	nis_object	*curObj;
880 	db_status	stat;
881 	char		*ent, *table, *objTable;
882 	int		rstat, isDir = 0, isTable = 0;
883 	const char	*myself = "refreshObj";
884 
885 	if (o == 0)
886 		/* Delete it */
887 		return (dbDeleteObj(name));
888 
889 	/* We don't work on entry objects */
890 	if (o->zo_data.zo_type == NIS_ENTRY_OBJ)
891 		return (DB_BADOBJECT);
892 
893 	if (name != 0)
894 		objName = name;
895 	else {
896 		bp2buf(myself, &b, "%s.%s", NIL(o->zo_name), NIL(o->zo_domain));
897 		objName = b.buf;
898 	}
899 
900 	curObj = dbFindObject(objName, &stat);
901 	if (curObj == 0 && stat != DB_NOTFOUND) {
902 		sfree(b.buf);
903 		return (stat);
904 	}
905 
906 	/*
907 	 * If the object doesn't change, just touch it to update the
908 	 * expiration time.
909 	 */
910 	if (curObj != 0) {
911 		if (sameNisPlusObj(o, curObj)) {
912 			sfree(b.buf);
913 			nis_destroy_object(curObj);
914 			return (dbTouchObj(objName));
915 		}
916 
917 		/* Otherwise, check that the name and type is the same */
918 		if (o->zo_data.zo_type != curObj->zo_data.zo_type ||
919 			o->zo_name == 0 || curObj->zo_name == 0 ||
920 			o->zo_domain == 0 || curObj->zo_domain == 0 ||
921 			strcmp(o->zo_name, curObj->zo_name) != 0 ||
922 			strcmp(o->zo_domain, curObj->zo_domain) != 0) {
923 			sfree(b.buf);
924 			nis_destroy_object(curObj);
925 			return (DB_BADOBJECT);
926 		}
927 
928 		/*
929 		 * If the object is a table, we can't allow the scheme
930 		 * to change.
931 		 */
932 		if (o->zo_data.zo_type == NIS_TABLE_OBJ) {
933 			int	i;
934 
935 			if (o->TA_data.ta_maxcol !=
936 					curObj->TA_data.ta_maxcol) {
937 				sfree(b.buf);
938 				nis_destroy_object(curObj);
939 				return (DB_BADOBJECT);
940 			}
941 
942 			for (i = 0; i < o->TA_data.ta_maxcol; i++) {
943 				if ((TABLE_COL(o, i).tc_flags &
944 						TA_SEARCHABLE) !=
945 					(TABLE_COL(curObj, i).tc_flags &
946 						TA_SEARCHABLE)) {
947 					sfree(b.buf);
948 					nis_destroy_object(curObj);
949 					return (DB_BADOBJECT);
950 				}
951 			}
952 		}
953 	} else {
954 		/*
955 		 * If we're creating a directory object, make a note
956 		 * so that we can add it to the serving list and create
957 		 * the disk file. Similarly, if creating a table, we
958 		 * also need to create the disk file.
959 		 */
960 		if (o->zo_data.zo_type == NIS_DIRECTORY_OBJ)
961 			isDir = 1;
962 		else if (o->zo_data.zo_type == NIS_TABLE_OBJ)
963 			isTable = 1;
964 	}
965 
966 	objTable = internalTableName(objName);
967 	if (objTable == 0) {
968 		sfree(b.buf);
969 		if (curObj != 0)
970 			nis_destroy_object(curObj);
971 		return (DB_BADQUERY);
972 	}
973 
974 	if (strcmp(ROOTDIRFILE, objTable) == 0) {
975 		sfree(objTable);
976 
977 		rstat = update_root_object((nis_name)ROOTOBJFILE, o);
978 		if (rstat == 1)
979 			stat = DB_SUCCESS;
980 		else
981 			stat = DB_INTERNAL_ERROR;
982 	} else {
983 		nis_attr	attr;
984 		entry_object	*e, eo;
985 		entry_col	ec[2];
986 		db		*dbase;
987 		db_table_desc	*tbl = 0;
988 		db_mindex	*mindex;
989 		db_result	*dbres;
990 		int		lstat;
991 
992 		/* Find parent */
993 		ent = entryName(myself, objName, &table);
994 		if (ent == 0 || table == 0) {
995 			sfree(b.buf);
996 			sfree(objTable);
997 			sfree(ent);
998 			if (curObj != 0)
999 				nis_destroy_object(curObj);
1000 			return (DB_MEMORY_LIMIT);
1001 		}
1002 
1003 		/*
1004 		 * Calling vanilla find_table() here (which might go to
1005 		 * LDAP and recurse back to ourselves) so that it should
1006 		 * work to create a hierarchy of directories.
1007 		 */
1008 		dbase = InUseDictionary->find_table(table, &tbl, TRUE);
1009 		if (dbase != 0)
1010 			mindex = dbase->mindex();
1011 		if (dbase == 0 || tbl == 0 || mindex == 0) {
1012 			sfree(b.buf);
1013 			sfree(objTable);
1014 			sfree(ent);
1015 			sfree(table);
1016 			if (curObj != 0)
1017 				nis_destroy_object(curObj);
1018 			return (DB_BADTABLE);
1019 		}
1020 
1021 		/* Construct suitable nis_attr and entry_object */
1022 		attr.zattr_ndx = (char *)"name";
1023 		attr.zattr_val.zattr_val_val = ent;
1024 		attr.zattr_val.zattr_val_len = slen(ent) + 1;
1025 
1026 		ec[1].ec_flags = 0;
1027 		ec[1].ec_value.ec_value_val = ent;
1028 		ec[1].ec_value.ec_value_len = attr.zattr_val.zattr_val_len;
1029 
1030 		eo.en_type = (char *)"IN_DIRECTORY";
1031 		eo.en_cols.en_cols_val = ec;
1032 		eo.en_cols.en_cols_len = 2;
1033 
1034 		e = makePseudoEntryObj(o, &eo, 0);
1035 		if (e == 0) {
1036 			sfree(objTable);
1037 			sfree(table);
1038 			sfree(ent);
1039 			if (curObj != 0)
1040 				nis_destroy_object(curObj);
1041 			return (DB_INTERNAL_ERROR);
1042 		}
1043 
1044 		/* Only want to update the local DB */
1045 
1046 		WRITELOCKNR(mindex, lstat, "mindex w dbRefreshObj");
1047 		if (lstat != 0) {
1048 			sfree(objTable);
1049 			sfree(table);
1050 			sfree(ent);
1051 			if (curObj != 0)
1052 				nis_destroy_object(curObj);
1053 			return (DB_LOCK_ERROR);
1054 		}
1055 		mindex->setNoWriteThrough();
1056 		mindex->setNoLDAPquery();
1057 
1058 		dbres = db_add_entry_x(table, 1, &attr, e, 0, 0);
1059 
1060 		mindex->clearNoLDAPquery();
1061 		mindex->clearNoWriteThrough();
1062 		WRITEUNLOCKNR(mindex, lstat, "mindex wu dbRefreshObj");
1063 		if (lstat != 0) {
1064 			sfree(objTable);
1065 			sfree(table);
1066 			sfree(ent);
1067 			if (curObj != 0)
1068 				nis_destroy_object(curObj);
1069 			db_free_result(dbres);
1070 			return (DB_LOCK_ERROR);
1071 		}
1072 
1073 		sfree(ent);
1074 		sfree(table);
1075 
1076 		if (dbres == 0)
1077 			stat = DB_MEMORY_LIMIT;
1078 		else
1079 			stat = dbres->status;
1080 
1081 		db_free_result(dbres);
1082 
1083 		/*
1084 		 * If successful so far, add the transaction.
1085 		 */
1086 		if (stat == DB_SUCCESS) {
1087 			int		xid, st;
1088 			db_status	ds;
1089 			nis_object	*dirObj;
1090 
1091 			/* Find the directory where this is added */
1092 			dirObj = dbFindObject(o->zo_domain, &ds);
1093 			if (dirObj == 0) {
1094 				sfree(objTable);
1095 				if (curObj != 0)
1096 					nis_destroy_object(curObj);
1097 				return (ds);
1098 			}
1099 
1100 			xid = beginTransaction();
1101 			if (xid == 0) {
1102 				sfree(objTable);
1103 				if (curObj != 0)
1104 					nis_destroy_object(curObj);
1105 				nis_destroy_object(dirObj);
1106 				return (DB_INTERNAL_ERROR);
1107 			}
1108 
1109 			st = addUpdate((curObj == 0) ? ADD_NAME : MOD_NAME_NEW,
1110 					objName, 0, 0, o, curObj, 0);
1111 			if (st != 0) {
1112 				(void) abort_transaction(xid);
1113 				sfree(objTable);
1114 				if (curObj != 0)
1115 					nis_destroy_object(curObj);
1116 				nis_destroy_object(dirObj);
1117 				return (DB_INTERNAL_ERROR);
1118 			}
1119 
1120 			st = endTransaction(xid, dirObj);
1121 			if (st != 0)
1122 				stat = DB_INTERNAL_ERROR;
1123 
1124 			if (curObj != 0)
1125 				nis_destroy_object(curObj);
1126 			nis_destroy_object(dirObj);
1127 		}
1128 
1129 		/*
1130 		 * If it's a table or directory, create the DB file.
1131 		 * If a directory, also add it to the serving list.
1132 		 */
1133 		if (stat == DB_SUCCESS &&(isDir || isTable)) {
1134 			if (isDir) {
1135 				stat = db_create_table(objTable,
1136 							&tbl_prototype);
1137 			} else {
1138 				stat = dbCreateTable(objTable, o);
1139 			}
1140 		}
1141 		sfree(objTable);
1142 	}
1143 
1144 	sfree(b.buf);
1145 
1146 	return (stat);
1147 }
1148 
1149 /*
1150  * Replace the object stored with the mapping 't'. Return TRUE if
1151  * at least one object was replaced, FALSE otherwise.
1152  */
1153 bool_t
1154 replaceMappingObj(__nis_table_mapping_t *t, nis_object *n) {
1155 	__nis_table_mapping_t	*x;
1156 	nis_object		*old = 0;
1157 	int			assigned = 0;
1158 
1159 	/*
1160 	 * The alternate mappings are usually mostly copies
1161 	 * of the original, so we try to make sure that we
1162 	 * don't free the same nis_object twice.
1163 	 */
1164 	for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) {
1165 		if (old == 0) {
1166 			old = x->obj;
1167 			if (x->obj != 0)
1168 				nis_destroy_object(x->obj);
1169 		} else {
1170 			if (x->obj != old && x->obj != 0)
1171 				nis_destroy_object(x->obj);
1172 		}
1173 		x->obj = n;
1174 		assigned++;
1175 	}
1176 
1177 	return (assigned > 0);
1178 }
1179 
1180 /*
1181  * Set object type, column info, and obj for the specified
1182  * mapping 't' from the object 'o'. Returns zero if 'o' was unused,
1183  * and should be freed by the caller, larger than zero otherwise.
1184  */
1185 int
1186 setMappingObjTypeEtc(__nis_table_mapping_t *t, nis_object *o) {
1187 	__nis_table_mapping_t	*x;
1188 	int			ls, ret;
1189 	int	                i;
1190 
1191 	if (t == 0 || o == 0)
1192 		return (0);
1193 
1194 	t->objType = o->zo_data.zo_type;
1195 	for (x = t; x != 0; x = (__nis_table_mapping_t *)x->next) {
1196 		if (x != t) {
1197 			x->objType = t->objType;
1198 		}
1199 		if (x->objType == NIS_TABLE_OBJ) {
1200 			/*
1201 			 * If we have rules, this mapping is for table entries,
1202 			 * and we need the column names. Otherwise, remove the
1203 			 * column names (if any).
1204 			 */
1205 
1206                         for (i = 0; i < x->numColumns; i++)
1207 			sfree(x->column[i]);
1208 		        sfree(x->column);
1209 			x->column = 0;
1210 			x->numColumns = 0;
1211 		}
1212 	}
1213 	ret = replaceMappingObj(t, o);
1214 
1215 	return (ret);
1216 }
1217 
1218 /*
1219  * Retrieve the specified object (internal DB name) from LDAP, and
1220  * refresh/create as appropriate.
1221  */
1222 db_status
1223 dbCreateFromLDAP(char *intName, int *ldapStat) {
1224 	__nis_table_mapping_t	*t;
1225 	int			lstat, doDestroy;
1226 	nis_object		*obj = 0;
1227 	db_status		dstat;
1228 	const char		*myself = "dbCreateFromLDAP";
1229 
1230 	if (!useLDAPrespository) {
1231 		if (ldapStat != 0)
1232 			*ldapStat = LDAP_SUCCESS;
1233 		return (DB_SUCCESS);
1234 	}
1235 
1236 	t = (__nis_table_mapping_t *)__nis_find_item_mt(intName,
1237 							&ldapMappingList,
1238 							0, 0);
1239 
1240 	/* No mapping isn't a failure */
1241 	if (t == 0) {
1242 		if (ldapStat != 0)
1243 			*ldapStat = LDAP_SUCCESS;
1244 		return (DB_NOTFOUND);
1245 	}
1246 
1247 	lstat = objFromLDAP(t, &obj, 0, 0);
1248 	if (ldapStat != 0)
1249 		*ldapStat = lstat;
1250 	if (lstat != LDAP_SUCCESS)
1251 		return (DB_NOTFOUND);
1252 
1253 	/*
1254 	 * If the LDAP operation was successful, but 'obj' is NULL,
1255 	 * there's no mapping for this object, and we're done.
1256 	 */
1257 	if (obj == 0)
1258 		return (DB_SUCCESS);
1259 
1260 	/* Update the mapping with object info */
1261 	doDestroy = setMappingObjTypeEtc(t, obj) == 0;
1262 
1263 	dstat = dbRefreshObj(t->objName, obj);
1264 
1265 	if (doDestroy)
1266 		nis_destroy_object(obj);
1267 
1268 	return (dstat);
1269 }
1270 
1271 /*
1272  * Up- (fromLDAP==0) or down- (fromLDAP==1) load all LDAP mapped data.
1273  * Returns an LDAP error status.
1274  */
1275 int
1276 loadAllLDAP(int fromLDAP, void *cookie, db_status *dstatP) {
1277 	__nis_table_mapping_t	*t, *start;
1278 	int			stat = LDAP_SUCCESS;
1279 	db_status		dstat = DB_SUCCESS;
1280 	db			*dbase;
1281 	db_table_desc		*tbl = 0;
1282 	db_mindex		*mindex;
1283 	const char		*myself = "loadAllLDAP";
1284 
1285 	/*
1286 	 * If the 'cookie' and '*cookie' are non-NULL, start scanning
1287 	 * the mappings from '*cookie'. When we return with an error,
1288 	 * we set '*cookie' to point to the mapping being processed.
1289 	 * This enables our caller to react appropriately, and retry
1290 	 * if desired.
1291 	 *
1292 	 * The cookie is opaque to our caller, who's only allowed to
1293 	 * initialize *cookie to NULL.
1294 	 */
1295 	if (cookie != 0) {
1296 		start = *((__nis_table_mapping_t **)cookie);
1297 		if (start == 0)
1298 			start = ldapMappingSeq;
1299 	} else {
1300 		start = ldapMappingSeq;
1301 	}
1302 
1303 	for (t = start; t != 0; t = (__nis_table_mapping_t *)t->seqNext) {
1304 		__nis_table_mapping_t	**tp;
1305 		int			nm;
1306 
1307 		if (fromLDAP) {
1308 			/* Are there any mappings for the object proper ? */
1309 			tp = selectTableMapping(t, 0, 0, 1, t->dbId, &nm);
1310 			if (tp != 0 && nm > 0) {
1311 				dstat = dbCreateFromLDAP(t->objPath, &stat);
1312 				if (dstat != DB_SUCCESS) {
1313 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1314 				"%s: DB error %d creating \"%s\": %s",
1315 						myself, dstat, NIL(t->objName),
1316 						ldap_err2string(stat));
1317 					if (cookie != 0)
1318 						*((__nis_table_mapping_t **)
1319 							cookie) = t;
1320 					if (dstatP != 0)
1321 						*dstatP = dstat;
1322 					else if (stat == LDAP_SUCCESS)
1323 						stat = LDAP_OPERATIONS_ERROR;
1324 					sfree(tp);
1325 					return (stat);
1326 				}
1327 			}
1328 			sfree(tp);
1329 
1330 			/* Any mappings for table entries ? */
1331 			tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm);
1332 			if (tp == 0 || nm <= 0) {
1333 				sfree(tp);
1334 				continue;
1335 			}
1336 			sfree(tp);
1337 
1338 			/*
1339 			 * The object itself must exist in the local
1340 			 * DB by now. Get the db_mindex and let
1341 			 * db_mindex::queryLDAP() do the work; if
1342 			 * the object isn't a table, queryLDAP()
1343 			 * will do nothing and return success.
1344 			 */
1345 			dbase = InUseDictionary->find_table(t->objPath,
1346 							&tbl, TRUE);
1347 			if (dbase != 0)
1348 				mindex = dbase->mindex();
1349 			if (dbase == 0 || tbl == 0 || mindex == 0) {
1350 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1351 				"%s: No local DB entry for \"%s\" (%s:%s)",
1352 					myself, NIL(t->objPath),
1353 					NIL(t->dbId), NIL(t->objName));
1354 				if (cookie != 0)
1355 					*((__nis_table_mapping_t **)cookie) =
1356 						t;
1357 				if (dstatP != 0)
1358 					*dstatP = DB_BADTABLE;
1359 				return ((dstatP != 0) ?
1360 					LDAP_SUCCESS : LDAP_OPERATIONS_ERROR);
1361 			}
1362 			mindex->setInitialLoad();
1363 			stat = mindex->queryLDAP(0, t->dbId, 0);
1364 			mindex->clearInitialLoad();
1365 			if (stat != LDAP_SUCCESS) {
1366 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1367 			"%s: LDAP error retrieving entries for %s:%s: %s",
1368 					myself, NIL(t->dbId), NIL(t->objName),
1369 					ldap_err2string(stat));
1370 				if (cookie != 0)
1371 					*((__nis_table_mapping_t **)cookie) =
1372 						t;
1373 				if (dstatP != 0)
1374 					*dstatP = DB_SUCCESS;
1375 				return (stat);
1376 			}
1377 		} else {
1378 			nis_object	*obj;
1379 			char		*ent, *objPath;
1380 			int		freeObjPath = 0;
1381 
1382 			/*
1383 			 * Up-loading to LDAP, so the object must
1384 			 * already exist in the local DB.
1385 			 */
1386 			obj = dbFindObject(t->objName, &dstat);
1387 			if (obj == 0) {
1388 				if (dstat == DB_NOTFOUND)
1389 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1390 		"%s: No local DB object for \"%s\" (%s:%s); skipping up-load",
1391 						myself, NIL(t->objPath),
1392 						NIL(t->dbId),
1393 						NIL(t->objName));
1394 				else
1395 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1396 			"%s: DB error %d for \"%s\" (%s:%s); skipping up-load",
1397 						myself, dstat,
1398 						NIL(t->objPath),
1399 						NIL(t->dbId),
1400 						NIL(t->objName));
1401 				continue;
1402 			}
1403 
1404 			/*
1405 			 * If it's a table or directory, there will be
1406 			 * a dictionary entry for the object itself.
1407 			 * Otherwise, we need the dictionary entry for
1408 			 * the parent directory.
1409 			 *
1410 			 * For a table, we need the db_mindex for both the
1411 			 * table object itself, as well as for the parent
1412 			 * directory (in order to store table entries).
1413 			 * We start with the latter.
1414 			 */
1415 			if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
1416 				objPath = t->objPath;
1417 				ent = 0;
1418 			} else {
1419 				objPath = 0;
1420 				ent = entryName(myself, t->objName,
1421 				    &objPath);
1422 				if (ent == 0 || objPath == 0) {
1423 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1424 	"%s: Error deriving entry/DB-table names for %s:%s; skipping up-load",
1425 						myself, NIL(t->dbId),
1426 						NIL(t->objName));
1427 					sfree(ent);
1428 					sfree(objPath);
1429 					nis_destroy_object(obj);
1430 					obj = 0;
1431 					continue;
1432 				}
1433 				freeObjPath = 1;
1434 			}
1435 
1436 			dbase = InUseDictionary->find_table(objPath,
1437 							&tbl, TRUE);
1438 			if (dbase != 0)
1439 				mindex = dbase->mindex();
1440 			if (dbase == 0 || tbl == 0 || mindex == 0) {
1441 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1442 		"%s: No local DB entry for \"%s\" (%s:%s); skipping up-load",
1443 					myself, objPath,
1444 					NIL(t->dbId), NIL(t->objName));
1445 				sfree(ent);
1446 				if (freeObjPath)
1447 					sfree(objPath);
1448 				nis_destroy_object(obj);
1449 				obj = 0;
1450 				continue;
1451 			}
1452 
1453 			/*
1454 			 * Our next action(s) depend on the object type:
1455 			 *
1456 			 *	directory	Store dir object
1457 			 *
1458 			 *	table		Store table obj, as well
1459 			 *			as any entries in the
1460 			 *			table
1461 			 *
1462 			 *	other		Store object; we need to
1463 			 *			build a db_query specifying
1464 			 *			the first-level name of the
1465 			 *			object.
1466 			 *
1467 			 * storeLDAP() will just do nothing and return
1468 			 * success if we try to, say, store a table object
1469 			 * when only the table entries are mapped. Hence,
1470 			 * we don't have to worry about those distinctions
1471 			 * here.
1472 			 */
1473 			if (obj->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
1474 				stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId);
1475 			} else {
1476 				nis_attr	attr;
1477 				db_query	*q;
1478 
1479 				attr.zattr_ndx = (char *)"name";
1480 				attr.zattr_val.zattr_val_val = ent;
1481 				attr.zattr_val.zattr_val_len = slen(ent) + 1;
1482 
1483 				q = new db_query(mindex->getScheme(), 1, &attr);
1484 				if (q == 0) {
1485 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1486 	"%s: error creating db_query for \"%s\" in \"%s\"; skipping up-load",
1487 						myself, ent, objPath);
1488 					sfree(ent);
1489 					if (freeObjPath)
1490 						sfree(objPath);
1491 					nis_destroy_object(obj);
1492 					obj = 0;
1493 					continue;
1494 				}
1495 
1496 				stat = mindex->storeLDAP(q, 0, obj, 0, t->dbId);
1497 
1498 				delete q;
1499 
1500 			}
1501 
1502 			sfree(ent);
1503 			if (freeObjPath)
1504 				sfree(objPath);
1505 
1506 			if (stat != LDAP_SUCCESS) {
1507 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1508 			"%s: Error storing %s:%s to LDAP: %s",
1509 					myself, NIL(t->dbId), NIL(t->objName),
1510 					ldap_err2string(stat));
1511 				nis_destroy_object(obj);
1512 				obj = 0;
1513 				if (cookie != 0)
1514 					*((__nis_table_mapping_t **)
1515 						cookie) = t;
1516 				if (dstatP != 0)
1517 					*dstatP = DB_SUCCESS;
1518 				return (stat);
1519 			}
1520 
1521 			/* Any mappings for table entries ? */
1522 			tp = selectTableMapping(t, 0, 0, 0, t->dbId, &nm);
1523 			if (tp == 0 || nm <= 0) {
1524 				sfree(tp);
1525 				nis_destroy_object(obj);
1526 				obj = 0;
1527 				continue;
1528 			}
1529 			sfree(tp);
1530 
1531 			/*
1532 			 * If it's a table, we also need to store the table
1533 			 * entries.
1534 			 */
1535 			if (obj->zo_data.zo_type == NIS_TABLE_OBJ) {
1536 				tbl = 0;
1537 				dbase = InUseDictionary->find_table(t->objPath,
1538 								&tbl, TRUE);
1539 				if (dbase != 0)
1540 				mindex = dbase->mindex();
1541 				if (dbase == 0 || tbl == 0 || mindex == 0) {
1542 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1543 	"%s: No local DB entry for \"%s\" (%s:%s); skipping entry up-load",
1544 						myself, NIL(t->objPath),
1545 						NIL(t->dbId), NIL(t->objName));
1546 					nis_destroy_object(obj);
1547 					obj = 0;
1548 					continue;
1549 				}
1550 
1551 				stat = mindex->storeLDAP(0, 0, obj, 0, t->dbId);
1552 
1553 				if (stat != LDAP_SUCCESS) {
1554 					logmsg(MSG_NOTIMECHECK, LOG_ERR,
1555 				"%s: Error storing %s:%s entries to LDAP: %s",
1556 						myself, NIL(t->dbId),
1557 						NIL(t->objName),
1558 						ldap_err2string(stat));
1559 					nis_destroy_object(obj);
1560 					obj = 0;
1561 					if (cookie != 0)
1562 						*((__nis_table_mapping_t **)
1563 							cookie) = t;
1564 					if (dstatP != 0)
1565 						*dstatP = DB_SUCCESS;
1566 					return (stat);
1567 				}
1568 			}
1569 			nis_destroy_object(obj);
1570 			obj = 0;
1571 		}
1572 	}
1573 
1574 	if (dstatP != 0)
1575 		*dstatP = dstat;
1576 	return (stat);
1577 }
1578 
1579 /*
1580  * Object identified by given attribute name is added to specified table.
1581  * If object already exists, it is replaced.  If more than one object
1582  * matches the given attribute name, DB_NOTUNIQUE is returned.
1583  */
1584 static
1585 db_result *
1586 db_add_entry_x(char * tab, int numattrs, nis_attr * attrname,
1587 		entry_obj * newobj, int skiplog, int nosync)
1588 {
1589 	db_result * safety = empty_result(DB_SUCCESS);
1590 	db_table_desc * tbl = NULL;
1591 	db * dbase = InUseDictionary->find_table(tab, &tbl, FALSE);
1592 
1593 	if (tbl == NULL || dbase == NULL) {
1594 		return (set_result(safety, DB_BADTABLE));
1595 	} else if (skiplog) {
1596 		db_result * res;
1597 		res = dbase->execute(DB_ADD_NOLOG, NULL,
1598 			    (entry_object *) newobj, NULL);
1599 		if (safety) delete safety;
1600 		return (res);
1601 	} else {
1602 		db_result *res;
1603 		db_query *
1604 		query = InUseDictionary->translate_to_query(tbl,
1605 						numattrs, attrname);
1606 		if (query == NULL)
1607 			return (set_result(safety, DB_BADQUERY));
1608 		if (nosync)
1609 			res = dbase->execute(DB_ADD_NOSYNC,
1610 				query, (entry_object *) newobj, NULL);
1611 		else
1612 			res = dbase->execute(DB_ADD, query,
1613 				(entry_object *) newobj, NULL);
1614 		delete query;
1615 		if (safety) delete safety;
1616 		return (res);
1617 	}
1618 }
1619 
1620 db_result *
1621 db_add_entry(char * tab, int numattrs, nis_attr * attrname,
1622 		entry_obj * newobj)
1623 {
1624 	return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 0));
1625 }
1626 
1627 db_result *
1628 __db_add_entry_nolog(char * tab, int numattrs, nis_attr * attrname,
1629 		entry_obj * newobj)
1630 {
1631 	return (db_add_entry_x(tab, numattrs, attrname, newobj, 1, 0));
1632 }
1633 
1634 db_result *
1635 __db_add_entry_nosync(char * tab, int numattrs, nis_attr * attrname,
1636 			entry_obj * newobj)
1637 {
1638 	return (db_add_entry_x(tab, numattrs, attrname, newobj, 0, 1));
1639 }
1640 
1641 /*
1642  * Remove object identified by given attributes from specified table.
1643  * If no attribute is supplied, all entries in table are removed.
1644  * If attributes identify more than one object, all objects are removed.
1645 */
1646 
1647 db_result *
1648 db_remove_entry_x(char * table_name, int num_attrs, nis_attr * attrname,
1649 			int nosync)
1650 {
1651 	db_result * safety = empty_result(DB_SUCCESS);
1652 	db_table_desc * tbl = NULL;
1653 	db * dbase = InUseDictionary->find_table(table_name, &tbl, FALSE);
1654 	db_result * res;
1655 
1656 	if (tbl == NULL || dbase == NULL)
1657 		return (set_result(safety, DB_BADTABLE));
1658 	else {
1659 		if (num_attrs != 0) {
1660 			db_query *query;
1661 			query = InUseDictionary->translate_to_query(tbl,
1662 					num_attrs, attrname);
1663 			if (query == NULL)
1664 				return (set_result(safety,
1665 						DB_BADQUERY));
1666 			if (nosync)
1667 				res = dbase->execute(DB_REMOVE_NOSYNC,
1668 						query, NULL, NULL);
1669 			else
1670 				res = dbase->execute(DB_REMOVE, query,
1671 						NULL, NULL);
1672 			delete query;
1673 		} else {
1674 			if (nosync)
1675 				res = dbase->execute(DB_REMOVE_NOSYNC,
1676 					NULL, NULL, NULL);
1677 			else
1678 				res = dbase->execute(DB_REMOVE,
1679 					NULL, NULL, NULL);
1680 		}
1681 		if (safety) delete safety;
1682 		return (res);
1683 	}
1684 }
1685 
1686 db_result *
1687 db_remove_entry(char * table_name, int num_attrs, nis_attr * attrname)
1688 {
1689 	return (db_remove_entry_x(table_name, num_attrs, attrname, 0));
1690 }
1691 
1692 db_result *
1693 __db_remove_entry_nosync(char * table_name, int num_attrs, nis_attr * attrname)
1694 {
1695 	return (db_remove_entry_x(table_name, num_attrs, attrname, 1));
1696 }
1697 
1698 /* Return a copy of the version of specified table. */
1699 vers *
1700 db_version(char * table_name)
1701 {
1702 	db * dbase = InUseDictionary->find_table(table_name);
1703 
1704 	if (dbase == NULL)
1705 		return (NULL);
1706 	vers* v = new vers(dbase->get_version());
1707 	if (v == NULL)
1708 		WARNING("nis_db::db_version: cannot allocate space");
1709 	return (v);
1710 }
1711 
1712 /* Return log entries since (later than) given version 'v' of table. */
1713 db_log_list *
1714 db_log_entries_since(char * table_name, vers * v)
1715 {
1716 	db * dbase = InUseDictionary->find_table(table_name);
1717 
1718 	if (dbase == NULL)
1719 		return (NULL);
1720 	return (dbase->get_log_entries_since(v));
1721 }
1722 
1723 db_status
1724 db_sync_log(char *table_name) {
1725 
1726 	db * dbase = InUseDictionary->find_table(table_name);
1727 
1728 	if (dbase == NULL)
1729 		return (DB_BADTABLE);
1730 	return (dbase->sync_log());
1731 }
1732 
1733 /*
1734  * Apply the given update specified in 'entry' to the specified table.
1735  * Returns DB_SUCCESS if update was executed.
1736  * Returns DB_NOTFOUND if update occurs too early to be applied.
1737 */
1738 db_status
1739 db_apply_log_entry(char * table_name, db_log_entry * entry)
1740 {
1741 	db * dbase = InUseDictionary->find_table(table_name, NULL, FALSE);
1742 
1743 	if (dbase == NULL)
1744 		return (DB_BADTABLE);
1745 	if (dbase->execute_log_entry(entry))
1746 		return (DB_SUCCESS);   /* got executed */
1747 	else
1748 		return (DB_NOTFOUND);  /* not executed */
1749 }
1750 
1751 /*
1752  * Checkpoint specified table (i.e. incorporate logged updates to main
1753  * database file).  If table_name is NULL, checkpoint all tables that
1754  * needs it.
1755 */
1756 db_status
1757 db_checkpoint(char * table_name)
1758 {
1759 	return (InUseDictionary->db_checkpoint(table_name));
1760 }
1761 
1762 /* Print names of tables in system. */
1763 void
1764 db_print_table_names()
1765 {
1766 	int i;
1767 	db_table_names * answer = InUseDictionary->get_table_names();
1768 
1769 	if (answer != NULL) {
1770 		for (i = 0; i < answer->db_table_names_len; i++) {
1771 			printf("%s\n", answer->db_table_names_val[i]);
1772 			delete answer->db_table_names_val[i];
1773 		}
1774 		delete answer->db_table_names_val;
1775 		delete answer;
1776 	}
1777 }
1778 
1779 /* Print statistics of specified table to stdout. */
1780 db_status
1781 db_stats(char * table_name)
1782 {
1783 	db_table_desc * tbl = NULL;
1784 	db *dbase = InUseDictionary->find_table(table_name, &tbl);
1785 
1786 	if (tbl == NULL || dbase == NULL || tbl->scheme == NULL)
1787 		return (DB_BADTABLE);
1788 
1789 	dbase->print();
1790 	tbl->scheme->print();
1791 	return (DB_SUCCESS);
1792 }
1793 
1794 
1795 /* Print statistics of indices of specified table to stdout. */
1796 db_status
1797 db_print_all_indices(char * table_name)
1798 {
1799 	db * dbase = InUseDictionary->find_table(table_name);
1800 
1801 	if (dbase == NULL)
1802 		return (DB_BADTABLE);
1803 	dbase->print_all_indices();
1804 	return (DB_SUCCESS);
1805 }
1806 
1807 /* Print specified index of table to stdout. */
1808 db_status
1809 db_print_index(char * table_name, int which)
1810 {
1811 	db * dbase = InUseDictionary->find_table(table_name);
1812 
1813 	if (dbase == NULL)
1814 		return (DB_BADTABLE);
1815 	dbase->print_index(which);
1816 	return (DB_SUCCESS);
1817 }
1818 
1819 /* close open files */
1820 db_status
1821 db_standby(char * table_name)
1822 {
1823 	return (InUseDictionary->db_standby(table_name));
1824 }
1825 
1826 /* Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist. */
1827 db_status
1828 db_table_exists(char * table_name)
1829 {
1830 	db_table_desc *dbtab = InUseDictionary->find_table_desc(table_name);
1831 
1832 	if (dbtab == NULL)
1833 		return (DB_BADTABLE);
1834 	return (DB_SUCCESS);
1835 }
1836 
1837 /*
1838  * Returns DB_SUCCESS if table exists; DB_BADTABLE if table does not exist.
1839  *  If table already loaded, unload it.
1840 */
1841 db_status
1842 db_unload_table(char * table_name)
1843 {
1844 	db_table_desc *
1845 	dbtab = InUseDictionary->find_table_desc(table_name);
1846 	if (dbtab == NULL)
1847 		return (DB_BADTABLE);
1848 	// unload
1849 	if (dbtab->database != NULL) {
1850 		delete dbtab->database;
1851 		dbtab->database = NULL;
1852 	}
1853 	return (DB_SUCCESS);
1854 }
1855 
1856 /*
1857  * Put the specified table in deferred mode, which means that updates go
1858  * to the original table, but reads are satisfied out of a copy (which we
1859  * make here). Thus, "defer" refers to the table as seen by read requests,
1860  * since for them, changes are deferred.
1861  */
1862 db_status
1863 __db_defer(char *table_name) {
1864 	db_status	stat;
1865 
1866 	stat = InUseDictionary->defer(table_name);
1867 	return (stat);
1868 }
1869 
1870 /*
1871  * Commit deferred changes for the specified table. I.e., make visible
1872  * any updates made since the table was deferred.
1873  */
1874 db_status
1875 __db_commit(char *table_name) {
1876 	db_status	stat;
1877 
1878 	stat = InUseDictionary->commit(table_name);
1879 	return (stat);
1880 }
1881 
1882 /*
1883  * Rollback, i.e., return to the state before we entered deferred mode.
1884  */
1885 db_status
1886 __db_rollback(char *table_name) {
1887 	db_status	stat;
1888 
1889 	stat = InUseDictionary->rollback(table_name);
1890 	return (stat);
1891 }
1892 
1893 db_status
1894 __db_configure(char *table_name) {
1895 	db_status	stat;
1896 	char		tablePath[MAXPATHLEN + NIS_MAXNAMELEN + 1];
1897 	db		*dbase = InUseDictionary->find_table(table_name, NULL);
1898 
1899 	if (dbase == NULL || table_name == 0)
1900 		return (DB_BADTABLE);
1901 
1902 	if (strlen(table_name) >= sizeof (tablePath))
1903 		return (DB_BADQUERY);
1904 
1905 	if (internal_table_name(table_name, tablePath) == 0)
1906 		return (DB_STORAGE_LIMIT);
1907 
1908 	if (dbase->configure(tablePath))
1909 		stat = DB_SUCCESS;
1910 	else
1911 		stat = DB_INTERNAL_ERROR;
1912 
1913 	return (stat);
1914 }
1915 
1916 /*
1917  * During some rpc.nisd operations (such as when recovering the trans.log),
1918  * we don't want to use the LDAP repository, so we provide a main switch.
1919  * Note that we expect this to be used only when rpc.nisd is single-threaded,
1920  * so there is no need for synchronization when reading or modifying the
1921  * value of the main switch.
1922  */
1923 int	useLDAPrespository = 1;
1924 
1925 void
1926 __db_disallowLDAP(void) {
1927 	useLDAPrespository = 0;
1928 }
1929 
1930 void
1931 __db_allowLDAP(void) {
1932 	useLDAPrespository = 1;
1933 }
1934 
1935 }  /* extern "C" */
1936