You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
7.6 KiB

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998, 1999, 2000
* Sleepycat Software. All rights reserved.
*/
#include "htconfig.h"
#ifndef lint
static const char revid[] = "$Id: db_upg.c,v 1.2 2002/02/02 18:18:05 ghutchis Exp $";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#endif
#include "db_int.h"
#include "db_page.h"
#include "db_swap.h"
#include "btree.h"
#include "hash.h"
#include "qam.h"
static int (* const func_31_list[P_PAGETYPE_MAX])
__P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *)) = {
NULL, /* P_INVALID */
NULL, /* __P_DUPLICATE */
CDB___ham_31_hash, /* P_HASH */
NULL, /* P_IBTREE */
NULL, /* P_IRECNO */
CDB___bam_31_lbtree, /* P_LBTREE */
NULL, /* P_LRECNO */
NULL, /* P_OVERFLOW */
CDB___ham_31_hashmeta, /* P_HASHMETA */
CDB___bam_31_btreemeta, /* P_BTREEMETA */
};
static int __db_page_pass __P((DB *, char *, u_int32_t, int (* const [])
(DB *, char *, u_int32_t, DB_FH *, PAGE *, int *), DB_FH *));
/*
* CDB___db_upgrade --
* Upgrade an existing database.
*
* PUBLIC: int CDB___db_upgrade __P((DB *, const char *, u_int32_t));
*/
int
CDB___db_upgrade(dbp, fname, flags)
DB *dbp;
const char *fname;
u_int32_t flags;
{
DB_ENV *dbenv;
DB_FH fh;
size_t n;
int ret, t_ret;
u_int8_t mbuf[256];
char *real_name;
dbenv = dbp->dbenv;
/* Validate arguments. */
if ((ret = CDB___db_fchk(dbenv, "DB->upgrade", flags, DB_DUPSORT)) != 0)
return (ret);
/* Get the real backing file name. */
if ((ret = CDB___db_appname(dbenv,
DB_APP_DATA, NULL, fname, 0, NULL, &real_name)) != 0)
return (ret);
/* Open the file. */
if ((ret = CDB___os_open(dbenv, real_name, 0, 0, &fh)) != 0) {
CDB___db_err(dbenv, "%s: %s", real_name, CDB_db_strerror(ret));
return (ret);
}
/* Initialize the feedback. */
if (dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_UPGRADE, 0);
/*
* Read the metadata page. We read 256 bytes, which is larger than
* any access method's metadata page and smaller than any disk sector.
*/
if ((ret = CDB___os_read(dbenv, &fh, mbuf, sizeof(mbuf), &n)) != 0)
goto err;
switch (((DBMETA *)mbuf)->magic) {
case DB_BTREEMAGIC:
switch (((DBMETA *)mbuf)->version) {
case 6:
/*
* Before V7 not all pages had page types, so we do the
* single meta-data page by hand.
*/
if ((ret =
CDB___bam_30_btreemeta(dbp, real_name, mbuf)) != 0)
goto err;
if ((ret = CDB___os_seek(dbenv,
&fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0)
goto err;
if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0)
goto err;
/* FALLTHROUGH */
case 7:
/*
* We need the page size to do more. Rip it out of
* the meta-data page.
*/
memcpy(&dbp->pgsize, mbuf + 20, sizeof(u_int32_t));
if ((ret = __db_page_pass(
dbp, real_name, flags, func_31_list, &fh)) != 0)
goto err;
/* FALLTHROUGH */
case 8:
break;
default:
CDB___db_err(dbenv, "%s: unsupported btree version: %lu",
real_name, (u_long)((DBMETA *)mbuf)->version);
ret = DB_OLD_VERSION;
goto err;
}
break;
case DB_HASHMAGIC:
switch (((DBMETA *)mbuf)->version) {
case 4:
case 5:
/*
* Before V6 not all pages had page types, so we do the
* single meta-data page by hand.
*/
if ((ret =
CDB___ham_30_hashmeta(dbp, real_name, mbuf)) != 0)
goto err;
if ((ret = CDB___os_seek(dbenv,
&fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0)
goto err;
if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0)
goto err;
/* FALLTHROUGH */
case 6:
/*
* We need the page size to do more. Rip it out of
* the meta-data page.
*/
memcpy(&dbp->pgsize, mbuf + 20, sizeof(u_int32_t));
if ((ret = __db_page_pass(
dbp, real_name, flags, func_31_list, &fh)) != 0)
goto err;
/* FALLTHROUGH */
case 7:
break;
default:
CDB___db_err(dbenv, "%s: unsupported hash version: %lu",
real_name, (u_long)((DBMETA *)mbuf)->version);
ret = DB_OLD_VERSION;
goto err;
}
break;
case DB_QAMMAGIC:
switch (((DBMETA *)mbuf)->version) {
case 1:
/*
* If we're in a Queue database, the only page that
* needs upgrading is the meta-database page, don't
* bother with a full pass.
*/
if ((ret = CDB___qam_31_qammeta(dbp, real_name, mbuf)) != 0)
return (ret);
if ((ret = CDB___os_seek(dbenv,
&fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0)
goto err;
if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0)
goto err;
/* FALLTHROUGH */
case 2:
break;
default:
CDB___db_err(dbenv, "%s: unsupported queue version: %lu",
real_name, (u_long)((DBMETA *)mbuf)->version);
ret = DB_OLD_VERSION;
goto err;
}
break;
default:
M_32_SWAP(((DBMETA *)mbuf)->magic);
switch (((DBMETA *)mbuf)->magic) {
case DB_BTREEMAGIC:
case DB_HASHMAGIC:
case DB_QAMMAGIC:
CDB___db_err(dbenv,
"%s: DB->upgrade only supported on native byte-order systems",
real_name);
break;
default:
CDB___db_err(dbenv,
"%s: unrecognized file type", real_name);
break;
}
ret = EINVAL;
goto err;
}
ret = CDB___os_fsync(dbenv, &fh);
err: if ((t_ret = CDB___os_closehandle(&fh)) != 0 && ret == 0)
ret = t_ret;
CDB___os_freestr(real_name);
/* We're done. */
if (dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_UPGRADE, 100);
return (ret);
}
/*
* __db_page_pass --
* Walk the pages of the database, upgrading whatever needs it.
*/
static int
__db_page_pass(dbp, real_name, flags, fl, fhp)
DB *dbp;
char *real_name;
u_int32_t flags;
int (* const fl[P_PAGETYPE_MAX])
__P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *));
DB_FH *fhp;
{
DB_ENV *dbenv;
PAGE *page;
db_pgno_t i, pgno_last;
size_t n;
int dirty, ret;
dbenv = dbp->dbenv;
/* Determine the last page of the file. */
if ((ret = CDB___db_lastpgno(dbp, real_name, fhp, &pgno_last)) != 0)
return (ret);
/* Allocate memory for a single page. */
if ((ret = CDB___os_malloc(dbenv, dbp->pgsize, NULL, &page)) != 0)
return (ret);
/* Walk the file, calling the underlying conversion functions. */
for (i = 0; i < pgno_last; ++i) {
if (dbp->db_feedback != NULL)
dbp->db_feedback(dbp, DB_UPGRADE, (i * 100)/pgno_last);
if ((ret = CDB___os_seek(dbenv,
fhp, dbp->pgsize, i, 0, 0, DB_OS_SEEK_SET)) != 0)
break;
if ((ret = CDB___os_read(dbenv, fhp, page, dbp->pgsize, &n)) != 0)
break;
dirty = 0;
if (fl[TYPE(page)] != NULL && (ret = fl[TYPE(page)]
(dbp, real_name, flags, fhp, page, &dirty)) != 0)
break;
if (dirty) {
if ((ret = CDB___os_seek(dbenv,
fhp, dbp->pgsize, i, 0, 0, DB_OS_SEEK_SET)) != 0)
break;
if ((ret = CDB___os_write(dbenv,
fhp, page, dbp->pgsize, &n)) != 0)
break;
}
}
CDB___os_free(page, dbp->pgsize);
return (ret);
}
/*
* CDB___db_lastpgno --
* Return the current last page number of the file.
*
* PUBLIC: int CDB___db_lastpgno __P((DB *, char *, DB_FH *, db_pgno_t *));
*/
int
CDB___db_lastpgno(dbp, real_name, fhp, pgno_lastp)
DB *dbp;
char *real_name;
DB_FH *fhp;
db_pgno_t *pgno_lastp;
{
DB_ENV *dbenv;
db_pgno_t pgno_last;
u_int32_t mbytes, bytes;
int ret;
dbenv = dbp->dbenv;
if ((ret = CDB___os_ioinfo(dbenv,
real_name, fhp, &mbytes, &bytes, NULL)) != 0) {
CDB___db_err(dbenv, "%s: %s", real_name, CDB_db_strerror(ret));
return (ret);
}
/* Page sizes have to be a power-of-two. */
if (bytes % dbp->pgsize != 0) {
CDB___db_err(dbenv,
"%s: file size not a multiple of the pagesize", real_name);
return (EINVAL);
}
pgno_last = mbytes * (MEGABYTE / dbp->pgsize);
pgno_last += bytes / dbp->pgsize;
*pgno_lastp = pgno_last;
return (0);
}