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