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