diff options
Diffstat (limited to 'src/qam/qam_files.c')
-rw-r--r-- | src/qam/qam_files.c | 939 |
1 files changed, 939 insertions, 0 deletions
diff --git a/src/qam/qam_files.c b/src/qam/qam_files.c new file mode 100644 index 00000000..e9a9ff07 --- /dev/null +++ b/src/qam/qam_files.c @@ -0,0 +1,939 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999, 2012 Oracle and/or its affiliates. All rights reserved. + * + * $Id$ + */ + +#include "db_config.h" + +#include "db_int.h" +#include "dbinc/db_page.h" +#include "dbinc/db_am.h" +#include "dbinc/fop.h" +#include "dbinc/mp.h" +#include "dbinc/qam.h" + +#define QAM_EXNAME(Q, I, B, L) \ + snprintf((B), (L), \ + QUEUE_EXTENT, (Q)->dir, PATH_SEPARATOR[0], (Q)->name, (I)) + +/* + * __qam_fprobe -- calculate and open extent + * + * Calculate which extent the page is in, open and create if necessary. + * + * PUBLIC: int __qam_fprobe __P((DBC *, db_pgno_t, + * PUBLIC: void *, qam_probe_mode, DB_CACHE_PRIORITY, u_int32_t)); + */ +int +__qam_fprobe(dbc, pgno, addrp, mode, priority, flags) + DBC *dbc; + db_pgno_t pgno; + void *addrp; + qam_probe_mode mode; + DB_CACHE_PRIORITY priority; + u_int32_t flags; +{ + DB *dbp; + DB_MPOOLFILE *mpf; + ENV *env; + MPFARRAY *array; + QUEUE *qp; + u_int8_t fid[DB_FILE_ID_LEN]; + u_int32_t i, extid, maxext, numext, lflags, offset, oldext, openflags; + char buf[DB_MAXPATHLEN]; + int ftype, less, ret, t_ret; + + dbp = dbc->dbp; + env = dbp->env; + qp = (QUEUE *)dbp->q_internal; + ret = 0; + + if (qp->page_ext == 0) { + mpf = dbp->mpf; + switch (mode) { + case QAM_PROBE_GET: + return (__memp_fget(mpf, &pgno, + dbc->thread_info, dbc->txn, flags, addrp)); + case QAM_PROBE_PUT: + return (__memp_fput(mpf, + dbc->thread_info, addrp, priority)); + case QAM_PROBE_DIRTY: + return (__memp_dirty(mpf, addrp, + dbc->thread_info, dbc->txn, priority, flags)); + case QAM_PROBE_MPF: + *(DB_MPOOLFILE **)addrp = mpf; + return (0); + } + } + + mpf = NULL; + + /* + * Need to lock long enough to find the mpf or create the file. + * The file cannot go away because we must have a record locked + * in that file. + */ + MUTEX_LOCK(env, dbp->mutex); + extid = QAM_PAGE_EXTENT(dbp, pgno); + + /* Array1 will always be in use if array2 is in use. */ + array = &qp->array1; + if (array->n_extent == 0) { + /* Start with 4 extents */ + array->n_extent = 4; + array->low_extent = extid; + numext = offset = oldext = 0; + less = 0; + goto alloc; + } + +retry: + if (extid < array->low_extent) { + less = 1; + offset = array->low_extent - extid; + } else { + less = 0; + offset = extid - array->low_extent; + } + if (qp->array2.n_extent != 0 && + (extid >= qp->array2.low_extent ? + offset > extid - qp->array2.low_extent : + offset > qp->array2.low_extent - extid)) { + array = &qp->array2; + if (extid < array->low_extent) { + less = 1; + offset = array->low_extent - extid; + } else { + less = 0; + offset = extid - array->low_extent; + } + } + + /* + * Check to see if the requested extent is outside the range of + * extents in the array. This is true by default if there are + * no extents here yet. + */ + if (less == 1 || offset >= array->n_extent) { + oldext = array->n_extent; + numext = (array->hi_extent - array->low_extent) + 1; + if (less == 1 && offset + numext <= array->n_extent) { + /* + * If we can fit this one into the existing array by + * shifting the existing entries then we do not have + * to allocate. + */ + memmove(&array->mpfarray[offset], + array->mpfarray, numext + * sizeof(array->mpfarray[0])); + memset(array->mpfarray, 0, offset + * sizeof(array->mpfarray[0])); + offset = 0; + } else if (less == 0 && offset == array->n_extent && + (mode == QAM_PROBE_GET || mode == QAM_PROBE_PUT) && + array->mpfarray[0].pinref == 0) { + /* + * If this is at the end of the array and the file at + * the beginning has a zero pin count we can close + * the bottom extent and put this one at the end. + */ + mpf = array->mpfarray[0].mpf; + if (mpf != NULL && (ret = __memp_fclose(mpf, 0)) != 0) + goto err; + memmove(&array->mpfarray[0], &array->mpfarray[1], + (array->n_extent - 1) * sizeof(array->mpfarray[0])); + array->low_extent++; + array->hi_extent++; + offset--; + array->mpfarray[offset].mpf = NULL; + array->mpfarray[offset].pinref = 0; + } else { + /* + * See if we have wrapped around the queue. + * If it has then allocate the second array. + * Otherwise just expand the one we are using. + */ + maxext = (u_int32_t) UINT32_MAX + / (qp->page_ext * qp->rec_page); + if (offset >= maxext/2) { + array = &qp->array2; + DB_ASSERT(env, array->n_extent == 0); + oldext = 0; + array->n_extent = 4; + array->low_extent = extid; + offset = 0; + numext = 0; + } else if (array->mpfarray[0].pinref == 0) { + /* + * Check to see if there are extents marked + * for deletion at the beginning of the cache. + * If so close them so they will go away. + */ + for (i = 0; i < array->n_extent; i++) { + if (array->mpfarray[i].pinref != 0) + break; + mpf = array->mpfarray[i].mpf; + if (mpf == NULL) + continue; + (void)__memp_get_flags(mpf, &lflags); + if (!FLD_ISSET(lflags, DB_MPOOL_UNLINK)) + break; + + array->mpfarray[i].mpf = NULL; + if ((ret = __memp_fclose(mpf, 0)) != 0) + goto err; + } + if (i == 0) + goto increase; + memmove(&array->mpfarray[0], + &array->mpfarray[i], + (array->n_extent - i) * + sizeof(array->mpfarray[0])); + memset(&array->mpfarray[array->n_extent - i], + '\0', i * sizeof(array->mpfarray[0])); + array->low_extent += i; + array->hi_extent += i; + goto retry; + } else { + /* + * Increase the size to at least include + * the new one and double it. + */ +increase: array->n_extent += offset; + array->n_extent <<= 2; + } +alloc: if ((ret = __os_realloc(env, + array->n_extent * sizeof(struct __qmpf), + &array->mpfarray)) != 0) + goto err; + + if (less == 1) { + /* + * Move the array up and put the new one + * in the first slot. + */ + memmove(&array->mpfarray[offset], + array->mpfarray, + numext * sizeof(array->mpfarray[0])); + memset(array->mpfarray, 0, + offset * sizeof(array->mpfarray[0])); + memset(&array->mpfarray[numext + offset], 0, + (array->n_extent - (numext + offset)) + * sizeof(array->mpfarray[0])); + offset = 0; + } + else + /* Clear the new part of the array. */ + memset(&array->mpfarray[oldext], 0, + (array->n_extent - oldext) * + sizeof(array->mpfarray[0])); + } + } + + /* Update the low and hi range of saved extents. */ + if (extid < array->low_extent) + array->low_extent = extid; + if (extid > array->hi_extent) + array->hi_extent = extid; + + /* If the extent file is not yet open, open it. */ + if (array->mpfarray[offset].mpf == NULL) { + QAM_EXNAME(qp, extid, buf, sizeof(buf)); + if ((ret = __memp_fcreate( + env, &array->mpfarray[offset].mpf)) != 0) + goto err; + mpf = array->mpfarray[offset].mpf; + (void)__memp_set_lsn_offset(mpf, 0); + (void)__memp_set_pgcookie(mpf, &qp->pgcookie); + (void)__memp_get_ftype(dbp->mpf, &ftype); + (void)__memp_set_ftype(mpf, ftype); + (void)__memp_set_clear_len(mpf, dbp->pgsize); + + /* Set up the fileid for this extent. */ + __qam_exid(dbp, fid, extid); + (void)__memp_set_fileid(mpf, fid); + openflags = DB_EXTENT; + if (LF_ISSET(DB_MPOOL_CREATE)) + openflags |= DB_CREATE; + if (F_ISSET(dbp, DB_AM_RDONLY)) + openflags |= DB_RDONLY; + if (F_ISSET(env->dbenv, DB_ENV_DIRECT_DB)) + openflags |= DB_DIRECT; + if ((ret = __memp_fopen(mpf, NULL, + buf, NULL, openflags, qp->mode, dbp->pgsize)) != 0) { + array->mpfarray[offset].mpf = NULL; + (void)__memp_fclose(mpf, 0); + goto err; + } + } + + /* + * We have found the right file. Update its ref count + * before dropping the dbp mutex so it does not go away. + */ + mpf = array->mpfarray[offset].mpf; + if (mode == QAM_PROBE_GET) + array->mpfarray[offset].pinref++; + + /* + * If we may create the page, then we are writing, + * the file may nolonger be empty after this operation + * so we clear the UNLINK flag. + */ + if (LF_ISSET(DB_MPOOL_CREATE)) + (void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 0); + +err: + MUTEX_UNLOCK(env, dbp->mutex); + + if (ret == 0) { + pgno--; + pgno %= qp->page_ext; + switch (mode) { + case QAM_PROBE_GET: + ret = __memp_fget(mpf, &pgno, + dbc->thread_info, dbc->txn, flags, addrp); + if (ret == 0) + return (0); + break; + case QAM_PROBE_PUT: + ret = __memp_fput(mpf, + dbc->thread_info, addrp, dbp->priority); + break; + case QAM_PROBE_DIRTY: + return (__memp_dirty(mpf, addrp, + dbc->thread_info, dbc->txn, dbp->priority, flags)); + case QAM_PROBE_MPF: + *(DB_MPOOLFILE **)addrp = mpf; + return (0); + } + + MUTEX_LOCK(env, dbp->mutex); + /* Recalculate because we dropped the lock. */ + offset = extid - array->low_extent; + DB_ASSERT(env, array->mpfarray[offset].pinref > 0); + if (--array->mpfarray[offset].pinref == 0 && + (mode == QAM_PROBE_GET || ret == 0)) { + /* Check to see if this file will be unlinked. */ + (void)__memp_get_flags(mpf, &flags); + if (LF_ISSET(DB_MPOOL_UNLINK)) { + array->mpfarray[offset].mpf = NULL; + if ((t_ret = + __memp_fclose(mpf, 0)) != 0 && ret == 0) + ret = t_ret; + } + } + MUTEX_UNLOCK(env, dbp->mutex); + } + return (ret); +} + +/* + * __qam_fclose -- close an extent. + * + * Calculate which extent the page is in and close it. + * We assume the mpf entry is present. + * + * PUBLIC: int __qam_fclose __P((DB *, db_pgno_t)); + */ +int +__qam_fclose(dbp, pgnoaddr) + DB *dbp; + db_pgno_t pgnoaddr; +{ + DB_MPOOLFILE *mpf; + ENV *env; + MPFARRAY *array; + QUEUE *qp; + u_int32_t extid, offset; + int ret; + + ret = 0; + env = dbp->env; + qp = (QUEUE *)dbp->q_internal; + + MUTEX_LOCK(env, dbp->mutex); + + extid = QAM_PAGE_EXTENT(dbp, pgnoaddr); + array = &qp->array1; + if (array->low_extent > extid || array->hi_extent < extid) + array = &qp->array2; + offset = extid - array->low_extent; + + DB_ASSERT(env, + extid >= array->low_extent && offset < array->n_extent); + + /* If other threads are still using this file, leave it. */ + if (array->mpfarray[offset].pinref != 0) + goto done; + + mpf = array->mpfarray[offset].mpf; + array->mpfarray[offset].mpf = NULL; + ret = __memp_fclose(mpf, 0); + +done: + MUTEX_UNLOCK(env, dbp->mutex); + return (ret); +} + +/* + * __qam_fremove -- remove an extent. + * + * Calculate which extent the page is in and remove it. There is no way + * to remove an extent without probing it first and seeing that is is empty + * so we assume the mpf entry is present. + * + * PUBLIC: int __qam_fremove __P((DB *, db_pgno_t)); + */ +int +__qam_fremove(dbp, pgnoaddr) + DB *dbp; + db_pgno_t pgnoaddr; +{ + DB_MPOOLFILE *mpf; + ENV *env; + MPFARRAY *array; + QUEUE *qp; + u_int32_t extid, offset; + int ret; + + qp = (QUEUE *)dbp->q_internal; + env = dbp->env; + ret = 0; + + MUTEX_LOCK(env, dbp->mutex); + + extid = QAM_PAGE_EXTENT(dbp, pgnoaddr); + array = &qp->array1; + if (array->low_extent > extid || array->hi_extent < extid) + array = &qp->array2; + offset = extid - array->low_extent; + + DB_ASSERT(env, + extid >= array->low_extent && offset < array->n_extent); + + mpf = array->mpfarray[offset].mpf; + /* This extent my already be marked for delete and closed. */ + if (mpf == NULL) + goto err; + + /* + * The log must be flushed before the file is deleted. We depend on + * the log record of the last delete to recreate the file if we crash. + */ + if (LOGGING_ON(env) && (ret = __log_flush(env, NULL)) != 0) + goto err; + + (void)__memp_set_flags(mpf, DB_MPOOL_UNLINK, 1); + /* Someone could be real slow, let them close it down. */ + if (array->mpfarray[offset].pinref != 0) + goto err; + array->mpfarray[offset].mpf = NULL; + if ((ret = __memp_fclose(mpf, 0)) != 0) + goto err; + + /* + * If the file is at the bottom of the array + * shift things down and adjust the end points. + */ + if (offset == 0) { + memmove(array->mpfarray, &array->mpfarray[1], + (array->hi_extent - array->low_extent) + * sizeof(array->mpfarray[0])); + array->mpfarray[ + array->hi_extent - array->low_extent].mpf = NULL; + if (array->low_extent != array->hi_extent) + array->low_extent++; + } else { + if (extid == array->hi_extent) + array->hi_extent--; + } + +err: MUTEX_UNLOCK(env, dbp->mutex); + + return (ret); +} + +/* + * __qam_sync -- + * Flush the database cache. + * + * PUBLIC: int __qam_sync __P((DB *)); + */ +int +__qam_sync(dbp) + DB *dbp; +{ + int ret; + /* + * We can't easily identify the extent files associated with a specific + * Queue file, so flush all Queue extent files. + */ + if ((ret = __memp_fsync(dbp->mpf)) != 0) + return (ret); + if (((QUEUE *)dbp->q_internal)->page_ext != 0) + return (__memp_sync_int( + dbp->env, NULL, 0, DB_SYNC_QUEUE_EXTENT, NULL, NULL)); + return (0); +} + +/* + * __qam_gen_filelist -- generate a list of extent files. + * Another thread may close the handle so this should only + * be used single threaded or with care. + * + * PUBLIC: int __qam_gen_filelist __P((DB *, + * PUBLIC: DB_THREAD_INFO *, QUEUE_FILELIST **)); + */ +int +__qam_gen_filelist(dbp, ip, filelistp) + DB *dbp; + DB_THREAD_INFO *ip; + QUEUE_FILELIST **filelistp; +{ + DBC *dbc; + DB_MPOOLFILE *mpf; + ENV *env; + QMETA *meta; + QUEUE *qp; + size_t extent_cnt; + db_recno_t i, current, first, stop, rec_extent; + QUEUE_FILELIST *fp; + int ret; + + env = dbp->env; + mpf = dbp->mpf; + qp = (QUEUE *)dbp->q_internal; + *filelistp = NULL; + + if (qp->page_ext == 0) + return (0); + + /* This may happen during metapage recovery. */ + if (qp->name == NULL) + return (0); + + /* Find out the first and last record numbers in the database. */ + i = PGNO_BASE_MD; + if ((ret = __memp_fget(mpf, &i, ip, NULL, 0, &meta)) != 0) + return (ret); + + current = meta->cur_recno; + first = meta->first_recno; + + if ((ret = __memp_fput(mpf, ip, meta, dbp->priority)) != 0) + return (ret); + + /* + * Allocate the extent array. Calculate the worst case number of + * pages and convert that to a count of extents. The count of + * extents has 3 or 4 extra slots: + * roundoff at first (e.g., current record in extent); + * roundoff at current (e.g., first record in extent); + * NULL termination; and + * UINT32_MAX wraparound (the last extent can be small). + */ + rec_extent = qp->rec_page * qp->page_ext; + if (current >= first) + extent_cnt = (current - first) / rec_extent + 3; + else + extent_cnt = + (current + (UINT32_MAX - first)) / rec_extent + 4; + + if (extent_cnt == 0) + return (0); + if ((ret = __os_calloc(env, + extent_cnt, sizeof(QUEUE_FILELIST), filelistp)) != 0) + return (ret); + fp = *filelistp; + if ((ret = __db_cursor(dbp, ip, NULL, &dbc, 0)) != 0) + return (ret); + +again: + if (current >= first) + stop = current; + else + stop = UINT32_MAX; + + /* + * Make sure that first is at the same offset in the extent as stop. + * This guarantees that the stop will be reached in the loop below, + * even if it is the only record in its extent. This calculation is + * safe because first won't move out of its extent. + */ + first -= first % rec_extent; + first += stop % rec_extent; + + for (i = first; i >= first && i <= stop; i += rec_extent) { + if ((ret = __qam_fprobe(dbc, QAM_RECNO_PAGE(dbp, i), + &fp->mpf, QAM_PROBE_MPF, dbp->priority, 0)) != 0) { + if (ret == ENOENT) + continue; + goto err; + } + fp->id = QAM_RECNO_EXTENT(dbp, i); + fp++; + DB_ASSERT(env, (size_t)(fp - *filelistp) < extent_cnt); + } + + if (current < first) { + first = 1; + goto again; + } + +err: (void)__dbc_close(dbc); + return (ret); +} + +/* + * __qam_extent_names -- generate a list of extent files names. + * + * PUBLIC: int __qam_extent_names __P((ENV *, char *, char ***)); + */ +int +__qam_extent_names(env, name, namelistp) + ENV *env; + char *name; + char ***namelistp; +{ + DB *dbp; + DB_THREAD_INFO *ip; + QUEUE *qp; + QUEUE_FILELIST *filelist, *fp; + size_t len; + int cnt, ret, t_ret; + char buf[DB_MAXPATHLEN], **cp, *freep; + + *namelistp = NULL; + filelist = NULL; + ENV_GET_THREAD_INFO(env, ip); + if ((ret = __db_create_internal(&dbp, env, 0)) != 0) + return (ret); + if ((ret = __db_open(dbp, ip, + NULL, name, NULL, DB_QUEUE, DB_RDONLY, 0, PGNO_BASE_MD)) != 0) + goto done; + qp = dbp->q_internal; + if (qp->page_ext == 0) + goto done; + + if ((ret = __qam_gen_filelist(dbp, ip, &filelist)) != 0) + goto done; + + if (filelist == NULL) + goto done; + + cnt = 0; + for (fp = filelist; fp->mpf != NULL; fp++) + cnt++; + + /* QUEUE_EXTENT contains extra chars, but add 6 anyway for the int. */ + len = (size_t)cnt * (sizeof(**namelistp) + + strlen(QUEUE_EXTENT) + strlen(qp->dir) + strlen(qp->name) + 6); + + if ((ret = __os_malloc(dbp->env, len, namelistp)) != 0) + goto done; + cp = *namelistp; + freep = (char *)(cp + cnt + 1); + for (fp = filelist; fp->mpf != NULL; fp++) { + QAM_EXNAME(qp, fp->id, buf, sizeof(buf)); + len = strlen(buf); + *cp++ = freep; + (void)strcpy(freep, buf); + freep += len + 1; + } + *cp = NULL; + +done: + if (filelist != NULL) + __os_free(dbp->env, filelist); + if ((t_ret = __db_close(dbp, NULL, DB_NOSYNC)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} + +/* + * __qam_exid -- + * Generate a fileid for an extent based on the fileid of the main + * file. Since we do not log schema creates/deletes explicitly, the log + * never captures the fileid of an extent file. In order that masters and + * replicas have the same fileids (so they can explicitly delete them), we + * use computed fileids for the extent files of Queue files. + * + * An extent file id retains the low order 12 bytes of the file id and + * overwrites the dev/inode fields, placing a 0 in the inode field, and + * the extent number in the dev field. + * + * PUBLIC: void __qam_exid __P((DB *, u_int8_t *, u_int32_t)); + */ +void +__qam_exid(dbp, fidp, exnum) + DB *dbp; + u_int8_t *fidp; + u_int32_t exnum; +{ + int i; + u_int8_t *p; + + /* Copy the fileid from the master. */ + memcpy(fidp, dbp->fileid, DB_FILE_ID_LEN); + + /* The first four bytes are the inode or the FileIndexLow; 0 it. */ + for (i = sizeof(u_int32_t); i > 0; --i) + *fidp++ = 0; + + /* The next four bytes are the dev/FileIndexHigh; insert the exnum . */ + for (p = (u_int8_t *)&exnum, i = sizeof(u_int32_t); i > 0; --i) + *fidp++ = *p++; +} + +/* + * __qam_nameop -- + * Remove or rename extent files associated with a particular file. + * This is to remove or rename (both in mpool and the file system) any + * extent files associated with the given dbp. + * This is either called from the QUEUE remove or rename methods or + * when undoing a transaction that created the database. + * + * PUBLIC: int __qam_nameop __P((DB *, DB_TXN *, const char *, qam_name_op)); + */ +int +__qam_nameop(dbp, txn, newname, op) + DB *dbp; + DB_TXN *txn; + const char *newname; + qam_name_op op; +{ + ENV *env; + QUEUE *qp; + size_t exlen, fulllen, len; + u_int8_t fid[DB_FILE_ID_LEN]; + u_int32_t exid; + int cnt, i, ret, t_ret; + char buf[DB_MAXPATHLEN], nbuf[DB_MAXPATHLEN], sepsave; + char *endname, *endpath, *exname, *fullname, **names; + char *ndir, *namep, *new, *cp; + + env = dbp->env; + qp = (QUEUE *)dbp->q_internal; + cnt = ret = t_ret = 0; + namep = exname = fullname = NULL; + names = NULL; + + /* If this isn't a queue with extents, we're done. */ + if (qp->page_ext == 0) + return (0); + + /* + * Generate the list of all queue extents for this file (from the + * file system) and then cycle through removing them and evicting + * from mpool. We have two modes of operation here. If we are + * undoing log operations, then do not write log records and try + * to keep going even if we encounter failures in nameop. If we + * are in mainline code, then return as soon as we have a problem. + * Memory allocation errors (__db_appname, __os_malloc) are always + * considered failure. + * + * Set buf to : dir/__dbq.NAME.0 and fullname to HOME/dir/__dbq.NAME.0 + * or, in the case of an absolute path: /dir/__dbq.NAME.0 + */ + QAM_EXNAME(qp, 0, buf, sizeof(buf)); + if ((ret = __db_appname(env, + DB_APP_DATA, buf, &dbp->dirname, &fullname)) != 0) + return (ret); + + /* We should always have a path separator here. */ + if ((endpath = __db_rpath(fullname)) == NULL) { + ret = EINVAL; + goto err; + } + sepsave = *endpath; + *endpath = '\0'; + + /* + * Get the list of all names in the directory and restore the + * path separator. + */ + if ((ret = __os_dirlist(env, fullname, 0, &names, &cnt)) != 0) + goto err; + *endpath = sepsave; + + /* If there aren't any names, don't allocate any space. */ + if (cnt == 0) + goto err; + + /* + * Now, make endpath reference the queue extent names upon which + * we can match. Then we set the end of the path to be the + * beginning of the extent number, and we can compare the bytes + * between endpath and endname (__dbq.NAME.). + */ + endpath++; + endname = strrchr(endpath, '.'); + if (endname == NULL) { + ret = EINVAL; + goto err; + } + ++endname; + *endname = '\0'; + len = strlen(endpath); + fulllen = strlen(fullname); + + /* Allocate space for a full extent name. */ + exlen = fulllen + 20; + if ((ret = __os_malloc(env, exlen, &exname)) != 0) + goto err; + + ndir = new = NULL; + if (newname != NULL) { + if ((ret = __os_strdup(env, newname, &namep)) != 0) + goto err; + ndir = namep; + if ((new = __db_rpath(namep)) != NULL) + *new++ = '\0'; + else { + new = namep; + ndir = PATH_DOT; + } + } + for (i = 0; i < cnt; i++) { + /* Check if this is a queue extent file. */ + if (strncmp(names[i], endpath, len) != 0) + continue; + /* Make sure we have all numbers. foo.db vs. foo.db.0. */ + for (cp = &names[i][len]; *cp != '\0'; cp++) + if (!isdigit((int)*cp)) + break; + if (*cp != '\0') + continue; + + /* + * We have a queue extent file. We need to generate its + * name and its fileid. + */ + exid = (u_int32_t)strtoul(names[i] + len, NULL, 10); + __qam_exid(dbp, fid, exid); + + switch (op) { + case QAM_NAME_DISCARD: + snprintf(exname, exlen, + "%s%s", fullname, names[i] + len); + if ((t_ret = __memp_nameop(dbp->env, + fid, NULL, exname, NULL, + F_ISSET(dbp, DB_AM_INMEM))) != 0 && ret == 0) + ret = t_ret; + break; + + case QAM_NAME_RENAME: + snprintf(nbuf, sizeof(nbuf), QUEUE_EXTENT, + ndir, PATH_SEPARATOR[0], new, exid); + QAM_EXNAME(qp, exid, buf, sizeof(buf)); + if ((ret = __fop_rename(env, + txn, buf, nbuf, &dbp->dirname, fid, DB_APP_DATA, 1, + F_ISSET(dbp, DB_AM_NOT_DURABLE) ? + DB_LOG_NOT_DURABLE : 0)) != 0) + goto err; + break; + + case QAM_NAME_REMOVE: + QAM_EXNAME(qp, exid, buf, sizeof(buf)); + if ((ret = __fop_remove(env, txn, fid, + buf, &dbp->dirname, + DB_APP_DATA, F_ISSET(dbp, DB_AM_NOT_DURABLE) ? + DB_LOG_NOT_DURABLE : 0)) != 0) + goto err; + break; + } + } + +err: if (fullname != NULL) + __os_free(env, fullname); + if (exname != NULL) + __os_free(env, exname); + if (namep != NULL) + __os_free(env, namep); + if (names != NULL) + __os_dirfree(env, names, cnt); + return (ret); +} + +/* + * __qam_lsn_reset -- reset the lsns for extents. + * + * PUBLIC: int __qam_lsn_reset __P((DB *, DB_THREAD_INFO *)); + */ +int +__qam_lsn_reset(dbp, ip) + DB *dbp; + DB_THREAD_INFO *ip; +{ + QUEUE *qp; + QUEUE_FILELIST *filelist, *fp; + int ret; + + qp = dbp->q_internal; + if (qp->page_ext == 0) + return (0); + + if ((ret = __qam_gen_filelist(dbp, ip, &filelist)) != 0) + return (ret); + + if (filelist == NULL) + return (ret); + + for (fp = filelist; fp->mpf != NULL; fp++) + if ((ret = __db_lsn_reset(fp->mpf, ip)) != 0) + break; + + __os_free(dbp->env, filelist); + return (ret); +} + +/* + * __qam_backup_extents-- + * Routine to safely copy the active queue extents of a database. + * PUBLIC: int __qam_backup_extents __P((DB *, + * PUBLIC: DB_THREAD_INFO *, const char *, u_int32_t)); + */ +int +__qam_backup_extents(dbp, ip, target, flags) + DB *dbp; + DB_THREAD_INFO *ip; + const char *target; + u_int32_t flags; +{ + DB_FH *filep; + QUEUE *qp; + QUEUE_FILELIST *fp, *filelist; + int ret, t_ret; + char buf[DB_MAXPATHLEN]; + void *handle; + + if ((ret = __qam_gen_filelist(dbp, ip, &filelist)) != 0) + return (ret); + + if (filelist == NULL) + return (0); + + qp = dbp->q_internal; + + for (fp = filelist; fp->mpf != NULL; fp++) { + QAM_EXNAME(qp, fp->id, buf, sizeof(buf)); + if ((ret = __memp_backup_open(dbp->dbenv->env, + fp->mpf, buf, target, flags, &filep, &handle)) == 0) + ret = __memp_backup_mpf(dbp->dbenv->env, fp->mpf, ip, + 0, fp->mpf->mfp->last_pgno, filep, handle, flags); + if ((t_ret = __memp_backup_close(dbp->dbenv->env, + fp->mpf, buf, filep, handle)) != 0 && ret == 0) + ret = t_ret; + if (ret != 0) + break; + } + + __os_free(dbp->env, filelist); + + return (ret); +} |