1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 1997, 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 8 #include "config.h" 9 10 #ifndef lint 11 static const char sccsid[] = "@(#)db_appinit.c 10.66 (Sleepycat) 12/7/98"; 12 #endif /* not lint */ 13 14 #ifndef NO_SYSTEM_INCLUDES 15 #include <sys/types.h> 16 17 #include <ctype.h> 18 #include <errno.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <unistd.h> 22 #endif 23 24 #include "db_int.h" 25 #include "shqueue.h" 26 #include "db_page.h" 27 #include "btree.h" 28 #include "hash.h" 29 #include "log.h" 30 #include "txn.h" 31 #include "clib_ext.h" 32 #include "common_ext.h" 33 34 static int __db_home __P((DB_ENV *, const char *, u_int32_t)); 35 static int __db_parse __P((DB_ENV *, char *)); 36 static int __db_tmp_open __P((DB_ENV *, u_int32_t, char *, int *)); 37 38 /* 39 * This conflict array is used for concurrent db access (cdb). It 40 * uses the same locks as the db_rw_conflict array, but adds an IW 41 * mode to be used for write cursors. 42 */ 43 static u_int8_t const db_cdb_conflicts[] = { 44 /* N R W IW */ 45 /* N */ 0, 0, 0, 0, 46 /* R */ 0, 0, 1, 0, 47 /* W */ 0, 1, 1, 1, 48 /* IW */ 0, 0, 1, 1 49 }; 50 51 /* 52 * db_version -- 53 * Return version information. 54 */ 55 char * 56 db_version(majverp, minverp, patchp) 57 int *majverp, *minverp, *patchp; 58 { 59 if (majverp != NULL) 60 *majverp = DB_VERSION_MAJOR; 61 if (minverp != NULL) 62 *minverp = DB_VERSION_MINOR; 63 if (patchp != NULL) 64 *patchp = DB_VERSION_PATCH; 65 return ((char *)DB_VERSION_STRING); 66 } 67 68 /* 69 * db_appinit -- 70 * Initialize the application environment. 71 */ 72 int 73 db_appinit(db_home, db_config, dbenv, flags) 74 const char *db_home; 75 char * const *db_config; 76 DB_ENV *dbenv; 77 u_int32_t flags; 78 { 79 FILE *fp; 80 int mode, ret; 81 char * const *p; 82 char *lp, buf[MAXPATHLEN * 2]; 83 84 fp = NULL; 85 86 /* Validate arguments. */ 87 if (dbenv == NULL) 88 return (EINVAL); 89 90 #ifdef HAVE_SPINLOCKS 91 #define OKFLAGS \ 92 (DB_CREATE | DB_INIT_CDB | DB_INIT_LOCK | DB_INIT_LOG | \ 93 DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_NOMMAP | \ 94 DB_RECOVER | DB_RECOVER_FATAL | DB_THREAD | DB_TXN_NOSYNC | \ 95 DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT) 96 #else 97 #define OKFLAGS \ 98 (DB_CREATE | DB_INIT_CDB | DB_INIT_LOCK | DB_INIT_LOG | \ 99 DB_INIT_MPOOL | DB_INIT_TXN | DB_MPOOL_PRIVATE | DB_NOMMAP | \ 100 DB_RECOVER | DB_RECOVER_FATAL | DB_TXN_NOSYNC | \ 101 DB_USE_ENVIRON | DB_USE_ENVIRON_ROOT) 102 #endif 103 if ((ret = __db_fchk(dbenv, "db_appinit", flags, OKFLAGS)) != 0) 104 return (ret); 105 106 /* Transactions imply logging. */ 107 if (LF_ISSET(DB_INIT_TXN)) 108 LF_SET(DB_INIT_LOG); 109 110 /* Convert the db_appinit(3) flags. */ 111 if (LF_ISSET(DB_THREAD)) 112 F_SET(dbenv, DB_ENV_THREAD); 113 114 /* Set the database home. */ 115 if ((ret = __db_home(dbenv, db_home, flags)) != 0) 116 goto err; 117 118 /* Parse the config array. */ 119 for (p = db_config; p != NULL && *p != NULL; ++p) 120 if ((ret = __db_parse(dbenv, *p)) != 0) 121 goto err; 122 123 /* 124 * Parse the config file. 125 * 126 * XXX 127 * Don't use sprintf(3)/snprintf(3) -- the former is dangerous, and 128 * the latter isn't standard, and we're manipulating strings handed 129 * us by the application. 130 */ 131 if (dbenv->db_home != NULL) { 132 #define CONFIG_NAME "/DB_CONFIG" 133 if (strlen(dbenv->db_home) + 134 strlen(CONFIG_NAME) + 1 > sizeof(buf)) { 135 ret = ENAMETOOLONG; 136 goto err; 137 } 138 (void)strcpy(buf, dbenv->db_home); 139 (void)strcat(buf, CONFIG_NAME); 140 if ((fp = fopen(buf, "r")) != NULL) { 141 while (fgets(buf, sizeof(buf), fp) != NULL) { 142 if ((lp = strchr(buf, '\n')) == NULL) { 143 __db_err(dbenv, 144 "%s: line too long", CONFIG_NAME); 145 ret = EINVAL; 146 goto err; 147 } 148 *lp = '\0'; 149 if (buf[0] == '\0' || 150 buf[0] == '#' || isspace(buf[0])) 151 continue; 152 153 if ((ret = __db_parse(dbenv, buf)) != 0) 154 goto err; 155 } 156 (void)fclose(fp); 157 fp = NULL; 158 } 159 } 160 161 /* Set up the tmp directory path. */ 162 if (dbenv->db_tmp_dir == NULL && (ret = __os_tmpdir(dbenv, flags)) != 0) 163 goto err; 164 165 /* 166 * Flag that the structure has been initialized by the application. 167 * Note, this must be set before calling into the subsystems as it 168 * is used when we're doing file naming. 169 */ 170 F_SET(dbenv, DB_ENV_APPINIT); 171 172 /* 173 * If we are doing recovery, remove all the old shared memory 174 * regions. 175 */ 176 if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL)) { 177 if ((ret = log_unlink(NULL, 1, dbenv)) != 0) 178 goto err; 179 if ((ret = memp_unlink(NULL, 1, dbenv)) != 0) 180 goto err; 181 if ((ret = lock_unlink(NULL, 1, dbenv)) != 0) 182 goto err; 183 if ((ret = txn_unlink(NULL, 1, dbenv)) != 0) 184 goto err; 185 } 186 187 /* 188 * Create the new shared regions. 189 * 190 * Default permissions are read-write for both owner and group. 191 */ 192 mode = __db_omode("rwrw--"); 193 if (LF_ISSET(DB_INIT_CDB)) { 194 if (LF_ISSET(DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN)) { 195 ret = EINVAL; 196 goto err; 197 } 198 F_SET(dbenv, DB_ENV_CDB); 199 dbenv->lk_conflicts = db_cdb_conflicts; 200 dbenv->lk_modes = DB_LOCK_RW_N + 1; 201 if ((ret = lock_open(NULL, LF_ISSET(DB_CREATE | DB_THREAD), 202 mode, dbenv, &dbenv->lk_info)) != 0) 203 goto err; 204 } 205 if (LF_ISSET(DB_INIT_LOCK) && (ret = lock_open(NULL, 206 LF_ISSET(DB_CREATE | DB_THREAD), 207 mode, dbenv, &dbenv->lk_info)) != 0) 208 goto err; 209 if (LF_ISSET(DB_INIT_LOG) && (ret = log_open(NULL, 210 LF_ISSET(DB_CREATE | DB_THREAD), 211 mode, dbenv, &dbenv->lg_info)) != 0) 212 goto err; 213 if (LF_ISSET(DB_INIT_MPOOL) && (ret = memp_open(NULL, 214 LF_ISSET(DB_CREATE | DB_MPOOL_PRIVATE | DB_NOMMAP | DB_THREAD), 215 mode, dbenv, &dbenv->mp_info)) != 0) 216 goto err; 217 if (LF_ISSET(DB_INIT_TXN) && (ret = txn_open(NULL, 218 LF_ISSET(DB_CREATE | DB_THREAD | DB_TXN_NOSYNC), 219 mode, dbenv, &dbenv->tx_info)) != 0) 220 goto err; 221 222 /* 223 * If the application is running with transactions, initialize the 224 * function tables. Once that's done, do recovery for any previous 225 * run. 226 */ 227 if (LF_ISSET(DB_INIT_TXN)) { 228 if ((ret = __bam_init_recover(dbenv)) != 0) 229 goto err; 230 if ((ret = __db_init_recover(dbenv)) != 0) 231 goto err; 232 if ((ret = __ham_init_recover(dbenv)) != 0) 233 goto err; 234 if ((ret = __log_init_recover(dbenv)) != 0) 235 goto err; 236 if ((ret = __txn_init_recover(dbenv)) != 0) 237 goto err; 238 239 if (LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL) && 240 (ret = __db_apprec(dbenv, 241 LF_ISSET(DB_RECOVER | DB_RECOVER_FATAL))) != 0) 242 goto err; 243 } 244 245 return (ret); 246 247 err: if (fp != NULL) 248 (void)fclose(fp); 249 250 (void)db_appexit(dbenv); 251 return (ret); 252 } 253 254 /* 255 * db_appexit -- 256 * Close down the default application environment. 257 */ 258 int 259 db_appexit(dbenv) 260 DB_ENV *dbenv; 261 { 262 int ret, t_ret; 263 char **p; 264 265 ret = 0; 266 267 /* Close subsystems. */ 268 if (dbenv->tx_info && (t_ret = txn_close(dbenv->tx_info)) != 0) 269 if (ret == 0) 270 ret = t_ret; 271 if (dbenv->lg_info && (t_ret = log_close(dbenv->lg_info)) != 0) 272 if (ret == 0) 273 ret = t_ret; 274 if (dbenv->mp_info && (t_ret = memp_close(dbenv->mp_info)) != 0) 275 if (ret == 0) 276 ret = t_ret; 277 if (dbenv->lk_info && (t_ret = lock_close(dbenv->lk_info)) != 0) 278 if (ret == 0) 279 ret = t_ret; 280 281 /* Clear initialized flag (after subsystems, it affects naming). */ 282 F_CLR(dbenv, DB_ENV_APPINIT); 283 284 /* Free allocated memory. */ 285 if (dbenv->db_home != NULL) 286 __os_freestr(dbenv->db_home); 287 if ((p = dbenv->db_data_dir) != NULL) { 288 for (; *p != NULL; ++p) 289 __os_freestr(*p); 290 __os_free(dbenv->db_data_dir, 291 dbenv->data_cnt * sizeof(char **)); 292 } 293 if (dbenv->db_log_dir != NULL) 294 __os_freestr(dbenv->db_log_dir); 295 if (dbenv->db_tmp_dir != NULL) 296 __os_freestr(dbenv->db_tmp_dir); 297 298 return (ret); 299 } 300 301 #define DB_ADDSTR(str) { \ 302 if ((str) != NULL) { \ 303 /* If leading slash, start over. */ \ 304 if (__os_abspath(str)) { \ 305 p = start; \ 306 slash = 0; \ 307 } \ 308 /* Append to the current string. */ \ 309 len = strlen(str); \ 310 if (slash) \ 311 *p++ = PATH_SEPARATOR[0]; \ 312 memcpy(p, str, len); \ 313 p += len; \ 314 slash = strchr(PATH_SEPARATOR, p[-1]) == NULL; \ 315 } \ 316 } 317 318 /* 319 * __db_appname -- 320 * Given an optional DB environment, directory and file name and type 321 * of call, build a path based on the db_appinit(3) rules, and return 322 * it in allocated space. 323 * 324 * PUBLIC: int __db_appname __P((DB_ENV *, 325 * PUBLIC: APPNAME, const char *, const char *, u_int32_t, int *, char **)); 326 */ 327 int 328 __db_appname(dbenv, appname, dir, file, tmp_oflags, fdp, namep) 329 DB_ENV *dbenv; 330 APPNAME appname; 331 const char *dir, *file; 332 u_int32_t tmp_oflags; 333 int *fdp; 334 char **namep; 335 { 336 DB_ENV etmp; 337 size_t len; 338 int data_entry, ret, slash, tmp_create, tmp_free; 339 const char *a, *b, *c; 340 char *p, *start; 341 342 a = b = c = NULL; 343 data_entry = -1; 344 tmp_create = tmp_free = 0; 345 346 /* 347 * We don't return a name when creating temporary files, just an fd. 348 * Default to error now. 349 */ 350 if (fdp != NULL) 351 *fdp = -1; 352 if (namep != NULL) 353 *namep = NULL; 354 355 /* 356 * Absolute path names are never modified. If the file is an absolute 357 * path, we're done. If the directory is, simply append the file and 358 * return. 359 */ 360 if (file != NULL && __os_abspath(file)) 361 return (__os_strdup(file, namep)); 362 if (dir != NULL && __os_abspath(dir)) { 363 a = dir; 364 goto done; 365 } 366 367 /* 368 * DB_ENV DIR APPNAME RESULT 369 * ------------------------------------------- 370 * null null none <tmp>/file 371 * null set none DIR/file 372 * set null none DB_HOME/file 373 * set set none DB_HOME/DIR/file 374 * 375 * DB_ENV FILE APPNAME RESULT 376 * ------------------------------------------- 377 * null null DB_APP_DATA <tmp>/<create> 378 * null set DB_APP_DATA ./file 379 * set null DB_APP_DATA <tmp>/<create> 380 * set set DB_APP_DATA DB_HOME/DB_DATA_DIR/file 381 * 382 * DB_ENV DIR APPNAME RESULT 383 * ------------------------------------------- 384 * null null DB_APP_LOG <tmp>/file 385 * null set DB_APP_LOG DIR/file 386 * set null DB_APP_LOG DB_HOME/DB_LOG_DIR/file 387 * set set DB_APP_LOG DB_HOME/DB_LOG_DIR/DIR/file 388 * 389 * DB_ENV APPNAME RESULT 390 * ------------------------------------------- 391 * null DB_APP_TMP* <tmp>/<create> 392 * set DB_APP_TMP* DB_HOME/DB_TMP_DIR/<create> 393 */ 394 retry: switch (appname) { 395 case DB_APP_NONE: 396 if (dbenv == NULL || !F_ISSET(dbenv, DB_ENV_APPINIT)) { 397 if (dir == NULL) 398 goto tmp; 399 a = dir; 400 } else { 401 a = dbenv->db_home; 402 b = dir; 403 } 404 break; 405 case DB_APP_DATA: 406 if (dir != NULL) { 407 __db_err(dbenv, 408 "DB_APP_DATA: illegal directory specification"); 409 return (EINVAL); 410 } 411 412 if (file == NULL) { 413 tmp_create = 1; 414 goto tmp; 415 } 416 if (dbenv == NULL || !F_ISSET(dbenv, DB_ENV_APPINIT)) 417 a = PATH_DOT; 418 else { 419 a = dbenv->db_home; 420 if (dbenv->db_data_dir != NULL && 421 (b = dbenv->db_data_dir[++data_entry]) == NULL) { 422 data_entry = -1; 423 b = dbenv->db_data_dir[0]; 424 } 425 } 426 break; 427 case DB_APP_LOG: 428 if (dbenv == NULL || !F_ISSET(dbenv, DB_ENV_APPINIT)) { 429 if (dir == NULL) 430 goto tmp; 431 a = dir; 432 } else { 433 a = dbenv->db_home; 434 b = dbenv->db_log_dir; 435 c = dir; 436 } 437 break; 438 case DB_APP_TMP: 439 if (dir != NULL || file != NULL) { 440 __db_err(dbenv, 441 "DB_APP_TMP: illegal directory or file specification"); 442 return (EINVAL); 443 } 444 445 tmp_create = 1; 446 if (dbenv == NULL || !F_ISSET(dbenv, DB_ENV_APPINIT)) 447 goto tmp; 448 else { 449 a = dbenv->db_home; 450 b = dbenv->db_tmp_dir; 451 } 452 break; 453 } 454 455 /* Reference a file from the appropriate temporary directory. */ 456 if (0) { 457 tmp: if (dbenv == NULL || !F_ISSET(dbenv, DB_ENV_APPINIT)) { 458 memset(&etmp, 0, sizeof(etmp)); 459 if ((ret = __os_tmpdir(&etmp, DB_USE_ENVIRON)) != 0) 460 return (ret); 461 tmp_free = 1; 462 a = etmp.db_tmp_dir; 463 } else 464 a = dbenv->db_tmp_dir; 465 } 466 467 done: len = 468 (a == NULL ? 0 : strlen(a) + 1) + 469 (b == NULL ? 0 : strlen(b) + 1) + 470 (c == NULL ? 0 : strlen(c) + 1) + 471 (file == NULL ? 0 : strlen(file) + 1); 472 473 /* 474 * Allocate space to hold the current path information, as well as any 475 * temporary space that we're going to need to create a temporary file 476 * name. 477 */ 478 #define DB_TRAIL "XXXXXX" 479 if ((ret = 480 __os_malloc(len + sizeof(DB_TRAIL) + 10, NULL, &start)) != 0) { 481 if (tmp_free) 482 __os_freestr(etmp.db_tmp_dir); 483 return (ret); 484 } 485 486 slash = 0; 487 p = start; 488 DB_ADDSTR(a); 489 DB_ADDSTR(b); 490 DB_ADDSTR(file); 491 *p = '\0'; 492 493 /* Discard any space allocated to find the temp directory. */ 494 if (tmp_free) { 495 __os_freestr(etmp.db_tmp_dir); 496 tmp_free = 0; 497 } 498 499 /* 500 * If we're opening a data file, see if it exists. If it does, 501 * return it, otherwise, try and find another one to open. 502 */ 503 if (data_entry != -1 && __os_exists(start, NULL) != 0) { 504 __os_freestr(start); 505 a = b = c = NULL; 506 goto retry; 507 } 508 509 /* Create the file if so requested. */ 510 if (tmp_create && 511 (ret = __db_tmp_open(dbenv, tmp_oflags, start, fdp)) != 0) { 512 __os_freestr(start); 513 return (ret); 514 } 515 516 if (namep == NULL) 517 __os_freestr(start); 518 else 519 *namep = start; 520 return (0); 521 } 522 523 /* 524 * __db_home -- 525 * Find the database home. 526 */ 527 static int 528 __db_home(dbenv, db_home, flags) 529 DB_ENV *dbenv; 530 const char *db_home; 531 u_int32_t flags; 532 { 533 const char *p; 534 535 p = db_home; 536 537 /* Use the environment if it's permitted and initialized. */ 538 #ifdef HAVE_GETUID 539 if (LF_ISSET(DB_USE_ENVIRON) || 540 (LF_ISSET(DB_USE_ENVIRON_ROOT) && getuid() == 0)) { 541 #else 542 if (LF_ISSET(DB_USE_ENVIRON)) { 543 #endif 544 if ((p = getenv("DB_HOME")) == NULL) 545 p = db_home; 546 else if (p[0] == '\0') { 547 __db_err(dbenv, 548 "illegal DB_HOME environment variable"); 549 return (EINVAL); 550 } 551 } 552 553 if (p == NULL) 554 return (0); 555 556 return (__os_strdup(p, &dbenv->db_home)); 557 } 558 559 /* 560 * __db_parse -- 561 * Parse a single NAME VALUE pair. 562 */ 563 static int 564 __db_parse(dbenv, s) 565 DB_ENV *dbenv; 566 char *s; 567 { 568 int ret; 569 char *local_s, *name, *value, **p, *tp; 570 571 /* 572 * We need to strdup the argument in case the caller passed us 573 * static data. 574 */ 575 if ((ret = __os_strdup(s, &local_s)) != 0) 576 return (ret); 577 578 /* 579 * Name/value pairs are parsed as two white-space separated strings. 580 * Leading and trailing white-space is trimmed from the value, but 581 * it may contain embedded white-space. Note: we use the isspace(3) 582 * macro because it's more portable, but that means that you can use 583 * characters like form-feed to separate the strings. 584 */ 585 name = local_s; 586 for (tp = name; *tp != '\0' && !isspace(*tp); ++tp) 587 ; 588 if (*tp == '\0' || tp == name) 589 goto illegal; 590 *tp = '\0'; 591 for (++tp; isspace(*tp); ++tp) 592 ; 593 if (*tp == '\0') 594 goto illegal; 595 value = tp; 596 for (++tp; *tp != '\0'; ++tp) 597 ; 598 for (--tp; isspace(*tp); --tp) 599 ; 600 if (tp == value) { 601 illegal: ret = EINVAL; 602 __db_err(dbenv, "illegal name-value pair: %s", s); 603 goto err; 604 } 605 *++tp = '\0'; 606 607 #define DATA_INIT_CNT 20 /* Start with 20 data slots. */ 608 if (!strcmp(name, "DB_DATA_DIR")) { 609 if (dbenv->db_data_dir == NULL) { 610 if ((ret = __os_calloc(DATA_INIT_CNT, 611 sizeof(char **), &dbenv->db_data_dir)) != 0) 612 goto err; 613 dbenv->data_cnt = DATA_INIT_CNT; 614 } else if (dbenv->data_next == dbenv->data_cnt - 1) { 615 dbenv->data_cnt *= 2; 616 if ((ret = __os_realloc(&dbenv->db_data_dir, 617 dbenv->data_cnt * sizeof(char **))) != 0) 618 goto err; 619 } 620 p = &dbenv->db_data_dir[dbenv->data_next++]; 621 } else if (!strcmp(name, "DB_LOG_DIR")) { 622 if (dbenv->db_log_dir != NULL) 623 __os_freestr(dbenv->db_log_dir); 624 p = &dbenv->db_log_dir; 625 } else if (!strcmp(name, "DB_TMP_DIR")) { 626 if (dbenv->db_tmp_dir != NULL) 627 __os_freestr(dbenv->db_tmp_dir); 628 p = &dbenv->db_tmp_dir; 629 } else 630 goto err; 631 632 ret = __os_strdup(value, p); 633 634 err: __os_freestr(local_s); 635 return (ret); 636 } 637 638 /* 639 * __db_tmp_open -- 640 * Create a temporary file. 641 */ 642 static int 643 __db_tmp_open(dbenv, flags, path, fdp) 644 DB_ENV *dbenv; 645 u_int32_t flags; 646 char *path; 647 int *fdp; 648 { 649 u_long pid; 650 int mode, isdir, ret; 651 const char *p; 652 char *trv; 653 654 /* 655 * Check the target directory; if you have six X's and it doesn't 656 * exist, this runs for a *very* long time. 657 */ 658 if ((ret = __os_exists(path, &isdir)) != 0) { 659 __db_err(dbenv, "%s: %s", path, strerror(ret)); 660 return (ret); 661 } 662 if (!isdir) { 663 __db_err(dbenv, "%s: %s", path, strerror(EINVAL)); 664 return (EINVAL); 665 } 666 667 /* Build the path. */ 668 for (trv = path; *trv != '\0'; ++trv) 669 ; 670 *trv = PATH_SEPARATOR[0]; 671 for (p = DB_TRAIL; (*++trv = *p) != '\0'; ++p) 672 ; 673 674 /* 675 * Replace the X's with the process ID. Pid should be a pid_t, 676 * but we use unsigned long for portability. 677 */ 678 for (pid = getpid(); *--trv == 'X'; pid /= 10) 679 switch (pid % 10) { 680 case 0: *trv = '0'; break; 681 case 1: *trv = '1'; break; 682 case 2: *trv = '2'; break; 683 case 3: *trv = '3'; break; 684 case 4: *trv = '4'; break; 685 case 5: *trv = '5'; break; 686 case 6: *trv = '6'; break; 687 case 7: *trv = '7'; break; 688 case 8: *trv = '8'; break; 689 case 9: *trv = '9'; break; 690 } 691 ++trv; 692 693 /* Set up open flags and mode. */ 694 LF_SET(DB_CREATE | DB_EXCL); 695 mode = __db_omode("rw----"); 696 697 /* Loop, trying to open a file. */ 698 for (;;) { 699 if ((ret = __db_open(path, flags, flags, mode, fdp)) == 0) 700 return (0); 701 702 /* 703 * XXX: 704 * If we don't get an EEXIST error, then there's something 705 * seriously wrong. Unfortunately, if the implementation 706 * doesn't return EEXIST for O_CREAT and O_EXCL regardless 707 * of other possible errors, we've lost. 708 */ 709 if (ret != EEXIST) { 710 __db_err(dbenv, 711 "tmp_open: %s: %s", path, strerror(ret)); 712 return (ret); 713 } 714 715 /* 716 * Tricky little algorithm for backward compatibility. 717 * Assumes the ASCII ordering of lower-case characters. 718 */ 719 for (;;) { 720 if (*trv == '\0') 721 return (EINVAL); 722 if (*trv == 'z') 723 *trv++ = 'a'; 724 else { 725 if (isdigit(*trv)) 726 *trv = 'a'; 727 else 728 ++*trv; 729 break; 730 } 731 } 732 } 733 /* NOTREACHED */ 734 } 735