00001 /*- 00002 * See the file LICENSE for redistribution information. 00003 * 00004 * Copyright (c) 1996, 1997, 1998, 1999, 2000 00005 * Sleepycat Software. All rights reserved. 00006 */ 00007 00008 #include "config.h" 00009 00010 #ifndef lint 00011 static const char revid[] = "$Id: db__upg_8c-source.html,v 1.1 2008年06月08日 10:18:20 sebdiaz Exp $"; 00012 #endif /* not lint */ 00013 00014 #ifndef NO_SYSTEM_INCLUDES 00015 #include <sys/types.h> 00016 00017 #include <errno.h> 00018 #include <string.h> 00019 #endif 00020 00021 #include "db_int.h" 00022 #include "db_page.h" 00023 #include "db_swap.h" 00024 #include "btree.h" 00025 #include "hash.h" 00026 #include "qam.h" 00027 00028 static int (* const func_31_list[P_PAGETYPE_MAX]) 00029 __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *)) = { 00030 NULL, /* P_INVALID */ 00031 NULL, /* __P_DUPLICATE */ 00032 CDB___ham_31_hash, /* P_HASH */ 00033 NULL, /* P_IBTREE */ 00034 NULL, /* P_IRECNO */ 00035 CDB___bam_31_lbtree, /* P_LBTREE */ 00036 NULL, /* P_LRECNO */ 00037 NULL, /* P_OVERFLOW */ 00038 CDB___ham_31_hashmeta, /* P_HASHMETA */ 00039 CDB___bam_31_btreemeta, /* P_BTREEMETA */ 00040 }; 00041 00042 static int __db_page_pass __P((DB *, char *, u_int32_t, int (* const []) 00043 (DB *, char *, u_int32_t, DB_FH *, PAGE *, int *), DB_FH *)); 00044 00045 /* 00046 * CDB___db_upgrade -- 00047 * Upgrade an existing database. 00048 * 00049 * PUBLIC: int CDB___db_upgrade __P((DB *, const char *, u_int32_t)); 00050 */ 00051 int 00052 CDB___db_upgrade(dbp, fname, flags) 00053 DB *dbp; 00054 const char *fname; 00055 u_int32_t flags; 00056 { 00057 DB_ENV *dbenv; 00058 DB_FH fh; 00059 size_t n; 00060 int ret, t_ret; 00061 u_int8_t mbuf[256]; 00062 char *real_name; 00063 00064 dbenv = dbp->dbenv; 00065 00066 /* Validate arguments. */ 00067 if ((ret = CDB___db_fchk(dbenv, "DB->upgrade", flags, DB_DUPSORT)) != 0) 00068 return (ret); 00069 00070 /* Get the real backing file name. */ 00071 if ((ret = CDB___db_appname(dbenv, 00072 DB_APP_DATA, NULL, fname, 0, NULL, &real_name)) != 0) 00073 return (ret); 00074 00075 /* Open the file. */ 00076 if ((ret = CDB___os_open(dbenv, real_name, 0, 0, &fh)) != 0) { 00077 CDB___db_err(dbenv, "%s: %s", real_name, CDB_db_strerror(ret)); 00078 return (ret); 00079 } 00080 00081 /* Initialize the feedback. */ 00082 if (dbp->db_feedback != NULL) 00083 dbp->db_feedback(dbp, DB_UPGRADE, 0); 00084 00085 /* 00086 * Read the metadata page. We read 256 bytes, which is larger than 00087 * any access method's metadata page and smaller than any disk sector. 00088 */ 00089 if ((ret = CDB___os_read(dbenv, &fh, mbuf, sizeof(mbuf), &n)) != 0) 00090 goto err; 00091 00092 switch (((DBMETA *)mbuf)->magic) { 00093 case DB_BTREEMAGIC: 00094 switch (((DBMETA *)mbuf)->version) { 00095 case 6: 00096 /* 00097 * Before V7 not all pages had page types, so we do the 00098 * single meta-data page by hand. 00099 */ 00100 if ((ret = 00101 CDB___bam_30_btreemeta(dbp, real_name, mbuf)) != 0) 00102 goto err; 00103 if ((ret = CDB___os_seek(dbenv, 00104 &fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0) 00105 goto err; 00106 if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0) 00107 goto err; 00108 /* FALLTHROUGH */ 00109 case 7: 00110 /* 00111 * We need the page size to do more. Rip it out of 00112 * the meta-data page. 00113 */ 00114 memcpy(&dbp->pgsize, mbuf + 20, sizeof(u_int32_t)); 00115 00116 if ((ret = __db_page_pass( 00117 dbp, real_name, flags, func_31_list, &fh)) != 0) 00118 goto err; 00119 /* FALLTHROUGH */ 00120 case 8: 00121 break; 00122 default: 00123 CDB___db_err(dbenv, "%s: unsupported btree version: %lu", 00124 real_name, (u_long)((DBMETA *)mbuf)->version); 00125 ret = DB_OLD_VERSION; 00126 goto err; 00127 } 00128 break; 00129 case DB_HASHMAGIC: 00130 switch (((DBMETA *)mbuf)->version) { 00131 case 4: 00132 case 5: 00133 /* 00134 * Before V6 not all pages had page types, so we do the 00135 * single meta-data page by hand. 00136 */ 00137 if ((ret = 00138 CDB___ham_30_hashmeta(dbp, real_name, mbuf)) != 0) 00139 goto err; 00140 if ((ret = CDB___os_seek(dbenv, 00141 &fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0) 00142 goto err; 00143 if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0) 00144 goto err; 00145 /* FALLTHROUGH */ 00146 case 6: 00147 /* 00148 * We need the page size to do more. Rip it out of 00149 * the meta-data page. 00150 */ 00151 memcpy(&dbp->pgsize, mbuf + 20, sizeof(u_int32_t)); 00152 00153 if ((ret = __db_page_pass( 00154 dbp, real_name, flags, func_31_list, &fh)) != 0) 00155 goto err; 00156 /* FALLTHROUGH */ 00157 case 7: 00158 break; 00159 default: 00160 CDB___db_err(dbenv, "%s: unsupported hash version: %lu", 00161 real_name, (u_long)((DBMETA *)mbuf)->version); 00162 ret = DB_OLD_VERSION; 00163 goto err; 00164 } 00165 break; 00166 case DB_QAMMAGIC: 00167 switch (((DBMETA *)mbuf)->version) { 00168 case 1: 00169 /* 00170 * If we're in a Queue database, the only page that 00171 * needs upgrading is the meta-database page, don't 00172 * bother with a full pass. 00173 */ 00174 if ((ret = CDB___qam_31_qammeta(dbp, real_name, mbuf)) != 0) 00175 return (ret); 00176 if ((ret = CDB___os_seek(dbenv, 00177 &fh, 0, 0, 0, 0, DB_OS_SEEK_SET)) != 0) 00178 goto err; 00179 if ((ret = CDB___os_write(dbenv, &fh, mbuf, 256, &n)) != 0) 00180 goto err; 00181 /* FALLTHROUGH */ 00182 case 2: 00183 break; 00184 default: 00185 CDB___db_err(dbenv, "%s: unsupported queue version: %lu", 00186 real_name, (u_long)((DBMETA *)mbuf)->version); 00187 ret = DB_OLD_VERSION; 00188 goto err; 00189 } 00190 break; 00191 default: 00192 M_32_SWAP(((DBMETA *)mbuf)->magic); 00193 switch (((DBMETA *)mbuf)->magic) { 00194 case DB_BTREEMAGIC: 00195 case DB_HASHMAGIC: 00196 case DB_QAMMAGIC: 00197 CDB___db_err(dbenv, 00198 "%s: DB->upgrade only supported on native byte-order systems", 00199 real_name); 00200 break; 00201 default: 00202 CDB___db_err(dbenv, 00203 "%s: unrecognized file type", real_name); 00204 break; 00205 } 00206 ret = EINVAL; 00207 goto err; 00208 } 00209 00210 ret = CDB___os_fsync(dbenv, &fh); 00211 00212 err: if ((t_ret = CDB___os_closehandle(&fh)) != 0 && ret == 0) 00213 ret = t_ret; 00214 CDB___os_freestr(real_name); 00215 00216 /* We're done. */ 00217 if (dbp->db_feedback != NULL) 00218 dbp->db_feedback(dbp, DB_UPGRADE, 100); 00219 00220 return (ret); 00221 } 00222 00223 /* 00224 * __db_page_pass -- 00225 * Walk the pages of the database, upgrading whatever needs it. 00226 */ 00227 static int 00228 __db_page_pass(dbp, real_name, flags, fl, fhp) 00229 DB *dbp; 00230 char *real_name; 00231 u_int32_t flags; 00232 int (* const fl[P_PAGETYPE_MAX]) 00233 __P((DB *, char *, u_int32_t, DB_FH *, PAGE *, int *)); 00234 DB_FH *fhp; 00235 { 00236 DB_ENV *dbenv; 00237 PAGE *page; 00238 db_pgno_t i, pgno_last; 00239 size_t n; 00240 int dirty, ret; 00241 00242 dbenv = dbp->dbenv; 00243 00244 /* Determine the last page of the file. */ 00245 if ((ret = CDB___db_lastpgno(dbp, real_name, fhp, &pgno_last)) != 0) 00246 return (ret); 00247 00248 /* Allocate memory for a single page. */ 00249 if ((ret = CDB___os_malloc(dbenv, dbp->pgsize, NULL, &page)) != 0) 00250 return (ret); 00251 00252 /* Walk the file, calling the underlying conversion functions. */ 00253 for (i = 0; i < pgno_last; ++i) { 00254 if (dbp->db_feedback != NULL) 00255 dbp->db_feedback(dbp, DB_UPGRADE, (i * 100)/pgno_last); 00256 if ((ret = CDB___os_seek(dbenv, 00257 fhp, dbp->pgsize, i, 0, 0, DB_OS_SEEK_SET)) != 0) 00258 break; 00259 if ((ret = CDB___os_read(dbenv, fhp, page, dbp->pgsize, &n)) != 0) 00260 break; 00261 dirty = 0; 00262 if (fl[TYPE(page)] != NULL && (ret = fl[TYPE(page)] 00263 (dbp, real_name, flags, fhp, page, &dirty)) != 0) 00264 break; 00265 if (dirty) { 00266 if ((ret = CDB___os_seek(dbenv, 00267 fhp, dbp->pgsize, i, 0, 0, DB_OS_SEEK_SET)) != 0) 00268 break; 00269 if ((ret = CDB___os_write(dbenv, 00270 fhp, page, dbp->pgsize, &n)) != 0) 00271 break; 00272 } 00273 } 00274 00275 CDB___os_free(page, dbp->pgsize); 00276 return (ret); 00277 } 00278 00279 /* 00280 * CDB___db_lastpgno -- 00281 * Return the current last page number of the file. 00282 * 00283 * PUBLIC: int CDB___db_lastpgno __P((DB *, char *, DB_FH *, db_pgno_t *)); 00284 */ 00285 int 00286 CDB___db_lastpgno(dbp, real_name, fhp, pgno_lastp) 00287 DB *dbp; 00288 char *real_name; 00289 DB_FH *fhp; 00290 db_pgno_t *pgno_lastp; 00291 { 00292 DB_ENV *dbenv; 00293 db_pgno_t pgno_last; 00294 u_int32_t mbytes, bytes; 00295 int ret; 00296 00297 dbenv = dbp->dbenv; 00298 00299 if ((ret = CDB___os_ioinfo(dbenv, 00300 real_name, fhp, &mbytes, &bytes, NULL)) != 0) { 00301 CDB___db_err(dbenv, "%s: %s", real_name, CDB_db_strerror(ret)); 00302 return (ret); 00303 } 00304 00305 /* Page sizes have to be a power-of-two. */ 00306 if (bytes % dbp->pgsize != 0) { 00307 CDB___db_err(dbenv, 00308 "%s: file size not a multiple of the pagesize", real_name); 00309 return (EINVAL); 00310 } 00311 pgno_last = mbytes * (MEGABYTE / dbp->pgsize); 00312 pgno_last += bytes / dbp->pgsize; 00313 00314 *pgno_lastp = pgno_last; 00315 return (0); 00316 }