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