1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1999,2008 Oracle. All rights reserved. 5 * 6 * $Id: qam_files.c,v 12.37 2008/03/13 15:44:50 mbrey Exp $ 7 */ 8 9#include "db_config.h" 10 11#include "db_int.h" 12#include "dbinc/db_page.h" 13#include "dbinc/db_am.h" 14#include "dbinc/log.h" 15#include "dbinc/fop.h" 16#include "dbinc/mp.h" 17#include "dbinc/qam.h" 18 19#define QAM_EXNAME(Q, I, B, L) \ 20 snprintf((B), (L), \ 21 QUEUE_EXTENT, (Q)->dir, PATH_SEPARATOR[0], (Q)->name, (I)) 22 23/* 24 * __qam_fprobe -- calculate and open extent 25 * 26 * Calculate which extent the page is in, open and create if necessary. 27 * 28 * PUBLIC: int __qam_fprobe __P((DBC *, db_pgno_t, 29 * PUBLIC: void *, qam_probe_mode, DB_CACHE_PRIORITY, u_int32_t)); 30 */ 31int 32__qam_fprobe(dbc, pgno, addrp, mode, priority, flags) 33 DBC *dbc; 34 db_pgno_t pgno; 35 void *addrp; 36 qam_probe_mode mode; 37 DB_CACHE_PRIORITY priority; 38 u_int32_t flags; 39{ 40 DB *dbp; 41 DB_MPOOLFILE *mpf; 42 ENV *env; 43 MPFARRAY *array; 44 QUEUE *qp; 45 u_int8_t fid[DB_FILE_ID_LEN]; 46 u_int32_t i, extid, maxext, numext, lflags, offset, oldext, openflags; 47 char buf[DB_MAXPATHLEN]; 48 int ftype, less, ret, t_ret; 49 50 dbp = dbc->dbp; 51 env = dbp->env; 52 qp = (QUEUE *)dbp->q_internal; 53 ret = 0; 54 55 if (qp->page_ext == 0) { 56 mpf = dbp->mpf; 57 switch (mode) { 58 case QAM_PROBE_GET: 59 return (__memp_fget(mpf, &pgno, 60 dbc->thread_info, dbc->txn, flags, addrp)); 61 case QAM_PROBE_PUT: 62 return (__memp_fput(mpf, 63 dbc->thread_info, addrp, priority)); 64 case QAM_PROBE_DIRTY: 65 return (__memp_dirty(mpf, addrp, 66 dbc->thread_info, dbc->txn, priority, flags)); 67 case QAM_PROBE_MPF: 68 *(DB_MPOOLFILE **)addrp = mpf; 69 return (0); 70 } 71 } 72 73 mpf = NULL; 74 75 /* 76 * Need to lock long enough to find the mpf or create the file. 77 * The file cannot go away because we must have a record locked 78 * in that file. 79 */ 80 MUTEX_LOCK(env, dbp->mutex); 81 extid = QAM_PAGE_EXTENT(dbp, pgno); 82 83 /* Array1 will always be in use if array2 is in use. */ 84 array = &qp->array1; 85 if (array->n_extent == 0) { 86 /* Start with 4 extents */ 87 array->n_extent = 4; 88 array->low_extent = extid; 89 numext = offset = oldext = 0; 90 less = 0; 91 goto alloc; 92 } 93 94retry: 95 if (extid < array->low_extent) { 96 less = 1; 97 offset = array->low_extent - extid; 98 } else { 99 less = 0; 100 offset = extid - array->low_extent; 101 } 102 if (qp->array2.n_extent != 0 && 103 (extid >= qp->array2.low_extent ? 104 offset > extid - qp->array2.low_extent : 105 offset > qp->array2.low_extent - extid)) { 106 array = &qp->array2; 107 if (extid < array->low_extent) { 108 less = 1; 109 offset = array->low_extent - extid; 110 } else { 111 less = 0; 112 offset = extid - array->low_extent; 113 } 114 } 115 116 /* 117 * Check to see if the requested extent is outside the range of 118 * extents in the array. This is true by default if there are 119 * no extents here yet. 120 */ 121 if (less == 1 || offset >= array->n_extent) { 122 oldext = array->n_extent; 123 numext = (array->hi_extent - array->low_extent) + 1; 124 if (less == 1 && offset + numext <= array->n_extent) { 125 /* 126 * If we can fit this one into the existing array by 127 * shifting the existing entries then we do not have 128 * to allocate. 129 */ 130 memmove(&array->mpfarray[offset], 131 array->mpfarray, numext 132 * sizeof(array->mpfarray[0])); 133 memset(array->mpfarray, 0, offset 134 * sizeof(array->mpfarray[0])); 135 offset = 0; 136 } else if (less == 0 && offset == array->n_extent && 137 (mode == QAM_PROBE_GET || mode == QAM_PROBE_PUT) && 138 array->mpfarray[0].pinref == 0) { 139 /* 140 * If this is at the end of the array and the file at 141 * the beginning has a zero pin count we can close 142 * the bottom extent and put this one at the end. 143 */ 144 mpf = array->mpfarray[0].mpf; 145 if (mpf != NULL && (ret = __memp_fclose(mpf, 0)) != 0) 146 goto err; 147 memmove(&array->mpfarray[0], &array->mpfarray[1], 148 (array->n_extent - 1) * sizeof(array->mpfarray[0])); 149 array->low_extent++; 150 array->hi_extent++; 151 offset--; 152 array->mpfarray[offset].mpf = NULL; 153 array->mpfarray[offset].pinref = 0; 154 } else { 155 /* 156 * See if we have wrapped around the queue. 157 * If it has then allocate the second array. 158 * Otherwise just expand the one we are using. 159 */ 160 maxext = (u_int32_t) UINT32_MAX 161 / (qp->page_ext * qp->rec_page); 162 if (offset >= maxext/2) { 163 array = &qp->array2; 164 DB_ASSERT(env, array->n_extent == 0); 165 oldext = 0; 166 array->n_extent = 4; 167 array->low_extent = extid; 168 offset = 0; 169 numext = 0; 170 } else if (array->mpfarray[0].pinref == 0) { 171 /* 172 * Check to see if there are extents marked 173 * for deletion at the beginning of the cache. 174 * If so close them so they will go away. 175 */ 176 for (i = 0; i < array->n_extent; i++) { 177 if (array->mpfarray[i].pinref != 0) 178 break; 179 mpf = array->mpfarray[i].mpf; 180 if (mpf == NULL) 181 continue; 182 (void)__memp_get_flags(mpf, &lflags); 183 if (!FLD_ISSET(lflags, DB_MPOOL_UNLINK)) 184 break; 185 186 array->mpfarray[i].mpf = NULL; 187 if ((ret = __memp_fclose(mpf, 0)) != 0) 188 goto err; 189 } 190 if (i == 0) 191 goto increase; 192 memmove(&array->mpfarray[0], 193 &array->mpfarray[i], 194 (array->n_extent - i) * 195 sizeof(array->mpfarray[0])); 196 memset(&array->mpfarray[array->n_extent - i], 197 '\0', i * sizeof(array->mpfarray[0])); 198 array->low_extent += i; 199 array->hi_extent += i; 200 goto retry; 201 } else { 202 /* 203 * Increase the size to at least include 204 * the new one and double it. 205 */ 206increase: array->n_extent += offset; 207 array->n_extent <<= 2; 208 } 209alloc: if ((ret = __os_realloc(env, 210 array->n_extent * sizeof(struct __qmpf), 211 &array->mpfarray)) != 0) 212 goto err; 213 214 if (less == 1) { 215 /* 216 * Move the array up and put the new one 217 * in the first slot. 218 */ 219 memmove(&array->mpfarray[offset], 220 array->mpfarray, 221 numext * sizeof(array->mpfarray[0])); 222 memset(array->mpfarray, 0, 223 offset * sizeof(array->mpfarray[0])); 224 memset(&array->mpfarray[numext + offset], 0, 225 (array->n_extent - (numext + offset)) 226 * sizeof(array->mpfarray[0])); 227 offset = 0; 228 } 229 else 230 /* Clear the new part of the array. */ 231 memset(&array->mpfarray[oldext], 0, 232 (array->n_extent - oldext) * 233 sizeof(array->mpfarray[0])); 234 } 235 } 236 237 /* Update the low and hi range of saved extents. */ 238 if (extid < array->low_extent) 239 array->low_extent = extid; 240 if (extid > array->hi_extent) 241 array->hi_extent = extid; 242 243 /* If the extent file is not yet open, open it. */ 244 if (array->mpfarray[offset].mpf == NULL) { 245 QAM_EXNAME(qp, extid, buf, sizeof(buf)); 246 if ((ret = __memp_fcreate( 247 env, &array->mpfarray[offset].mpf)) != 0) 248 goto err; 249 mpf = array->mpfarray[offset].mpf; 250 (void)__memp_set_lsn_offset(mpf, 0); 251 (void)__memp_set_pgcookie(mpf, &qp->pgcookie); 252 (void)__memp_get_ftype(dbp->mpf, &ftype); 253 (void)__memp_set_ftype(mpf, ftype); 254 (void)__memp_set_clear_len(mpf, dbp->pgsize); 255 256 /* Set up the fileid for this extent. */ 257 __qam_exid(dbp, fid, extid); 258 (void)__memp_set_fileid(mpf, fid); 259 openflags = DB_EXTENT; 260 if (LF_ISSET(DB_MPOOL_CREATE)) 261 openflags |= DB_CREATE; 262 if (F_ISSET(dbp, DB_AM_RDONLY)) 263 openflags |= DB_RDONLY; 264 if (F_ISSET(env->dbenv, DB_ENV_DIRECT_DB)) 265 openflags |= DB_DIRECT; 266 if ((ret = __memp_fopen( 267 mpf, NULL, buf, openflags, qp->mode, dbp->pgsize)) != 0) { 268 array->mpfarray[offset].mpf = NULL; 269 (void)__memp_fclose(mpf, 0); 270 goto err; 271 } 272 } 273 274 /* 275 * We have found the right file. Update its ref count 276 * before dropping the dbp mutex so it does not go away. 277 */ 278 mpf = array->mpfarray[offset].mpf; 279 if (mode == QAM_PROBE_GET) 280 array->mpfarray[offset].pinref++; 281 282 /* 283 * If we may create the page, then we are writing, 284 * the file may nolonger be empty after this operation 285 * so we clear the UNLINK flag. 286 */ 287 if (LF_ISSET(DB_MPOOL_CREATE)) 288 (void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 0); 289 290err: 291 MUTEX_UNLOCK(env, dbp->mutex); 292 293 if (ret == 0) { 294 pgno--; 295 pgno %= qp->page_ext; 296 switch (mode) { 297 case QAM_PROBE_GET: 298 ret = __memp_fget(mpf, &pgno, 299 dbc->thread_info, dbc->txn, flags, addrp); 300 if (ret == 0) 301 return (0); 302 break; 303 case QAM_PROBE_PUT: 304 ret = __memp_fput(mpf, 305 dbc->thread_info, addrp, dbp->priority); 306 break; 307 case QAM_PROBE_DIRTY: 308 return (__memp_dirty(mpf, addrp, 309 dbc->thread_info, dbc->txn, dbp->priority, flags)); 310 case QAM_PROBE_MPF: 311 *(DB_MPOOLFILE **)addrp = mpf; 312 return (0); 313 } 314 315 MUTEX_LOCK(env, dbp->mutex); 316 /* Recalculate because we dropped the lock. */ 317 offset = extid - array->low_extent; 318 DB_ASSERT(env, array->mpfarray[offset].pinref > 0); 319 if (--array->mpfarray[offset].pinref == 0 && 320 (mode == QAM_PROBE_GET || ret == 0)) { 321 /* Check to see if this file will be unlinked. */ 322 (void)__memp_get_flags(mpf, &flags); 323 if (LF_ISSET(DB_MPOOL_UNLINK)) { 324 array->mpfarray[offset].mpf = NULL; 325 if ((t_ret = 326 __memp_fclose(mpf, 0)) != 0 && ret == 0) 327 ret = t_ret; 328 } 329 } 330 MUTEX_UNLOCK(env, dbp->mutex); 331 } 332 return (ret); 333} 334 335/* 336 * __qam_fclose -- close an extent. 337 * 338 * Calculate which extent the page is in and close it. 339 * We assume the mpf entry is present. 340 * 341 * PUBLIC: int __qam_fclose __P((DB *, db_pgno_t)); 342 */ 343int 344__qam_fclose(dbp, pgnoaddr) 345 DB *dbp; 346 db_pgno_t pgnoaddr; 347{ 348 DB_MPOOLFILE *mpf; 349 ENV *env; 350 MPFARRAY *array; 351 QUEUE *qp; 352 u_int32_t extid, offset; 353 int ret; 354 355 ret = 0; 356 env = dbp->env; 357 qp = (QUEUE *)dbp->q_internal; 358 359 MUTEX_LOCK(env, dbp->mutex); 360 361 extid = QAM_PAGE_EXTENT(dbp, pgnoaddr); 362 array = &qp->array1; 363 if (array->low_extent > extid || array->hi_extent < extid) 364 array = &qp->array2; 365 offset = extid - array->low_extent; 366 367 DB_ASSERT(env, 368 extid >= array->low_extent && offset < array->n_extent); 369 370 /* If other threads are still using this file, leave it. */ 371 if (array->mpfarray[offset].pinref != 0) 372 goto done; 373 374 mpf = array->mpfarray[offset].mpf; 375 array->mpfarray[offset].mpf = NULL; 376 ret = __memp_fclose(mpf, 0); 377 378done: 379 MUTEX_UNLOCK(env, dbp->mutex); 380 return (ret); 381} 382 383/* 384 * __qam_fremove -- remove an extent. 385 * 386 * Calculate which extent the page is in and remove it. There is no way 387 * to remove an extent without probing it first and seeing that is is empty 388 * so we assume the mpf entry is present. 389 * 390 * PUBLIC: int __qam_fremove __P((DB *, db_pgno_t)); 391 */ 392int 393__qam_fremove(dbp, pgnoaddr) 394 DB *dbp; 395 db_pgno_t pgnoaddr; 396{ 397 DB_MPOOLFILE *mpf; 398 ENV *env; 399 MPFARRAY *array; 400 QUEUE *qp; 401 u_int32_t extid, offset; 402 int ret; 403 404 qp = (QUEUE *)dbp->q_internal; 405 env = dbp->env; 406 ret = 0; 407 408 MUTEX_LOCK(env, dbp->mutex); 409 410 extid = QAM_PAGE_EXTENT(dbp, pgnoaddr); 411 array = &qp->array1; 412 if (array->low_extent > extid || array->hi_extent < extid) 413 array = &qp->array2; 414 offset = extid - array->low_extent; 415 416 DB_ASSERT(env, 417 extid >= array->low_extent && offset < array->n_extent); 418 419 mpf = array->mpfarray[offset].mpf; 420 /* This extent my already be marked for delete and closed. */ 421 if (mpf == NULL) 422 goto err; 423 424 /* 425 * The log must be flushed before the file is deleted. We depend on 426 * the log record of the last delete to recreate the file if we crash. 427 */ 428 if (LOGGING_ON(env) && (ret = __log_flush(env, NULL)) != 0) 429 goto err; 430 431 (void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 1); 432 /* Someone could be real slow, let them close it down. */ 433 if (array->mpfarray[offset].pinref != 0) 434 goto err; 435 array->mpfarray[offset].mpf = NULL; 436 if ((ret = __memp_fclose(mpf, 0)) != 0) 437 goto err; 438 439 /* 440 * If the file is at the bottom of the array 441 * shift things down and adjust the end points. 442 */ 443 if (offset == 0) { 444 memmove(array->mpfarray, &array->mpfarray[1], 445 (array->hi_extent - array->low_extent) 446 * sizeof(array->mpfarray[0])); 447 array->mpfarray[ 448 array->hi_extent - array->low_extent].mpf = NULL; 449 if (array->low_extent != array->hi_extent) 450 array->low_extent++; 451 } else { 452 if (extid == array->hi_extent) 453 array->hi_extent--; 454 } 455 456err: MUTEX_UNLOCK(env, dbp->mutex); 457 458 return (ret); 459} 460 461/* 462 * __qam_sync -- 463 * Flush the database cache. 464 * 465 * PUBLIC: int __qam_sync __P((DB *)); 466 */ 467int 468__qam_sync(dbp) 469 DB *dbp; 470{ 471 int ret; 472 /* 473 * We can't easily identify the extent files associated with a specific 474 * Queue file, so flush all Queue extent files. 475 */ 476 if ((ret = __memp_fsync(dbp->mpf)) != 0) 477 return (ret); 478 if (((QUEUE *)dbp->q_internal)->page_ext != 0) 479 return (__memp_sync_int( 480 dbp->env, NULL, 0, DB_SYNC_QUEUE_EXTENT, NULL, NULL)); 481 return (0); 482} 483 484/* 485 * __qam_gen_filelist -- generate a list of extent files. 486 * Another thread may close the handle so this should only 487 * be used single threaded or with care. 488 * 489 * PUBLIC: int __qam_gen_filelist __P((DB *, 490 * PUBLIC: DB_THREAD_INFO *, QUEUE_FILELIST **)); 491 */ 492int 493__qam_gen_filelist(dbp, ip, filelistp) 494 DB *dbp; 495 DB_THREAD_INFO *ip; 496 QUEUE_FILELIST **filelistp; 497{ 498 DBC *dbc; 499 DB_MPOOLFILE *mpf; 500 ENV *env; 501 QMETA *meta; 502 QUEUE *qp; 503 size_t extent_cnt; 504 db_recno_t i, current, first, stop, rec_extent; 505 QUEUE_FILELIST *fp; 506 int ret; 507 508 env = dbp->env; 509 mpf = dbp->mpf; 510 qp = (QUEUE *)dbp->q_internal; 511 *filelistp = NULL; 512 513 if (qp->page_ext == 0) 514 return (0); 515 516 /* This may happen during metapage recovery. */ 517 if (qp->name == NULL) 518 return (0); 519 520 /* Find out the first and last record numbers in the database. */ 521 i = PGNO_BASE_MD; 522 if ((ret = __memp_fget(mpf, &i, ip, NULL, 0, &meta)) != 0) 523 return (ret); 524 525 current = meta->cur_recno; 526 first = meta->first_recno; 527 528 if ((ret = __memp_fput(mpf, ip, meta, dbp->priority)) != 0) 529 return (ret); 530 531 /* 532 * Allocate the extent array. Calculate the worst case number of 533 * pages and convert that to a count of extents. The count of 534 * extents has 3 or 4 extra slots: 535 * roundoff at first (e.g., current record in extent); 536 * roundoff at current (e.g., first record in extent); 537 * NULL termination; and 538 * UINT32_MAX wraparound (the last extent can be small). 539 */ 540 rec_extent = qp->rec_page * qp->page_ext; 541 if (current >= first) 542 extent_cnt = (current - first) / rec_extent + 3; 543 else 544 extent_cnt = 545 (current + (UINT32_MAX - first)) / rec_extent + 4; 546 547 if (extent_cnt == 0) 548 return (0); 549 if ((ret = __os_calloc(env, 550 extent_cnt, sizeof(QUEUE_FILELIST), filelistp)) != 0) 551 return (ret); 552 fp = *filelistp; 553 if ((ret = __db_cursor(dbp, ip, NULL, &dbc, 0)) != 0) 554 return (ret); 555 556again: 557 if (current >= first) 558 stop = current; 559 else 560 stop = UINT32_MAX; 561 562 /* 563 * Make sure that first is at the same offset in the extent as stop. 564 * This guarantees that the stop will be reached in the loop below, 565 * even if it is the only record in its extent. This calculation is 566 * safe because first won't move out of its extent. 567 */ 568 first -= first % rec_extent; 569 first += stop % rec_extent; 570 571 for (i = first; i >= first && i <= stop; i += rec_extent) { 572 if ((ret = __qam_fprobe(dbc, QAM_RECNO_PAGE(dbp, i), 573 &fp->mpf, QAM_PROBE_MPF, dbp->priority, 0)) != 0) { 574 if (ret == ENOENT) 575 continue; 576 goto err; 577 } 578 fp->id = QAM_RECNO_EXTENT(dbp, i); 579 fp++; 580 DB_ASSERT(env, (size_t)(fp - *filelistp) < extent_cnt); 581 } 582 583 if (current < first) { 584 first = 1; 585 goto again; 586 } 587 588err: (void)__dbc_close(dbc); 589 return (ret); 590} 591 592/* 593 * __qam_extent_names -- generate a list of extent files names. 594 * 595 * PUBLIC: int __qam_extent_names __P((ENV *, char *, char ***)); 596 */ 597int 598__qam_extent_names(env, name, namelistp) 599 ENV *env; 600 char *name; 601 char ***namelistp; 602{ 603 DB *dbp; 604 DB_THREAD_INFO *ip; 605 QUEUE *qp; 606 QUEUE_FILELIST *filelist, *fp; 607 size_t len; 608 int cnt, ret, t_ret; 609 char buf[DB_MAXPATHLEN], **cp, *freep; 610 611 *namelistp = NULL; 612 filelist = NULL; 613 ENV_GET_THREAD_INFO(env, ip); 614 if ((ret = __db_create_internal(&dbp, env, 0)) != 0) 615 return (ret); 616 if ((ret = __db_open(dbp, ip, 617 NULL, name, NULL, DB_QUEUE, DB_RDONLY, 0, PGNO_BASE_MD)) != 0) 618 goto done; 619 qp = dbp->q_internal; 620 if (qp->page_ext == 0) 621 goto done; 622 623 if ((ret = __qam_gen_filelist(dbp, ip, &filelist)) != 0) 624 goto done; 625 626 if (filelist == NULL) 627 goto done; 628 629 cnt = 0; 630 for (fp = filelist; fp->mpf != NULL; fp++) 631 cnt++; 632 633 /* QUEUE_EXTENT contains extra chars, but add 6 anyway for the int. */ 634 len = (size_t)cnt * (sizeof(**namelistp) + 635 strlen(QUEUE_EXTENT) + strlen(qp->dir) + strlen(qp->name) + 6); 636 637 if ((ret = __os_malloc(dbp->env, len, namelistp)) != 0) 638 goto done; 639 cp = *namelistp; 640 freep = (char *)(cp + cnt + 1); 641 for (fp = filelist; fp->mpf != NULL; fp++) { 642 QAM_EXNAME(qp, fp->id, buf, sizeof(buf)); 643 len = strlen(buf); 644 *cp++ = freep; 645 (void)strcpy(freep, buf); 646 freep += len + 1; 647 } 648 *cp = NULL; 649 650done: 651 if (filelist != NULL) 652 __os_free(dbp->env, filelist); 653 if ((t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0) 654 ret = t_ret; 655 656 return (ret); 657} 658 659/* 660 * __qam_exid -- 661 * Generate a fileid for an extent based on the fileid of the main 662 * file. Since we do not log schema creates/deletes explicitly, the log 663 * never captures the fileid of an extent file. In order that masters and 664 * replicas have the same fileids (so they can explicitly delete them), we 665 * use computed fileids for the extent files of Queue files. 666 * 667 * An extent file id retains the low order 12 bytes of the file id and 668 * overwrites the dev/inode fields, placing a 0 in the inode field, and 669 * the extent number in the dev field. 670 * 671 * PUBLIC: void __qam_exid __P((DB *, u_int8_t *, u_int32_t)); 672 */ 673void 674__qam_exid(dbp, fidp, exnum) 675 DB *dbp; 676 u_int8_t *fidp; 677 u_int32_t exnum; 678{ 679 int i; 680 u_int8_t *p; 681 682 /* Copy the fileid from the master. */ 683 memcpy(fidp, dbp->fileid, DB_FILE_ID_LEN); 684 685 /* The first four bytes are the inode or the FileIndexLow; 0 it. */ 686 for (i = sizeof(u_int32_t); i > 0; --i) 687 *fidp++ = 0; 688 689 /* The next four bytes are the dev/FileIndexHigh; insert the exnum . */ 690 for (p = (u_int8_t *)&exnum, i = sizeof(u_int32_t); i > 0; --i) 691 *fidp++ = *p++; 692} 693 694/* 695 * __qam_nameop -- 696 * Remove or rename extent files associated with a particular file. 697 * This is to remove or rename (both in mpool and the file system) any 698 * extent files associated with the given dbp. 699 * This is either called from the QUEUE remove or rename methods or 700 * when undoing a transaction that created the database. 701 * 702 * PUBLIC: int __qam_nameop __P((DB *, DB_TXN *, const char *, qam_name_op)); 703 */ 704int 705__qam_nameop(dbp, txn, newname, op) 706 DB *dbp; 707 DB_TXN *txn; 708 const char *newname; 709 qam_name_op op; 710{ 711 ENV *env; 712 QUEUE *qp; 713 size_t exlen, fulllen, len; 714 u_int8_t fid[DB_FILE_ID_LEN]; 715 u_int32_t exid; 716 int cnt, i, ret, t_ret; 717 char buf[DB_MAXPATHLEN], nbuf[DB_MAXPATHLEN], sepsave; 718 char *endname, *endpath, *exname, *fullname, **names; 719 char *ndir, *namep, *new, *cp; 720 721 env = dbp->env; 722 qp = (QUEUE *)dbp->q_internal; 723 cnt = ret = t_ret = 0; 724 namep = exname = fullname = NULL; 725 names = NULL; 726 727 /* If this isn't a queue with extents, we're done. */ 728 if (qp->page_ext == 0) 729 return (0); 730 731 /* 732 * Generate the list of all queue extents for this file (from the 733 * file system) and then cycle through removing them and evicting 734 * from mpool. We have two modes of operation here. If we are 735 * undoing log operations, then do not write log records and try 736 * to keep going even if we encounter failures in nameop. If we 737 * are in mainline code, then return as soon as we have a problem. 738 * Memory allocation errors (__db_appname, __os_malloc) are always 739 * considered failure. 740 * 741 * Set buf to : dir/__dbq.NAME.0 and fullname to HOME/dir/__dbq.NAME.0 742 * or, in the case of an absolute path: /dir/__dbq.NAME.0 743 */ 744 QAM_EXNAME(qp, 0, buf, sizeof(buf)); 745 if ((ret = 746 __db_appname(env, DB_APP_DATA, buf, 0, NULL, &fullname)) != 0) 747 return (ret); 748 749 /* We should always have a path separator here. */ 750 if ((endpath = __db_rpath(fullname)) == NULL) { 751 ret = EINVAL; 752 goto err; 753 } 754 sepsave = *endpath; 755 *endpath = '\0'; 756 757 /* 758 * Get the list of all names in the directory and restore the 759 * path separator. 760 */ 761 if ((ret = __os_dirlist(env, fullname, 0, &names, &cnt)) != 0) 762 goto err; 763 *endpath = sepsave; 764 765 /* If there aren't any names, don't allocate any space. */ 766 if (cnt == 0) 767 goto err; 768 769 /* 770 * Now, make endpath reference the queue extent names upon which 771 * we can match. Then we set the end of the path to be the 772 * beginning of the extent number, and we can compare the bytes 773 * between endpath and endname (__dbq.NAME.). 774 */ 775 endpath++; 776 endname = strrchr(endpath, '.'); 777 if (endname == NULL) { 778 ret = EINVAL; 779 goto err; 780 } 781 ++endname; 782 *endname = '\0'; 783 len = strlen(endpath); 784 fulllen = strlen(fullname); 785 786 /* Allocate space for a full extent name. */ 787 exlen = fulllen + 20; 788 if ((ret = __os_malloc(env, exlen, &exname)) != 0) 789 goto err; 790 791 ndir = new = NULL; 792 if (newname != NULL) { 793 if ((ret = __os_strdup(env, newname, &namep)) != 0) 794 goto err; 795 ndir = namep; 796 if ((new = __db_rpath(namep)) != NULL) 797 *new++ = '\0'; 798 else { 799 new = namep; 800 ndir = PATH_DOT; 801 } 802 } 803 for (i = 0; i < cnt; i++) { 804 /* Check if this is a queue extent file. */ 805 if (strncmp(names[i], endpath, len) != 0) 806 continue; 807 /* Make sure we have all numbers. foo.db vs. foo.db.0. */ 808 for (cp = &names[i][len]; *cp != '\0'; cp++) 809 if (!isdigit((int)*cp)) 810 break; 811 if (*cp != '\0') 812 continue; 813 814 /* 815 * We have a queue extent file. We need to generate its 816 * name and its fileid. 817 */ 818 exid = (u_int32_t)strtoul(names[i] + len, NULL, 10); 819 __qam_exid(dbp, fid, exid); 820 821 switch (op) { 822 case QAM_NAME_DISCARD: 823 snprintf(exname, exlen, 824 "%s%s", fullname, names[i] + len); 825 if ((t_ret = __memp_nameop(dbp->env, 826 fid, NULL, exname, NULL, 827 F_ISSET(dbp, DB_AM_INMEM))) != 0 && ret == 0) 828 ret = t_ret; 829 break; 830 831 case QAM_NAME_RENAME: 832 snprintf(nbuf, sizeof(nbuf), QUEUE_EXTENT, 833 ndir, PATH_SEPARATOR[0], new, exid); 834 QAM_EXNAME(qp, exid, buf, sizeof(buf)); 835 if ((ret = __fop_rename(env, 836 txn, buf, nbuf, fid, DB_APP_DATA, 1, 837 F_ISSET(dbp, DB_AM_NOT_DURABLE) ? 838 DB_LOG_NOT_DURABLE : 0)) != 0) 839 goto err; 840 break; 841 842 case QAM_NAME_REMOVE: 843 QAM_EXNAME(qp, exid, buf, sizeof(buf)); 844 if ((ret = __fop_remove(env, txn, fid, buf, 845 DB_APP_DATA, F_ISSET(dbp, DB_AM_NOT_DURABLE) ? 846 DB_LOG_NOT_DURABLE : 0)) != 0) 847 goto err; 848 break; 849 } 850 } 851 852err: if (fullname != NULL) 853 __os_free(env, fullname); 854 if (exname != NULL) 855 __os_free(env, exname); 856 if (namep != NULL) 857 __os_free(env, namep); 858 if (names != NULL) 859 __os_dirfree(env, names, cnt); 860 return (ret); 861} 862