xref: /titanic_51/usr/src/lib/libnisdb/db_mindex2.cc (revision df14233e629298598736976c5bfcf4a31873745f)
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  *	Copyright (c) 2001 by Sun Microsystems, Inc.
24  *	All Rights Reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 
31 #include <malloc.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <sys/param.h>
35 #include <sys/types.h>
36 #include <time.h>
37 #include "db_headers.h"
38 #include "db.h"
39 #include "db_mindex.h"
40 #include "db_pickle.h"
41 #include "nisdb_mt.h"
42 #include "nisdb_ldap.h"
43 #include "ldap_nisdbquery.h"
44 #include "ldap_map.h"
45 #include "ldap_ruleval.h"
46 #include "ldap_scheme.h"
47 #include "ldap_parse.h"
48 #include "nis_hashitem.h"
49 #include "nis_servlist.h"
50 #include "ldap_nisplus.h"
51 #include "nis_db.h"
52 #include "ldap_glob.h"
53 
54 /* Pass through configuration information to the table */
55 bool_t
56 db_mindex::configure(char *tablePath) {
57 	if (tablePath == NULL)
58 		return (FALSE);
59 
60 	if (objPath.ptr != 0)
61 		free(objPath.ptr);
62 	objPath.ptr = strdup(tablePath);
63 
64 	if (table != NULL) {
65 		return (table->configure(tablePath));
66 	} else {
67 		/* Defer table config until we have a table instance */
68 		return (objPath.ptr != NULL);
69 	}
70 }
71 
72 /*
73  * The noWriteThrough flag is used to prevent modifies/updates to LDAP
74  * while we're incorporating log data into the in-memory tables.
75  */
76 void
77 db_mindex::setNoWriteThrough(void) {
78 	ASSERTWHELD(this->mindex);
79 	noWriteThrough.flag++;
80 }
81 
82 void
83 db_mindex::clearNoWriteThrough(void) {
84 	ASSERTWHELD(this->mindex);
85 	if (noWriteThrough.flag > 0)
86 		noWriteThrough.flag--;
87 #ifdef	NISDB_LDAP_DEBUG
88 	else
89 		abort();
90 #endif	/* NISDB_LDAP_DEBUG */
91 }
92 
93 /*
94  * The noLDAPquery flag is used to prevent recursive LDAP queries when
95  * satisfy_query() is re-entered as we add an entry from queryLDAP().
96  */
97 void
98 db_mindex::setNoLDAPquery(void) {
99 	ASSERTWHELD(this->mindex);
100 	noLDAPquery.flag++;
101 }
102 
103 void
104 db_mindex::clearNoLDAPquery(void) {
105 	ASSERTWHELD(this->mindex);
106 	if (noLDAPquery.flag > 0)
107 		noLDAPquery.flag--;
108 #ifdef	NISDB_LDAP_DEBUG
109 	else
110 		abort();
111 #endif	/* NISDB_LDAP_DEBUG */
112 }
113 
114 /*
115  * The initialLoad flag tells us if an add or remove is done as part of
116  * the initial load of data, in which case we should use the initial TTLs.
117  */
118 void
119 db_mindex::setInitialLoad(void) {
120 	ASSERTWHELD(this->mindex);
121 	initialLoad.flag++;
122 }
123 
124 void
125 db_mindex::clearInitialLoad(void) {
126 	ASSERTWHELD(this->mindex);
127 	if (initialLoad.flag > 0)
128 		initialLoad.flag--;
129 #ifdef	NISDB_LDAP_DEBUG
130 	else
131 		abort();
132 #endif	/* NISDB_LDAP_DEBUG */
133 }
134 
135 void
136 db_mindex::setDbPtr(void *ptr) {
137 	dbptr.ptr = ptr;
138 }
139 
140 void *
141 db_mindex::getDbPtr(void) {
142 	return (dbptr.ptr);
143 }
144 
145 db_table *
146 db_mindex::getTable(void) {
147 	return (table);
148 }
149 
150 extern void	db_free_result(db_result *);
151 
152 zotypes
153 updateMappingObj(__nis_table_mapping_t *t, char **objNameP,
154 		bool_t *isMasterP) {
155 	zotypes	type = NIS_BOGUS_OBJ;
156 	char	*objName = 0;
157 	char	*myself = "updateMappingObj";
158 
159 	if (t != 0)
160 		objName = t->objName;
161 	else if (objNameP != 0)
162 		objName = *objNameP;
163 	else
164 		return (NIS_BOGUS_OBJ);
165 
166 	if (objName != 0) {
167 		db_status	stat;
168 		int		lstat = LDAP_SUCCESS;
169 		nis_object	*o = dbFindObject(objName, &stat);
170 
171 		/* If not found in the local DB, try LDAP */
172 		if (o == 0) {
173 			if (stat != DB_NOTFOUND) {
174 				logmsg(MSG_NOTIMECHECK, LOG_INFO,
175 					"%s: DB err %d for \"%s\"",
176 					myself, stat, NIL(objName));
177 			}
178 			o = ldapFindObj(t, objName, &lstat);
179 			/* If found, refresh/create the local copy */
180 			if (o != 0) {
181 				db_status	rstat;
182 				rstat = dbRefreshObj(objName, o);
183 				if (rstat != DB_SUCCESS)
184 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
185 					"%s: DB error %d refreshing \"%s\"",
186 					myself, rstat, NIL(objName));
187 			}
188 		}
189 
190 		if (o != 0) {
191 			type = o->zo_data.zo_type;
192 			if (objNameP != 0) {
193 				*objNameP = sdup(myself, T, objName);
194 				if (*objNameP == 0) {
195 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
196 				"%s: Unable to copy object name (\"%s\")",
197 						myself, NIL(objName));
198 				}
199 			}
200 			if (t != 0) {
201 				if (!setMappingObjTypeEtc(t, o))
202 					nis_destroy_object(o);
203 
204 				if (isMasterP != 0)
205 					*isMasterP = t->isMaster;
206 			} else {
207 				if (isMasterP != 0)
208 					*isMasterP = isMaster(o);
209 				nis_destroy_object(o);
210 			}
211 		} else if (lstat != LDAP_SUCCESS) {
212 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
213 				"%s: LDAP err %d for \"%s\"",
214 				myself, lstat, NIL(objName));
215 		}
216 	}
217 
218 	return (type);
219 }
220 
221 static __nis_table_mapping_t *
222 mappingFromObj(nis_object *obj, int *statP) {
223 	__nis_table_mapping_t	*t;
224 	__nis_buffer_t		b = {0, 0};
225 	char			*objPath;
226 	char			*myself = "mappingFromObj";
227 
228 	if (obj == 0 || obj->zo_data.zo_type == NIS_ENTRY_OBJ)
229 		return (0);
230 
231 	/*
232 	 * Convert full object name to the db table path used as
233 	 * key for the mapping hash list.
234 	 */
235 	bp2buf(myself, &b, "%s.%s",
236 		NIL(obj->zo_name), NIL(obj->zo_domain));
237 	objPath = internalTableName(b.buf);
238 	sfree(b.buf);
239 	if (slen(objPath) <= 0) {
240 		if (statP != 0)
241 			*statP = LDAP_OPERATIONS_ERROR;
242 		sfree(objPath);
243 		return (0);
244 	}
245 
246 	t = (__nis_table_mapping_t *)__nis_find_item_mt(objPath,
247 						&ldapMappingList, 0, 0);
248 
249 	sfree(objPath);
250 
251 	return (t);
252 }
253 
254 static __nis_table_mapping_t *
255 selectMapping(db_table *table, nis_object *obj, db_query *qin,
256 		bool_t wantWrite, bool_t *asObjP, int *statP) {
257 	__nis_table_mapping_t	*t;
258 	__nis_buffer_t		b = {0, 0};
259 	bool_t			doLDAP, asObj;
260 	int			stat = LDAP_SUCCESS;
261 	char			*objPath = 0, buf[MAXPATHLEN+NIS_MAXNAMELEN+1];
262 	char			*myself = "db_mindex::selectMapping";
263 
264 	/*
265 	 * If 'table' is NULL, we try to find a mapping for 'obj'.
266 	 * We expect this to happen when our caller wants to write
267 	 * the object from a directory entry to LDAP.
268 	 */
269 	if (table == 0) {
270 		if (asObjP != 0)
271 			*asObjP = TRUE;
272 		if (statP != 0)
273 			*statP = LDAP_SUCCESS;
274 
275 		t = mappingFromObj(obj, statP);
276 
277 		if (t == 0)
278 			return (0);
279 
280 		/*
281 		 * Should the object type in the mapping be NIS_BOGUS_OBJ,
282 		 * we need to determine what kind of object this is.
283 		 */
284 		if (t->objType == NIS_BOGUS_OBJ) {
285 			t->objType = updateMappingObj(t, 0, 0);
286 			if (t->objType == NIS_BOGUS_OBJ) {
287 				if (statP != 0)
288 					*statP = LDAP_OPERATIONS_ERROR;
289 				return (0);
290 			}
291 		}
292 
293 		/*
294 		 * If caller wants a mapping suitable for writing,
295 		 * check that we're the master for this object.
296 		 */
297 		if (wantWrite && !t->isMaster)
298 			return (0);
299 
300 		return (t);
301 	}
302 
303 	/*
304 	 * If the object type for the mapping is NIS_BOGUS_OBJ, then
305 	 * we haven't yet been able to determine what kind of object this
306 	 * is. Try to fix that now.
307 	 */
308 	if (table->mapping.objType == NIS_BOGUS_OBJ) {
309 		table->mapping.objType = updateMappingObj(table->mapping.tm,
310 						&table->mapping.objName,
311 						&table->mapping.isMaster);
312 		table->mapping.expireType = table->mapping.objType;
313 	}
314 
315 	/*
316 	 * Depending on the object type (table->mapping.objType):
317 	 *
318 	 *	table		Use table->mapping.tm to query LDAP
319 	 *			for entries per 'qin'.
320 	 *
321 	 *	directory	Use 'qin' and table->mapping.objName
322 	 *			to retrieve a mapping entry, and then
323 	 *			query LDAP for the corresponding object.
324 	 *			'qin' == NULL means reading/writing the
325 	 *			entire directory object, plus the names
326 	 *			of the directory entries.
327 	 *
328 	 *	bogus		Not mapping this object. However, we may
329 	 *			still be mapping the object 'obj'.
330 	 *
331 	 *	other		Shouldn't happen; illegal.
332 	 */
333 	switch (table->mapping.objType) {
334 	case NIS_TABLE_OBJ:
335 		t = table->mapping.tm;
336 		if (wantWrite)
337 			doLDAP = table->mapping.isMaster &&
338 					table->mapping.toLDAP;
339 		else
340 			doLDAP = table->mapping.fromLDAP;
341 		asObj = FALSE;
342 		break;
343 	case NIS_DIRECTORY_OBJ: {
344 		char		*sub = 0;
345 		int		nqc, len = 0;
346 		db_qcomp	*qc;
347 
348 		t = 0;
349 		doLDAP = FALSE;
350 		asObj = TRUE;
351 
352 		/*
353 		 * We expect the query to have one component, containing
354 		 * the directory entry name. If there's no query, we want
355 		 * an enumeration of the entries in the directory. They're
356 		 * stored with the XDR:ed directory object in LDAP, so
357 		 * asObj should be TRUE.
358 		 */
359 		if (qin == 0) {
360 			t = table->mapping.tm;
361 			if (wantWrite)
362 				doLDAP = table->mapping.isMaster &&
363 					table->mapping.toLDAP;
364 			else
365 				doLDAP = table->mapping.fromLDAP;
366 			asObj = TRUE;
367 			break;
368 		}
369 
370 		nqc = qin->size();
371 		if (nqc != 1 || (qc = qin->queryloc()) == 0 ||
372 				qc[0].index_value == 0) {
373 			stat = LDAP_PARAM_ERROR;
374 			break;
375 		}
376 		qc[0].index_value->get_value(&sub, &len);
377 		if (sub == 0 || len <= 0) {
378 			stat = LDAP_PARAM_ERROR;
379 			break;
380 		}
381 
382 		/* Append directory name to dir entry name */
383 		sbc2buf(myself, sub, len, &b);
384 		bp2buf(myself, &b, ".%s", table->mapping.objName);
385 
386 		/* Convert to the DB internal name */
387 		objPath = internal_table_name(b.buf, buf);
388 		sfree(b.buf);
389 		if (slen(objPath) <= 0) {
390 			stat = LDAP_OPERATIONS_ERROR;
391 			break;
392 		}
393 
394 		/* Look for the corresponding table mapping */
395 		t = (__nis_table_mapping_t *)__nis_find_item_mt(
396 					objPath, &ldapMappingList, 0, 0);
397 
398 		if (t == 0)
399 			break;
400 
401 		/* Update object mapping information */
402 		if (t->objType == NIS_BOGUS_OBJ)
403 			(void) updateMappingObj(t, 0, 0);
404 
405 		/*
406 		 * Should check the objectDN's in 't', but leave that to
407 		 * underlying functions.
408 		 */
409 		if (wantWrite)
410 			doLDAP = t->isMaster;
411 		else
412 			doLDAP = TRUE;
413 
414 		break;
415 	}
416 	case NIS_BOGUS_OBJ:
417 		t = mappingFromObj(obj, statP);
418 		doLDAP = TRUE;
419 		asObj = TRUE;
420 		break;
421 	default:
422 		t = 0;
423 		doLDAP = FALSE;
424 		asObj = TRUE;
425 		break;
426 	}
427 
428 	if (!doLDAP)
429 		t = 0;
430 
431 	if (asObjP != 0)
432 		*asObjP = asObj;
433 
434 	if (statP != 0)
435 		*statP = stat;
436 
437 	return (t);
438 }
439 
440 /*
441  * Replace or remove the table entry identified by 'e'. 'tableName' is
442  * the name of the table (which could be a directory) in which the entry
443  * resides. 'obj' is an un-XDR:ed copy of the object in 'e', optionally
444  * supplied to save re-doing unpacking of the entry object. 'tobj' is
445  * a pointer to the table object; needed for table entries, but not
446  * for directory entries.
447  *
448  * 'ttime' contains the current time, to be supplied for the trans log
449  * entry.
450  *
451  * Returns LDAP_SUCCESS when entry successfully added/modified/deleted,
452  * LDAP_COMPARE_TRUE if an entry to be added/modified was the same as
453  * an already existing one, and a suitable error otherwise.
454  */
455 int
456 db_mindex::updateTableEntry(entry_object *e, int replace, char *tableName,
457 		nis_object *obj, nis_object *tobj, uint32_t ttime,
458 		int *xid) {
459 	int			stat, freeObj = 0;
460 	db_index_entry		*dbie;
461 	long			count = 0;
462 	bool_t			valid = TRUE;
463 	db_result		*dbres;
464 	db_query		*qi;
465 	nis_object		*oldObj = 0;
466 	char			*myself = "db_mindex::updateTableEntry";
467 
468 	if (table == 0 || e == 0)
469 		return (LDAP_PARAM_ERROR);
470 
471 	qi = extract_index_values_from_object(e);
472 	if (qi == 0) {
473 		logmsg(MSG_NOMEM, LOG_ERR,
474 				"%s: Out of memory for query index",
475 				myself);
476 		return (LDAP_NO_MEMORY);
477 	}
478 
479 	dbie = satisfy_query(qi, &count, &valid, FALSE);
480 	if (dbie != 0 && (count != 1 || !valid)) {
481 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
482 			"%s: count=%d, valid=%s",
483 			myself, count, valid ? "TRUE" : "FALSE");
484 		delete qi;
485 		return (LDAP_OPERATIONS_ERROR);
486 	}
487 
488 	/*
489 	 * Need a copy of the old object in order to log a removal
490 	 * (this is true even if we're modifying an existing entry).
491 	 */
492 	if (dbie != 0) {
493 		oldObj = unmakePseudoEntryObj(
494 				table->get_entry(dbie->getlocation()), tobj);
495 		if (oldObj == 0) {
496 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
497 	"%s: Error getting object from old pseudo-entry for \"%s\" in \"%s\"",
498 					myself, NIL(obj->zo_name),
499 					NIL(tableName));
500 			delete qi;
501 			return (LDAP_OPERATIONS_ERROR);
502 		}
503 	}
504 
505 	if (replace) {
506 		/* Need the object from the entry */
507 		if (dbie != 0 && obj == 0) {
508 			obj = unmakePseudoEntryObj(e, tobj);
509 			if (obj == 0) {
510 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
511 	"%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
512 					myself, NIL(obj->zo_name),
513 					NIL(tableName));
514 				delete qi;
515 				nis_destroy_object(oldObj);
516 				return (LDAP_OPERATIONS_ERROR);
517 			}
518 			freeObj = 1;
519 		}
520 
521 		/* Is the new object a dup of the old ? */
522 		if (dbie != 0 && sameNisPlusObj(oldObj, obj)) {
523 			/* Yes, it's a dup, so just update the timestamp */
524 			table->touchEntry(dbie->getlocation());
525 			delete qi;
526 			if (freeObj)
527 				nis_destroy_object(obj);
528 			nis_destroy_object(oldObj);
529 			return (LDAP_COMPARE_TRUE);
530 		} else {
531 			/*
532 			 * Not a dup, so go ahead and add it. Provided
533 			 * that 'qi' isn't NULL (which we've already
534 			 * checked), DB_ADD(_NOSYNC) does the right
535 			 * thing even if matching entries already
536 			 * exist.
537 			 */
538 			dbres = ((db *)dbptr.ptr)->log_action(DB_ADD_NOSYNC,
539 								qi, e);
540 			if (dbres == 0)
541 				stat = LDAP_OPERATIONS_ERROR;
542 			else if (dbres->status == DB_SUCCESS)
543 				stat = LDAP_SUCCESS;
544 			else
545 				stat = LDAP_OPERATIONS_ERROR;
546 			db_free_result(dbres);
547 		}
548 	} else {	/* Removing */
549 		/* If the object doesn't exist, we're done */
550 		if (dbie == 0) {
551 			delete qi;
552 			return (LDAP_SUCCESS);
553 		}
554 
555 		dbres = ((db *)dbptr.ptr)->log_action(DB_REMOVE_NOSYNC, qi, 0);
556 		if (dbres == 0)
557 			stat = LDAP_OPERATIONS_ERROR;
558 		else if (dbres->status == DB_SUCCESS)
559 			stat = LDAP_SUCCESS;
560 		else
561 			stat = LDAP_OPERATIONS_ERROR;
562 		db_free_result(dbres);
563 	}
564 
565 	/* Log the operation */
566 	if (stat == LDAP_SUCCESS) {
567 		int		ret, numAttrs;
568 		nis_attr	*attr, attrbuf[NIS_MAXCOLUMNS];
569 
570 		/* If we haven't begun the transaction yet, do so now */
571 		if (*xid == 0) {
572 			*xid = beginTransaction();
573 			if (*xid == 0) {
574 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
575 				"%s: Error starting transaction for \"%s\"",
576 					myself, NIL(tableName));
577 				delete qi;
578 				if (oldObj != 0)
579 					nis_destroy_object(oldObj);
580 				return (LDAP_OPERATIONS_ERROR);
581 			}
582 		}
583 
584 		if (replace && obj == 0) {
585 			obj = unmakePseudoEntryObj(e, tobj);
586 			if (obj == 0) {
587 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
588 	"%s: Error getting object from pseudo-entry for \"%s\" in \"%s\"",
589 					myself, NIL(obj->zo_name),
590 					NIL(tableName));
591 				delete qi;
592 				if (oldObj != 0)
593 					nis_destroy_object(oldObj);
594 				return (LDAP_OPERATIONS_ERROR);
595 			}
596 			freeObj = 1;
597 		}
598 
599 		/*
600 		 * The log stores nis_attr information, so we need to
601 		 * convert the scheme-query to a nis_attr array.
602 		 */
603 		attr = schemeQuery2nisAttr(qi, attrbuf, scheme,
604 				table->mapping.tm, &numAttrs);
605 		if (attr == 0) {
606 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
607 	"%s: Error converting index query to nis_attr for \"%s\" in \"%s\"",
608 				myself, NIL(obj->zo_name), NIL(tableName));
609 			if (freeObj)
610 				nis_destroy_object(obj);
611 			if (oldObj != 0)
612 				nis_destroy_object(oldObj);
613 			delete qi;
614 			return (LDAP_OPERATIONS_ERROR);
615 		}
616 
617 		if (replace) {
618 			/*
619 			 * While the DB can handle a modify (replace)
620 			 * operation, the trans log stores this as a
621 			 * remove followed by an add (which allows
622 			 * backing out the change by removing the new
623 			 * object incarnation, and adding the old one).
624 			 */
625 			if (oldObj != 0)
626 				ret = addUpdate(REM_IBASE, tableName,
627 					numAttrs, attr, oldObj, 0, ttime);
628 			else
629 				ret = 0;
630 			if (ret == 0)
631 				ret = addUpdate(ADD_IBASE, tableName,
632 					numAttrs, attr, obj, 0, ttime);
633 		} else {	/* Removal */
634 			ret = addUpdate(REM_IBASE, tableName, numAttrs, attr,
635 					oldObj, 0, ttime);
636 		}
637 		if (ret != 0) {
638 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
639 		"%s: Error adding trans log entry for \"%s\" in \"%s\"",
640 				myself, NIL(obj->zo_name), NIL(tableName));
641 			stat = LDAP_OPERATIONS_ERROR;
642 		}
643 	}
644 
645 	delete qi;
646 
647 	if (oldObj != 0)
648 		nis_destroy_object(oldObj);
649 	if (freeObj)
650 		nis_destroy_object(obj);
651 
652 	return (stat);
653 }
654 
655 bool_t
656 db_mindex::touchEntry(entry_object *e) {
657 	db_query		*qi;
658 	bool_t			ret;
659 
660 	if (table == 0 || e == 0)
661 		return (FALSE);
662 
663 	qi = extract_index_values_from_object(e);
664 	if (qi == 0)
665 		return (FALSE);
666 
667 	ret = touchEntry(qi);
668 
669 	delete qi;
670 
671 	return (ret);
672 }
673 
674 bool_t
675 db_mindex::touchEntry(db_query *q) {
676 	db_index_entry		*dbie;
677 	long			count;
678 	bool_t			valid;
679 
680 	dbie = satisfy_query(q, &count, &valid, FALSE);
681 	if (dbie != 0 && count == 1 && valid)
682 		table->touchEntry(dbie->getlocation());
683 	else
684 		return (FALSE);
685 
686 	return (TRUE);
687 }
688 
689 /*
690  * Compose an object name from column zero of 'e' and 't->objName',
691  * and return the mapping for that object, if any. Also set '*name'
692  * to point to the dir entry name in 'e'. Note that this is a pointer
693  * to existing data, and shouldn't be freed other than as part of
694  * freeing 'e'.
695  */
696 static __nis_table_mapping_t *
697 findDirEntryMapping(__nis_table_mapping_t *t, entry_object *e, char **name) {
698 	__nis_table_mapping_t	*x;
699 	char			*entryName;
700 	char			*myself = "findDirEntryMapping";
701 	__nis_buffer_t		b = {0, 0};
702 
703 	if (e == 0 || e->en_cols.en_cols_len != 2 ||
704 			e->en_cols.en_cols_val == 0)
705 		return (0);
706 
707 	entryName = e->en_cols.en_cols_val[1].ec_value.ec_value_val;
708 	if (name != 0)
709 		*name = entryName;
710 
711 	if (t == 0 || entryName == 0 || t->objName == 0)
712 		return (0);
713 
714 	bp2buf(myself, &b, "%s.%s", entryName, t->objName);
715 	if (b.len == 0 || b.buf == 0)
716 		return (0);
717 
718 	x = (__nis_table_mapping_t *)__nis_find_item_mt(b.buf,
719 						&ldapMappingList, 0, 0);
720 
721 	sfree(b.buf);
722 
723 	return (x);
724 }
725 
726 /*
727  * Query LDAP per the supplied (scheme-) query 'qin'. If 'doAsynch' is
728  * set, and the query is an enumeration (qin == 0), the query will be
729  * performed in a detached thread, and complete asynchronously. In this
730  * case, the return status reflects the setup and launch of the
731  * detached thread; the query will complete asynchronously.
732  *
733  * Returns an appropriate LDAP status code.
734  */
735 int
736 db_mindex::queryLDAP(db_query *qin, char *dbId, int doAsynch) {
737 	__nis_table_mapping_t	*t;
738 	int			i, na, nq = 0, stat, stat2, numAttrs, ret;
739 	int			xid = 0;
740 	long			numEa;
741 	bool_t			asObj, doEnum;
742 	db_query		*q;
743 	entry_object		**ea;
744 	nis_attr		attr;
745 	nis_object		*dirObj;
746 	db_status		dstat;
747 	char			*myself = "db_mindex::queryLDAP";
748 
749 	if (!useLDAPrespository || table == 0)
750 		return (LDAP_SUCCESS);
751 
752 	/*
753 	 * Instances from the deferred dictionary shouldn't change,
754 	 * there's no point in querying LDAP.
755 	 */
756 	if (table->mapping.isDeferredTable)
757 		return (LDAP_SUCCESS);
758 
759 	t = selectMapping(table, 0, qin, FALSE, &asObj, &stat);
760 
761 	if (t == 0)
762 		return (stat);
763 
764 #ifdef	NISDB_LDAP_DEBUG
765 	printf("%s: %s (%s)\n",
766 		myself, NIL(t->objName), (asObj ? "object" : "entry"));
767 #endif	/* NISDB_LDAP_DEBUG */
768 
769 	if (qin != NULL) {
770 		q = schemeQuery2Query(qin, scheme);
771 		if (q == 0)
772 			return (LDAP_PARAM_ERROR);
773 #ifdef	NISDB_LDAP_DEBUG
774 		q->print();
775 #endif	/* NISDB_LDAP_DEBUG */
776 	} else {
777 		q = 0;
778 #ifdef	NISDB_LDAP_DEBUG
779 		printf("\tenumerating %s%s%s\n",
780 			dbId ? dbId : "", dbId ? ":" : "", NIL(t->objName));
781 #endif	/* NISDB_LDAP_DEBUG */
782 	}
783 
784 	/*
785 	 * Do we have any active mappings for this particular query and
786 	 * dbId ?  If not, we're done.
787 	 *
788 	 * Note that we don't care about the return value from
789 	 * selectTableMapping(), just wheter or not there are
790 	 * any valid mappings.
791 	 */
792 	i = 0;
793 	sfree(selectTableMapping(t, q, 0, asObj, dbId, &i));
794 	if (i <= 0) {
795 		freeQuery(q);
796 		return (LDAP_SUCCESS);
797 	}
798 
799 	/* Is the object a directory ? */
800 	if (asObj) {
801 		nis_object	*o;
802 		entry_object	*e, eo;
803 		entry_col	ec[2];
804 		int		nea;
805 
806 		stat = objFromLDAP(t, &o, &ea, &nea);
807 		numEa = nea;
808 
809 		if (stat == LDAP_NO_SUCH_OBJECT) {
810 			/* Positive failure; remove the object */
811 			dstat = dbDeleteObj(t->objName);
812 			if (dstat == DB_SUCCESS || dstat == DB_NOTFOUND) {
813 				stat = LDAP_SUCCESS;
814 			} else {
815 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
816 					"%s: DB error %d deleting \"%s\"",
817 					myself, dstat, NIL(t->objName));
818 				stat = LDAP_OPERATIONS_ERROR;
819 			}
820 
821 			freeQuery(q);
822 
823 			return (stat);
824 		} else if (stat != LDAP_SUCCESS) {
825 			freeQuery(q);
826 			return (stat);
827 		} else if (o == 0) {
828 			/* OK; this entry just isn't mapped */
829 			freeQuery(q);
830 			return (LDAP_SUCCESS);
831 		}
832 
833 		if (q != 0) {
834 			/*
835 			 * We're updating one particular entry (described
836 			 * by 't') in the directory 'table->mapping.tm'.
837 			 */
838 
839 			setOid(o);
840 			dstat = dbRefreshObj(t->objName, o);
841 			if (dstat == DB_SUCCESS) {
842 				stat = LDAP_SUCCESS;
843 			} else {
844 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
845 			"%s: DB error %d updating \"%s\" in \"%s\"",
846 					myself, NIL(t->objName),
847 					NIL(table->mapping.tm->objName));
848 				stat = LDAP_OPERATIONS_ERROR;
849 			}
850 
851 			freeEntryObjArray(ea, numEa);
852 			freeQuery(q);
853 			nis_destroy_object(o);
854 
855 			return (stat);
856 		}
857 
858 		dirObj = o;
859 
860 		/*
861 		 * q == 0, so we're enumerating. Update the list of
862 		 * directory entries.
863 		 */
864 
865 		/*
866 		 * Need to disable write-through to LDAP, for which we need
867 		 * a lock on our db_mindex ('this'); we're also updating the
868 		 * table, so we need a write lock on that as well.
869 		 */
870 		WRITELOCKNR(this, stat, "w db_mindex::queryLDAP");
871 		if (stat == 0) {
872 			WRITELOCKNR(table, stat2,
873 				"table w db_mindex::queryLDAP");
874 		}
875 		if (stat != 0 || stat2 != 0) {
876 			nis_destroy_object(dirObj);
877 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
878 				"%s: lock error %d", myself,
879 				stat != 0 ? stat : stat2);
880 			return (LDAP_OPERATIONS_ERROR);
881 		}
882 
883 		setNoWriteThrough();
884 		setNoLDAPquery();
885 		table->setEnumMode(0);
886 
887 		for (i = 0, na = 0; i < numEa; i++) {
888 			int			st;
889 			__nis_table_mapping_t	*x;
890 			char			*name = 0;
891 			entry_obj		*e;
892 
893 			if (ea[i] == 0)
894 				continue;
895 
896 			/*
897 			 * We've got a list of dir entries. In the general,
898 			 * case, some are new, and some already exist.
899 			 * We definitely want to add the new ones, and to
900 			 * that end, we need a copy of the object for the
901 			 * entry. By definition, if an entry is new, we
902 			 * don't yet have a copy of the object for it, so
903 			 * it's LDAP or nothing.
904 			 *
905 			 * If the entry already exists, try to update the
906 			 * entry object. In this case, we again only need
907 			 * to look in LDAP for the object; if there already
908 			 * is one in the DB, it's in the dir entry which we
909 			 * want to update.
910 			 *
911 			 * So, whether adding or replacing, try to get the
912 			 * object from LDAP.
913 			 *
914 			 * If we can't get a copy of the object, there's not
915 			 * much point in adding or updating (since a dir
916 			 * entry just consists of the entry object and name),
917 			 * so we continue to the next entry.
918 			 *
919 			 * However, in that case, we do need to touch the
920 			 * dir entry; otherwise, it will be removed later
921 			 * on.
922 			 */
923 
924 			x = findDirEntryMapping(t, ea[i], &name);
925 			o = 0;
926 			if (x == 0 || (st = objFromLDAP(x, &o, 0, 0)) !=
927 					LDAP_SUCCESS) {
928 				if (x != 0)
929 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
930 			"%s: Unable to obtain object for \"%s\" in \"%s\": %s",
931 					myself, NIL(name), NIL(t->objName),
932 						ldap_err2string(st));
933 				if (o != 0)
934 					nis_destroy_object(o);
935 				if (!touchEntry(ea[i])) {
936 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
937 			"%s: Inconsistency: LDAP-derived directory \"%s\" "
938 			"contains entry \"%s\", which is unknown locally, "
939 			"and has no LDAP mapping",
940 						myself, NIL(t->objName),
941 						NIL(name));
942 				}
943 				continue;
944 			}
945 
946 			if (ea[i]->en_cols.en_cols_len != 2 ||
947 				ea[i]->en_cols.en_cols_val == 0 ||
948 				ea[i]->en_cols.en_cols_val[0].
949 					ec_value.ec_value_val != 0 ||
950 				ea[i]->en_cols.en_cols_val[0].
951 					ec_value.ec_value_len != 0) {
952 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
953 			"%s: Illegal entry_obj col 0 for \"%s\" in \"%s\"",
954 					myself, NIL(name), NIL(t->objName));
955 				nis_destroy_object(o);
956 				touchEntry(ea[i]);
957 				continue;
958 			}
959 
960 			setOid(o);
961 			e = makePseudoEntryObj(o, ea[i], 0);
962 			if (e == 0) {
963 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
964 	"%s: Unable to create pseudo entry object for \"%s\" in \"%s\"",
965 					myself, NIL(name), NIL(t->objName));
966 				nis_destroy_object(o);
967 				touchEntry(ea[i]);
968 				continue;
969 			}
970 
971 			st = updateTableEntry(e, 1, t->objName, o, 0,
972 						o->zo_oid.mtime, &xid);
973 			if (st == LDAP_SUCCESS) {
974 				na++;
975 			} else if (st == LDAP_COMPARE_TRUE) {
976 				/* OK, same as existing entry */
977 				st = LDAP_SUCCESS;
978 			} else {
979 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
980 		"%s: Error updating directory entry for \"%s\" in \"%s\": %s",
981 					myself, NIL(name), NIL(t->objName),
982 					ldap_err2string(st));
983 				if (stat == LDAP_SUCCESS)
984 					stat = st;
985 			}
986 
987 			/* Free the XDR buffer */
988 			sfree(e->en_cols.en_cols_val[0].
989 					ec_value.ec_value_val);
990 			/* Restore ea[i] */
991 			ea[i]->en_cols.en_cols_val[0].
992 					ec_value.ec_value_val = 0;
993 			ea[i]->en_cols.en_cols_val[0].
994 					ec_value.ec_value_len = 0;
995 			nis_destroy_object(o);
996 		}
997 
998 		freeEntryObjArray(ea, numEa);
999 
1000 		/* Get list of entries to remove */
1001 		ea = table->endEnumMode(&numEa);
1002 		if (ea != 0) {
1003 			uint32_t	nowt = time(0);
1004 
1005 			for (i = 0; i < numEa; i++) {
1006 				int	st;
1007 
1008 				if (ea[i] == 0)
1009 					continue;
1010 
1011 				st = updateTableEntry(ea[i], 0, t->objName, 0,
1012 							0, nowt, &xid);
1013 				if (st == LDAP_SUCCESS) {
1014 					na++;
1015 				} else {
1016 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1017 			"%s: Error removing directory entry for \"%s\": %s",
1018 						myself, NIL(t->objName),
1019 						ldap_err2string(st));
1020 					if (stat == LDAP_SUCCESS)
1021 						stat = st;
1022 				}
1023 			}
1024 		}
1025 
1026 		if (stat == LDAP_SUCCESS) {
1027 			struct timeval	now;
1028 			(void) gettimeofday(&now, 0);
1029 			table->mapping.enumExpire = now.tv_sec +
1030 				table->mapping.ttl;
1031 		}
1032 
1033 		if (na > 0)
1034 			(void) ((db *)dbptr.ptr)->sync_log();
1035 
1036 		if (xid != 0 && na > 0 && stat == LDAP_SUCCESS) {
1037 			ret = endTransaction(xid, dirObj);
1038 			if (ret != 0) {
1039 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1040 				"%s: Error ending transaction for \"%s\"",
1041 					myself, NIL(t->objName));
1042 				stat = LDAP_OPERATIONS_ERROR;
1043 			}
1044 		} else if (xid != 0) {
1045 			ret = abort_transaction(xid);
1046 			if (ret != 0) {
1047 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1048 				"%s: Error aborting transaction for \"%s\"",
1049 					myself, NIL(t->objName));
1050 			}
1051 		}
1052 		nis_destroy_object(dirObj);
1053 
1054 		sfree(ea);
1055 
1056 		clearNoLDAPquery();
1057 		clearNoWriteThrough();
1058 
1059 		WRITEUNLOCK2(table, this,
1060 			stat, stat,
1061 			"table wu db_mindex::queryLDAP",
1062 			"wu db_mindex::queryLDAP");
1063 
1064 		return (stat);
1065 	}
1066 
1067 	/*
1068 	 * In order to ping replicas, if any, we need to find the
1069 	 * directory containing the table to be updated. If we
1070 	 * can't find the directory object, we're sunk, so let's
1071 	 * start with that.
1072 	 */
1073 	if (t->isMaster) {
1074 		dirObj = findObj(t->obj->zo_domain, &dstat, &stat);
1075 		if (dirObj == 0) {
1076 			if (stat == LDAP_SUCCESS)
1077 				stat = LDAP_OPERATIONS_ERROR;
1078 			return (stat);
1079 		}
1080 	} else {
1081 		dirObj = 0;
1082 	}
1083 
1084 	stat = entriesFromLDAP(t, qin, q, dbId, dirObj, doAsynch);
1085 
1086 	return (stat);
1087 }
1088 
1089 extern db	*tableDB(char *);
1090 
1091 /*
1092  * Remove the LDAP entry/entries corresponding to 'qin'/'obj'.
1093  */
1094 int
1095 db_mindex::removeLDAP(db_query *qin, nis_object *obj) {
1096 	__nis_table_mapping_t	*t;
1097 	db_query		*q;
1098 	bool_t			asObj;
1099 	int			stat;
1100 
1101 	if (!useLDAPrespository || table == 0)
1102 		return (LDAP_SUCCESS);
1103 
1104 	/* Instances from the deferred dictionary should not update LDAP */
1105 	if (table->mapping.isDeferredTable)
1106 		return (LDAP_SUCCESS);
1107 
1108 	t = selectMapping(table, 0, qin, TRUE, &asObj, &stat);
1109 	if (t == 0 && stat != LDAP_SUCCESS)
1110 		return (stat);
1111 
1112 #ifdef	NISDB_LDAP_DEBUG
1113 	if (t != 0)
1114 		printf("removeLDAP: %s\n", NIL(t->objName));
1115 #endif	/* NISDB_LDAP_DEBUG */
1116 
1117 	if (qin != NULL) {
1118 		if (asObj) {
1119 			/*
1120 			 * selectMapping() gave us the mapping for the
1121 			 * directory entry. However, if 't' is NULL, this
1122 			 * could be due to the directory itself not being
1123 			 * mapped, in which case we must obtain the mapping
1124 			 * info from 'obj'.
1125 			 */
1126 			if (t == 0) {
1127 				t = selectMapping(0, obj, 0, TRUE, &asObj,
1128 						&stat);
1129 				if (t == 0 && stat != LDAP_SUCCESS)
1130 					return (stat);
1131 			}
1132 
1133 			if (t != 0) {
1134 				stat = deleteLDAPobj(t);
1135 				/*
1136 				 * If we were successful, update the object
1137 				 * stored with the mapping.
1138 				 */
1139 				if (stat == LDAP_SUCCESS)
1140 					(void) replaceMappingObj(t, 0);
1141 				else
1142 					return (stat);
1143 			}
1144 
1145 			/*
1146 			 * Since it's a directory entry we've removed, we also
1147 			 * need to update the directory object itself.
1148 			 */
1149 			stat = storeLDAP(0, 0, 0, 0, 0);
1150 		} else {
1151 			q = schemeQuery2Query(qin, scheme);
1152 			if (q == 0)
1153 				return (LDAP_PARAM_ERROR);
1154 #ifdef	NISDB_LDAP_DEBUG
1155 			q->print();
1156 #endif	/* NISDB_LDAP_DEBUG */
1157 			stat = mapToLDAP(t, 1, &q, 0, 0, 0, 0);
1158 			freeQuery(q);
1159 		}
1160 	} else {
1161 		/*
1162 		 * This isn't the way to remove the LDAP entries
1163 		 * corresponding to the entire table.
1164 		 */
1165 #ifdef	NISDB_LDAP_DEBUG
1166 		abort();
1167 #endif	/* NISDB_LDAP_DEBUG */
1168 		stat = LDAP_PARAM_ERROR;
1169 	}
1170 
1171 	return (stat);
1172 }
1173 
1174 /*
1175  * Helper function for storeLDAP() which handles updates for objects
1176  * other than table entries.
1177  */
1178 int
1179 db_mindex::storeObjLDAP(__nis_table_mapping_t *t, nis_object *o) {
1180 	int		stat, assigned = 0;
1181 	entry_object	**ea;
1182 	int		numEa, doUnlock = 0;
1183 	db		*dbase = 0;
1184 	db_mindex	*dbm = 0;
1185 	char		*myself = "db_mindex::storeObjLDAP";
1186 
1187 	if (t == 0 || o == 0)
1188 		return (LDAP_SUCCESS);
1189 
1190 	/*
1191 	 * If the object to be stored is anything other than a
1192 	 * directory, we can just go ahead and write it to LDAP.
1193 	 * A directory object, however, also needs a directory
1194 	 * entry list, so we should to get hold of the db_table
1195 	 * that goes with the directory.
1196 	 */
1197 	if (o->zo_data.zo_type == NIS_DIRECTORY_OBJ) {
1198 		dbase = tableDB(t->objName);
1199 		if (dbase != 0)
1200 			dbm = dbase->mindex();
1201 		if (dbase == 0 || dbm == 0 || dbm->table == 0) {
1202 			/* By definition, no dir entries */
1203 			ea = 0;
1204 			numEa = 0;
1205 			dbase = 0;
1206 		} else {
1207 			entry_object	**tea;
1208 			long		i, ntea;
1209 
1210 			/*
1211 			 * Read-lock the table so that 'tab'
1212 			 * doesn't change while we're using it.
1213 			 */
1214 			READLOCK(dbm->table, LDAP_OPERATIONS_ERROR,
1215 					"r table db_mindex::storeLDAP");
1216 			doUnlock = 1;
1217 
1218 			tea = dbm->table->gettab();
1219 			ntea = dbm->table->getsize();
1220 
1221 			/*
1222 			 * There may be empty slots in the table 'tab'
1223 			 * array, so get rid of those.
1224 			 */
1225 			if (tea != 0 && ntea > 0) {
1226 				ea = (entry_object **)am(myself,
1227 						ntea * sizeof (ea[0]));
1228 				if (ea == 0) {
1229 					READUNLOCK(dbm->table, LDAP_NO_MEMORY,
1230 					"ru table db_mindex::storeLDAP");
1231 					return (LDAP_NO_MEMORY);
1232 				}
1233 				for (i = 0, numEa = 0; i < ntea; i++) {
1234 					if (tea[i] != 0) {
1235 						ea[numEa] = tea[i];
1236 						numEa++;
1237 					}
1238 				}
1239 				if (numEa == 0) {
1240 					/* No non-empty slots */
1241 					sfree(ea);
1242 					ea = 0;
1243 					READUNLOCK(dbm->table,
1244 						LDAP_OPERATIONS_ERROR,
1245 					"ru table db_mindex::storeLDAP");
1246 					doUnlock = 0;
1247 				}
1248 			} else {
1249 				ea = 0;
1250 				numEa = 0;
1251 				READUNLOCK(dbm->table,
1252 					LDAP_OPERATIONS_ERROR,
1253 					"ru table db_mindex::storeLDAP");
1254 				doUnlock = 0;
1255 			}
1256 		}
1257 	} else {
1258 		ea = 0;
1259 		numEa = 0;
1260 	}
1261 
1262 	stat = objToLDAP(t, o, ea, numEa);
1263 
1264 	if (ea != 0)
1265 		sfree(ea);
1266 	if (doUnlock) {
1267 		READUNLOCK(dbm->table, stat,
1268 				"ru table db_mindex::storeLDAP");
1269 	}
1270 
1271 	return (stat);
1272 }
1273 
1274 
1275 /*
1276  * Store data specified by the index-query 'qin' to LDAP. If 'obj' is
1277  * non-null, it's a pointer to the pseudo-entry object corresponding to
1278  * 'qin'. As a short-cut/convenience, the caller can instead supply
1279  * the actual nis_object 'o'; if 'o' is NULL, it's derived from 'obj'.
1280  *
1281  * 'oldObj' is used for table entries if the store operation is
1282  * an update, and the corresponding NIS+ operation was a delete followed
1283  * by an add. In this case, oldObj contains the pre-delete incarnation of
1284  * the entry object to be modified.
1285  *
1286  * The 'dbId' string is used to select one dbId for mapping chains
1287  * that contain more than one.
1288  *
1289  * Returns an LDAP status code.
1290  */
1291 int
1292 db_mindex::storeLDAP(db_query *qin, entry_object *obj, nis_object *o,
1293 			entry_obj *oldObj, char *dbId) {
1294 	__nis_table_mapping_t	*t;
1295 	bool_t			asObj;
1296 	db_query		*q, *qo, **qa;
1297 	__nis_rule_value_t	*rv = 0;
1298 	int			stat;
1299 	char			*myself = "db_mindex::storeLDAP";
1300 
1301 	if (!useLDAPrespository || table == 0)
1302 		return (LDAP_SUCCESS);
1303 
1304 	/* Instances from the deferred dictionary should not update LDAP */
1305 	if (table->mapping.isDeferredTable)
1306 		return (LDAP_SUCCESS);
1307 
1308 	t = selectMapping(table, 0, qin, TRUE, &asObj, &stat);
1309 	if (t == 0 && stat != LDAP_SUCCESS)
1310 		return (stat);
1311 
1312 #ifdef	NISDB_LDAP_DEBUG
1313 	if (t != 0)
1314 		printf("storeLDAP: %s%s%s\n",
1315 			dbId ? dbId : "", dbId ? ":" : "", NIL(t->objName));
1316 #endif	/* NISDB_LDAP_DEBUG */
1317 
1318 	/*
1319 	 * selectMapping() didn't have the object to look at, so we
1320 	 * must check if this is a directory entry or not.
1321 	 */
1322 	if (asObj) {
1323 		if (o != 0) {
1324 			if (o->zo_data.zo_type == NIS_ENTRY_OBJ)
1325 				asObj = FALSE;
1326 		} else if (obj != 0) {
1327 			if (obj->en_type == 0 ||
1328 				strcmp(obj->en_type, "IN_DIRECTORY") != 0)
1329 				asObj = FALSE;
1330 		}
1331 	}
1332 
1333 	if (asObj) {
1334 		bool_t		freeO = FALSE;
1335 
1336 		/*
1337 		 * If we don't have a mapping, that's probably because
1338 		 * the directory (represented by 'this') isn't mapped.
1339 		 * Try to get a mapping from 'o' or 'obj'.
1340 		 */
1341 		if (t == 0) {
1342 			if (o == 0 && obj != 0) {
1343 				o = unmakePseudoEntryObj(obj, 0);
1344 				if (o == 0)
1345 					return (LDAP_OPERATIONS_ERROR);
1346 				freeO = TRUE;
1347 			}
1348 			if (o != 0) {
1349 				t = selectMapping(0, o, 0, TRUE, &asObj, &stat);
1350 				if (t == 0) {
1351 					if (freeO)
1352 						nis_destroy_object(o);
1353 					return (stat);
1354 				}
1355 			}
1356 		}
1357 
1358 		/*
1359 		 * If we found a mapping for the 'table' in this db_mindex,
1360 		 * store the object.
1361 		 */
1362 		if (t != 0) {
1363 			if (o == 0) {
1364 				if (obj != 0) {
1365 					o = unmakePseudoEntryObj(obj, 0);
1366 					freeO = TRUE;
1367 				} else {
1368 					db_status	dstat;
1369 
1370 					o = dbFindObject(t->objName, &dstat);
1371 					if (o == 0)
1372 						logmsg(MSG_NOTIMECHECK, LOG_ERR,
1373 					"%s: DB error %d finding \"%s\"",
1374 							myself,
1375 							NIL(t->objName));
1376 					freeO = TRUE;
1377 				}
1378 			}
1379 			if (o == 0)
1380 				return (LDAP_OPERATIONS_ERROR);
1381 
1382 			stat = storeObjLDAP(t, o);
1383 
1384 			/*
1385 			 * Store the object with the mapping. If 'o' was
1386 			 * supplied by the caller, we first need to make
1387 			 * a copy.
1388 			 */
1389 			if (!freeO) {
1390 				o = nis_clone_object(o, 0);
1391 				if (o == 0)
1392 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1393 			"%s: Unable to refresh mapping object for \"%s\"",
1394 						myself, NIL(t->objName));
1395 			}
1396 			if (o != 0) {
1397 				if (!replaceMappingObj(t, o))
1398 					nis_destroy_object(o);
1399 			}
1400 
1401 			/*
1402 			 * Object now either destroyed or stored in 't'.
1403 			 * Set pointer to NULL in order to avoid freeing
1404 			 * it below.
1405 			 */
1406 			o = 0;
1407 
1408 			if (stat != LDAP_SUCCESS)
1409 				return (stat);
1410 		}
1411 
1412 		if (freeO && o != 0) {
1413 			nis_destroy_object(o);
1414 			o = 0;
1415 		}
1416 
1417 		/*
1418 		 * If the entry object 'obj' has the type "IN_DIRECTORY",
1419 		 * then it's a directory entry, and we should check if
1420 		 * the directory is mapped to LDAP, and update the dir
1421 		 * entry list accordingly.
1422 		 */
1423 		if (obj == 0 || obj->en_type == 0 ||
1424 				strcmp(obj->en_type, "IN_DIRECTORY") != 0)
1425 			return (LDAP_SUCCESS);
1426 
1427 		/* Does it have a mapping  ? */
1428 		t = selectMapping(table, 0, 0, TRUE, &asObj, &stat);
1429 		if (t == 0)
1430 			return (stat);
1431 
1432 		stat = storeObjLDAP(t, t->obj);
1433 
1434 		return (stat);
1435 	}
1436 
1437 	/* Store table entries. If we don't have a mapping, we're done. */
1438 	if (t == 0)
1439 		return (LDAP_SUCCESS);
1440 
1441 	if (qin != NULL && obj != NULL) {
1442 		db_index_entry	*dbie;
1443 		int		i, size, nq = 0;
1444 		long		l, count;
1445 		bool_t		valid;
1446 		db_query	qbuf, **qold;
1447 
1448 		rv = (__nis_rule_value_t *)am(myself, sizeof (*rv));
1449 		qa = (db_query **)am(myself, sizeof (qa[0]));
1450 		if (oldObj != 0) {
1451 			/*
1452 			 * Note that only qold[0] is a unique query pointer.
1453 			 * All the other qold[i]'s are copies of qa[i].
1454 			 * Hence, we only free qold[0], as well as qold
1455 			 * itself.
1456 			 */
1457 			qold = (db_query **)am(myself, sizeof (qold[0]));
1458 		} else {
1459 			qold = 0;
1460 		}
1461 		if (rv == 0 || qa == 0 || (oldObj != 0 && qold == 0)) {
1462 			sfree(rv);
1463 			sfree(qa);
1464 			sfree(qold);
1465 			return (LDAP_NO_MEMORY);
1466 		}
1467 
1468 		q = schemeQuery2Query(qin, scheme);
1469 		if (q == 0) {
1470 			sfree(rv);
1471 			sfree(qa);
1472 			return (LDAP_PARAM_ERROR);
1473 		}
1474 
1475 		qa[0] = pseudoEntryObj2Query(obj, t->obj, &rv[0]);
1476 		if (qa[0] == 0) {
1477 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1478 "%s: Unable to obtain query representation of new entry object for \"%s\"",
1479 				myself, NIL(t->dbId));
1480 			freeQuery(q);
1481 			sfree(rv);
1482 			sfree(qa);
1483 			sfree(qold);
1484 			return (LDAP_OPERATIONS_ERROR);
1485 		}
1486 		if (oldObj != 0) {
1487 			qold[0] = pseudoEntryObj2Query(oldObj, t->obj, 0);
1488 			if (qold[0] == 0) {
1489 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
1490 "%s: Unable to obtain query representation of old entry object for \"%s\"",
1491 					myself, NIL(t->dbId));
1492 				freeQueries(qa, 1);
1493 				freeQuery(q);
1494 				sfree(rv);
1495 				sfree(qa);
1496 				sfree(qold);
1497 				return (LDAP_OPERATIONS_ERROR);
1498 			}
1499 		}
1500 
1501 		nq++;
1502 
1503 		/*
1504 		 * In order to support many-to-one NIS+ to LDAP mapping,
1505 		 * we need to find all possible matches in the NIS+ DB,
1506 		 * and then merge to produce a single update. mapToLDAP()
1507 		 * takes care of the merging, so our job is to collect
1508 		 * the matches. Worst case is that we need to search
1509 		 * individually for each component in 'qin', so that's
1510 		 * what we'll do.
1511 		 *
1512 		 * mapToLDAP() has a mode that only performs an update
1513 		 * for the first DN, and that's what we want. In order
1514 		 * to make sure that it's the correct DN, we leave the
1515 		 * original query as the first one passed to mapToLDAP().
1516 		 */
1517 
1518 		size = qin->size();
1519 
1520 		/* For each component of 'qin' */
1521 		for (i = 0; i < size; i++) {
1522 			db_query		*qc, **qat, **qoldt;
1523 			long			j;
1524 			__nis_rule_value_t	*rvt;
1525 
1526 			qc = queryFromComponent(qin, i, &qbuf);
1527 			if (qc == 0)
1528 				continue;
1529 
1530 			dbie = satisfy_query_dbonly(qc, &count, FALSE, &valid);
1531 			if (dbie == 0 || !valid || count <= 0)
1532 				continue;
1533 
1534 			rvt = (__nis_rule_value_t *)realloc(rv,
1535 						(nq+count) * sizeof (rv[0]));
1536 			qat = (db_query **)realloc(qa,
1537 						(nq+count) * sizeof (qa[0]));
1538 			if (qold != 0)
1539 				qoldt = (db_query **)realloc(qold,
1540 						(nq+count) * sizeof (qold[0]));
1541 			if (rvt == 0 || qat == 0 || (qold != 0 && qoldt == 0)) {
1542 				if (qat == 0)
1543 					freeQueries(qa, nq);
1544 				else
1545 					freeQueries(qat, nq);
1546 				if (rvt == 0)
1547 					freeRuleValue(rv, nq);
1548 				else
1549 					freeRuleValue(rvt, nq);
1550 				if (qold != 0) {
1551 					if (qoldt == 0)
1552 						freeQueries(qold, 1);
1553 					else
1554 						freeQueries(qoldt, 1);
1555 				}
1556 				freeQuery(q);
1557 				(void) memset(&qbuf, 0, sizeof (qbuf));
1558 				logmsg(MSG_NOMEM, LOG_ERR,
1559 					"%s: realloc(%d) failed",
1560 					myself, (nq+count) * sizeof (void *));
1561 				return (LDAP_NO_MEMORY);
1562 			}
1563 
1564 			rv = rvt;
1565 			qa = qat;
1566 
1567 			(void) memset(&rv[nq], 0, count * sizeof (rv[0]));
1568 			(void) memset(&qa[nq], 0, count * sizeof (qa[0]));
1569 			if (qold != 0) {
1570 				qold = qoldt;
1571 				(void) memset(&qold[nq], 0,
1572 						count * sizeof (qold[0]));
1573 			}
1574 
1575 			for (j = 0; j < count; j++) {
1576 				qa[nq] = pseudoEntryObj2Query(
1577 					table->get_entry(dbie->getlocation()),
1578 							t->obj, &rv[nq]);
1579 				if (qa[nq] == 0) {
1580 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1581 			"%s: Could not create query from entry obj for \"%s\"",
1582 						myself, NIL(t->objName));
1583 					freeQueries(qa, nq);
1584 					freeQueries(qold, 1);
1585 					freeRuleValue(rv, nq);
1586 					freeQuery(q);
1587 					(void) memset(&qbuf, 0, sizeof (qbuf));
1588 					return (LDAP_PARAM_ERROR);
1589 				}
1590 				if (qold != 0)
1591 					qold[nq] = qa[nq];
1592 				nq++;
1593 				dbie = dbie->getnextresult();
1594 				if (dbie == 0)
1595 					break;
1596 			}
1597 		}
1598 
1599 		stat = mapToLDAP(t, nq, (qold != 0 ? qold : qa), qa, rv, 1,
1600 				dbId);
1601 
1602 		freeQueries(qa, nq);
1603 		freeRuleValue(rv, nq);
1604 		freeQuery(q);
1605 		freeQueries(qold, 1);
1606 		(void) memset(&qbuf, 0, sizeof (qbuf));
1607 
1608 	} else if (qin == 0 && obj == 0 && t->objType == NIS_TABLE_OBJ) {
1609 		long			i, j, ntab;
1610 		entry_object		**tab;
1611 
1612 		READLOCK(table, LDAP_OPERATIONS_ERROR,
1613 				"r table db_mindex::storeLDAP");
1614 
1615 		tab = table->gettab();
1616 		ntab = table->getsize();
1617 		if (tab == 0 || ntab <= 0) {
1618 			READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1619 					"ru table db_mindex::storeLDAP");
1620 			return (LDAP_SUCCESS);
1621 		}
1622 
1623 		qa = (db_query **)am(myself, ntab * sizeof (qa[0]));
1624 		rv = (__nis_rule_value_t *)am(myself, ntab * sizeof (rv[0]));
1625 		if (qa == 0 || rv == 0) {
1626 			sfree(qa);
1627 			sfree(rv);
1628 			READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1629 					"ru table db_mindex::storeLDAP");
1630 			return (LDAP_NO_MEMORY);
1631 		}
1632 
1633 		for (i = 0; i < ntab; i++) {
1634 			if (tab[i] == 0)
1635 				continue;
1636 
1637 			qa[i] = pseudoEntryObj2Query(tab[i], t->obj, &rv[i]);
1638 			if (qa[i] == 0) {
1639 				freeQueries(qa, i);
1640 				freeRuleValue(rv, i);
1641 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1642 			"%s: Could not create query from entry for \"%s\"",
1643 					myself, NIL(t->objName));
1644 				READUNLOCK(table, LDAP_OPERATIONS_ERROR,
1645 					"ru table db_mindex::storeLDAP");
1646 				return (LDAP_OPERATIONS_ERROR);
1647 			}
1648 		}
1649 
1650 		stat = mapToLDAP(t, ntab, qa, qa, rv, 0, dbId);
1651 
1652 		freeQueries(qa, ntab);
1653 		freeRuleValue(rv, ntab);
1654 
1655 		if (stat == LDAP_SUCCESS) {
1656 			struct timeval  now;
1657 			int		lstat, lck = 1;
1658 			/*
1659 			 * Since we've just successfully uploaded everthing
1660 			 * in this table, we now consider our local copy
1661 			 * up-to-date as well.
1662 			 */
1663 
1664 			(void) gettimeofday(&now, 0);
1665 			WRITELOCKNR(table, lstat,
1666 				"w table db_mindex::storeLDAP");
1667 			if (lstat == 0) {
1668 				table->mapping.enumExpire = now.tv_sec +
1669 					table->mapping.ttl;
1670 				lck = 0;
1671 				WRITEUNLOCKNR(table, lstat,
1672 					"wu table db_mindex::storeLDAP");
1673 			}
1674 			if (lstat != 0) {
1675 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
1676 					"%s: %sock error %d for \"%s\"%s",
1677 					myself, lck?"L":"Unl", lstat,
1678 					NIL(t->objName),
1679 					lck ?
1680 				"; unable to update enumeration expiration":
1681 				"");
1682 			}
1683 		}
1684 
1685 		READUNLOCK(table, stat,
1686 				"ru table db_mindex::storeLDAP");
1687 	}
1688 
1689 	return (stat);
1690 }
1691