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