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: crdel__rec_8c-source.html,v 1.1 2008年06月08日 10:16:30 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 "log.h" 00024 #include "hash.h" 00025 #include "mp.h" 00026 #include "db_dispatch.h" 00027 00028 /* 00029 * CDB___crdel_fileopen_recover -- 00030 * Recovery function for fileopen. 00031 * 00032 * PUBLIC: int CDB___crdel_fileopen_recover 00033 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00034 */ 00035 int 00036 CDB___crdel_fileopen_recover(dbenv, dbtp, lsnp, op, info) 00037 DB_ENV *dbenv; 00038 DBT *dbtp; 00039 DB_LSN *lsnp; 00040 db_recops op; 00041 void *info; 00042 { 00043 __crdel_fileopen_args *argp; 00044 DBMETA ondisk; 00045 DB_FH fh; 00046 size_t nr; 00047 int do_unlink, ret; 00048 u_int32_t b, mb, io; 00049 char *real_name; 00050 00051 COMPQUIET(info, NULL); 00052 00053 real_name = NULL; 00054 REC_PRINT(CDB___crdel_fileopen_print); 00055 00056 if ((ret = CDB___crdel_fileopen_read(dbenv, dbtp->data, &argp)) != 0) 00057 goto out; 00058 /* 00059 * If this is an in-memory database, then the name is going to 00060 * be NULL, which looks like a 0-length name in recovery. 00061 */ 00062 if (argp->name.size == 0) 00063 goto done; 00064 00065 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00066 NULL, argp->name.data, 0, NULL, &real_name)) != 0) 00067 goto out; 00068 if (DB_REDO(op)) { 00069 /* 00070 * The create commited, so we need to make sure that the file 00071 * exists. A simple open should suffice. 00072 */ 00073 if ((ret = CDB___os_open(dbenv, real_name, 00074 DB_OSO_CREATE, argp->mode, &fh)) != 0) 00075 goto out; 00076 if ((ret = CDB___os_closehandle(&fh)) != 0) 00077 goto out; 00078 } else if (DB_UNDO(op)) { 00079 /* 00080 * If the file is 0-length then it was in the process of being 00081 * created, so we should unlink it. If it is non-0 length, then 00082 * either someone else created it and we need to leave it 00083 * untouched or we were in the process of creating it, allocated 00084 * the first page on a system that requires you to actually 00085 * write pages as you allocate them, but never got any data 00086 * on it. 00087 * If the file doesn't exist, we never got around to creating 00088 * it, so that's fine. 00089 */ 00090 if (CDB___os_exists(real_name, NULL) != 0) 00091 goto done; 00092 00093 if ((ret = CDB___os_open(dbenv, real_name, 0, 0, &fh)) != 0) 00094 goto out; 00095 if ((ret = CDB___os_ioinfo(dbenv, 00096 real_name, &fh, &mb, &b, &io)) != 0) 00097 goto out; 00098 do_unlink = 0; 00099 if (mb != 0 || b != 0) { 00100 /* 00101 * We need to read the first page 00102 * to see if its got valid data on it. 00103 */ 00104 if ((ret = CDB___os_read(dbenv, &fh, 00105 &ondisk, sizeof(ondisk), &nr)) != 0 || 00106 nr != sizeof(ondisk)) 00107 goto out; 00108 if (ondisk.magic == 0) 00109 do_unlink = 1; 00110 } 00111 if ((ret = CDB___os_closehandle(&fh)) != 0) 00112 goto out; 00113 /* Check for 0-length and if it is, delete it. */ 00114 if (do_unlink || (mb == 0 && b == 0)) 00115 if ((ret = CDB___os_unlink(dbenv, real_name)) != 0) 00116 goto out; 00117 } 00118 00119 done: *lsnp = argp->prev_lsn; 00120 ret = 0; 00121 00122 out: if (argp != NULL) 00123 CDB___os_free(argp, sizeof(*argp)); 00124 if (real_name != NULL) 00125 CDB___os_freestr(real_name); 00126 return (ret); 00127 } 00128 00129 /* 00130 * CDB___crdel_metasub_recover -- 00131 * Recovery function for metasub. 00132 * 00133 * PUBLIC: int CDB___crdel_metasub_recover 00134 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00135 */ 00136 int 00137 CDB___crdel_metasub_recover(dbenv, dbtp, lsnp, op, info) 00138 DB_ENV *dbenv; 00139 DBT *dbtp; 00140 DB_LSN *lsnp; 00141 db_recops op; 00142 void *info; 00143 { 00144 __crdel_metasub_args *argp; 00145 DB *file_dbp; 00146 DBC *dbc; 00147 DB_MPOOLFILE *mpf; 00148 PAGE *pagep; 00149 int cmp_p, modified, ret; 00150 00151 COMPQUIET(info, NULL); 00152 REC_PRINT(CDB___crdel_metasub_print); 00153 REC_INTRO(CDB___crdel_metasub_read, 0); 00154 00155 if ((ret = CDB_memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) { 00156 if (DB_REDO(op)) { 00157 if ((ret = CDB_memp_fget(mpf, 00158 &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) 00159 goto out; 00160 } else { 00161 *lsnp = argp->prev_lsn; 00162 ret = 0; 00163 goto out; 00164 } 00165 } 00166 00167 modified = 0; 00168 cmp_p = CDB_log_compare(&LSN(pagep), &argp->lsn); 00169 CHECK_LSN(op, cmp_p, &LSN(pagep), &argp->lsn); 00170 00171 if (cmp_p == 0 && DB_REDO(op)) { 00172 memcpy(pagep, argp->page.data, argp->page.size); 00173 LSN(pagep) = *lsnp; 00174 modified = 1; 00175 } else if (DB_UNDO(op)) { 00176 /* 00177 * We want to undo this page creation. The page creation 00178 * happened in two parts. First, we called __bam_new which 00179 * was logged separately. Then we wrote the meta-data onto 00180 * the page. So long as we restore the LSN, then the recovery 00181 * for __bam_new will do everything else. 00182 * Don't bother checking the lsn on the page. If we 00183 * are rolling back the next thing is that this page 00184 * will get freed. Opening the subdb will have reinitialized 00185 * the page, but not the lsn. 00186 */ 00187 LSN(pagep) = argp->lsn; 00188 modified = 1; 00189 } 00190 if ((ret = CDB_memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) 00191 goto out; 00192 00193 done: *lsnp = argp->prev_lsn; 00194 ret = 0; 00195 00196 out: REC_CLOSE; 00197 } 00198 00199 /* 00200 * CDB___crdel_metapage_recover -- 00201 * Recovery function for metapage. 00202 * 00203 * PUBLIC: int CDB___crdel_metapage_recover 00204 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00205 */ 00206 int 00207 CDB___crdel_metapage_recover(dbenv, dbtp, lsnp, op, info) 00208 DB_ENV *dbenv; 00209 DBT *dbtp; 00210 DB_LSN *lsnp; 00211 db_recops op; 00212 void *info; 00213 { 00214 __crdel_metapage_args *argp; 00215 DB *dbp; 00216 DBMETA *meta, ondisk; 00217 DB_FH fh; 00218 size_t nr; 00219 u_int32_t b, io, mb, pagesize; 00220 int is_done, ret; 00221 char *real_name; 00222 00223 COMPQUIET(info, NULL); 00224 00225 real_name = NULL; 00226 memset(&fh, 0, sizeof(fh)); 00227 REC_PRINT(CDB___crdel_metapage_print); 00228 00229 if ((ret = CDB___crdel_metapage_read(dbenv, dbtp->data, &argp)) != 0) 00230 goto out; 00231 00232 /* 00233 * If this is an in-memory database, then the name is going to 00234 * be NULL, which looks like a 0-length name in recovery. 00235 */ 00236 if (argp->name.size == 0) 00237 goto done; 00238 00239 meta = (DBMETA *)argp->page.data; 00240 CDB___ua_memcpy(&pagesize, &meta->pagesize, sizeof(pagesize)); 00241 00242 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00243 NULL, argp->name.data, 0, NULL, &real_name)) != 0) 00244 goto out; 00245 if (DB_REDO(op)) { 00246 if ((ret = CDB___db_fileid_to_db(dbenv, 00247 &dbp, argp->fileid, 0)) != 0) { 00248 if (ret == DB_DELETED) 00249 goto done; 00250 else 00251 goto out; 00252 } 00253 00254 /* 00255 * We simply read the first page and if the LSN is 0, we 00256 * write the meta-data page. 00257 */ 00258 if ((ret = CDB___os_open(dbenv, real_name, 0, 0, &fh)) != 0) 00259 goto out; 00260 if ((ret = CDB___os_seek(dbenv, &fh, 00261 pagesize, argp->pgno, 0, 0, DB_OS_SEEK_SET)) != 0) 00262 goto out; 00263 /* 00264 * If the read succeeds then the page exists, then we need 00265 * to vrify that the page has actually been written, because 00266 * on some systems (e.g., Windows) we preallocate pages because 00267 * files aren't allowed to have holes in them. If the page 00268 * looks good then we're done. 00269 */ 00270 if ((ret = CDB___os_read(dbenv, &fh, &ondisk, 00271 sizeof(ondisk), &nr)) == 0 && nr == sizeof(ondisk)) { 00272 if (ondisk.magic != 0) 00273 goto done; 00274 if ((ret = CDB___os_seek(dbenv, &fh, 00275 pagesize, argp->pgno, 0, 0, DB_OS_SEEK_SET)) != 0) 00276 goto out; 00277 } 00278 00279 /* 00280 * Page didn't exist, update the LSN and write a new one. 00281 * (seek pointer shouldn't have moved) 00282 */ 00283 CDB___ua_memcpy(&meta->lsn, lsnp, sizeof(DB_LSN)); 00284 if ((ret = CDB___os_write(dbp->dbenv, &fh, 00285 argp->page.data, argp->page.size, &nr)) != 0) 00286 goto out; 00287 if (nr != (size_t)argp->page.size) { 00288 CDB___db_err(dbenv, "Write failed during recovery"); 00289 ret = EIO; 00290 goto out; 00291 } 00292 00293 /* 00294 * We must close and reopen the file to be sure 00295 * that we have the proper meta information 00296 * in the in memory structures 00297 */ 00298 00299 if ((ret = CDB___log_reopen_file(dbenv, 00300 argp->name.data, argp->fileid, 00301 meta->uid, argp->pgno)) != 0) 00302 goto out; 00303 00304 /* Handle will be closed on exit. */ 00305 } else if (DB_UNDO(op)) { 00306 is_done = 0; 00307 00308 /* If file does not exist, there is nothing to undo. */ 00309 if (CDB___os_exists(real_name, NULL) != 0) 00310 goto done; 00311 00312 /* 00313 * Before we can look at anything on disk, we have to check 00314 * if there is a valid dbp for this, and if there is, we'd 00315 * better flush it. 00316 */ 00317 dbp = NULL; 00318 if ((ret = 00319 CDB___db_fileid_to_db(dbenv, &dbp, argp->fileid, 0)) == 0) 00320 (void)dbp->sync(dbp, 0); 00321 00322 /* 00323 * We need to make sure that we do not remove a file that 00324 * someone else created. If the file is 0-length, then we 00325 * can assume that we created it and remove it. If it is 00326 * not 0-length, then we need to check the LSN and make 00327 * sure that it's the file we created. 00328 */ 00329 if ((ret = CDB___os_open(dbenv, real_name, 0, 0, &fh)) != 0) 00330 goto out; 00331 if ((ret = CDB___os_ioinfo(dbenv, 00332 real_name, &fh, &mb, &b, &io)) != 0) 00333 goto out; 00334 if (mb != 0 || b != 0) { 00335 /* The file has something in it. */ 00336 if ((ret = CDB___os_seek(dbenv, &fh, 00337 pagesize, argp->pgno, 0, 0, DB_OS_SEEK_SET)) != 0) 00338 goto out; 00339 if ((ret = CDB___os_read(dbenv, &fh, 00340 &ondisk, sizeof(ondisk), &nr)) != 0) 00341 goto out; 00342 if (CDB_log_compare(&ondisk.lsn, lsnp) != 0) 00343 is_done = 1; 00344 } 00345 00346 /* 00347 * Must close here, because unlink with the file open fails 00348 * on some systems. 00349 */ 00350 if ((ret = CDB___os_closehandle(&fh)) != 0) 00351 goto out; 00352 00353 if (!is_done) { 00354 /* 00355 * On some systems, you cannot unlink an open file so 00356 * we close the fd in the dbp here and make sure we 00357 * don't try to close it again. First, check for a 00358 * saved_open_fhp, then close down the mpool. 00359 */ 00360 if (dbp != NULL && dbp->saved_open_fhp != NULL && 00361 F_ISSET(dbp->saved_open_fhp, DB_FH_VALID) && 00362 (ret = CDB___os_closehandle(dbp->saved_open_fhp)) != 0) 00363 goto out; 00364 if (dbp != NULL && dbp->mpf != NULL) { 00365 (void)CDB___memp_fremove(dbp->mpf); 00366 if ((ret = CDB_memp_fclose(dbp->mpf)) != 0) 00367 goto out; 00368 F_SET(dbp, DB_AM_DISCARD); 00369 dbp->mpf = NULL; 00370 } 00371 if ((ret = CDB___os_unlink(dbenv, real_name)) != 0) 00372 goto out; 00373 } 00374 } 00375 00376 done: *lsnp = argp->prev_lsn; 00377 ret = 0; 00378 00379 out: if (argp != NULL) 00380 CDB___os_free(argp, sizeof(*argp)); 00381 if (real_name != NULL) 00382 CDB___os_freestr(real_name); 00383 if (F_ISSET(&fh, DB_FH_VALID)) 00384 (void)CDB___os_closehandle(&fh); 00385 return (ret); 00386 } 00387 00388 /* 00389 * CDB___crdel_delete_recover -- 00390 * Recovery function for delete. 00391 * 00392 * PUBLIC: int CDB___crdel_delete_recover 00393 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00394 */ 00395 int 00396 CDB___crdel_delete_recover(dbenv, dbtp, lsnp, op, info) 00397 DB_ENV *dbenv; 00398 DBT *dbtp; 00399 DB_LSN *lsnp; 00400 db_recops op; 00401 void *info; 00402 { 00403 DB *dbp; 00404 __crdel_delete_args *argp; 00405 int ret; 00406 char *backup, *real_back, *real_name; 00407 00408 REC_PRINT(CDB___crdel_delete_print); 00409 00410 backup = real_back = real_name = NULL; 00411 if ((ret = CDB___crdel_delete_read(dbenv, dbtp->data, &argp)) != 0) 00412 goto out; 00413 00414 if (DB_REDO(op)) { 00415 /* 00416 * On a recovery, as we recreate what was going on, we 00417 * recreate the creation of the file. And so, even though 00418 * it committed, we need to delete it. Try to delete it, 00419 * but it is not an error if that delete fails. 00420 */ 00421 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00422 NULL, argp->name.data, 0, NULL, &real_name)) != 0) 00423 goto out; 00424 if (CDB___os_exists(real_name, NULL) == 0) { 00425 /* 00426 * On Windows, the underlying file must be 00427 * closed to perform a remove. 00428 */ 00429 if ((ret = CDB___db_fileid_to_db( 00430 dbenv, &dbp, argp->fileid, 0)) != 0) 00431 goto out; 00432 (void)CDB___memp_fremove(dbp->mpf); 00433 if ((ret = CDB_memp_fclose(dbp->mpf)) != 0) 00434 goto out; 00435 dbp->mpf = NULL; 00436 if ((ret = CDB___os_unlink(dbenv, real_name)) != 0) 00437 goto out; 00438 } 00439 /* 00440 * The transaction committed, so the only thing that might 00441 * be true is that the backup file is still around. Try 00442 * to delete it, but it's not an error if that delete fails. 00443 */ 00444 if ((ret = CDB___db_backup_name(dbenv, argp->name.data, 00445 &backup, lsnp)) != 0) 00446 goto out; 00447 if ((ret = CDB___db_appname(dbenv, 00448 DB_APP_DATA, NULL, backup, 0, NULL, &real_back)) != 0) 00449 goto out; 00450 if (CDB___os_exists(real_back, NULL) == 0) 00451 if ((ret = CDB___os_unlink(dbenv, real_back)) != 0) 00452 goto out; 00453 if ((ret = CDB___db_txnlist_delete(dbenv, info, 00454 argp->name.data, TXNLIST_INVALID_ID, 1)) != 0) 00455 goto out; 00456 } else if (DB_UNDO(op)) { 00457 /* 00458 * Trying to undo. File may or may not have been deleted. 00459 * Try to move the backup to the original. If the backup 00460 * exists, then this is right. If it doesn't exist, then 00461 * nothing will happen and that's OK. 00462 */ 00463 if ((ret = CDB___db_backup_name(dbenv, argp->name.data, 00464 &backup, lsnp)) != 0) 00465 goto out; 00466 if ((ret = CDB___db_appname(dbenv, 00467 DB_APP_DATA, NULL, backup, 0, NULL, &real_back)) != 0) 00468 goto out; 00469 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00470 NULL, argp->name.data, 0, NULL, &real_name)) != 0) 00471 goto out; 00472 if (CDB___os_exists(real_back, NULL) == 0) 00473 if ((ret = 00474 CDB___os_rename(dbenv, real_back, real_name)) != 0) 00475 goto out; 00476 } 00477 00478 *lsnp = argp->prev_lsn; 00479 ret = 0; 00480 00481 out: if (argp != NULL) 00482 CDB___os_free(argp, sizeof(*argp)); 00483 if (backup != NULL) 00484 CDB___os_freestr(backup); 00485 if (real_back != NULL) 00486 CDB___os_freestr(real_back); 00487 if (real_name != NULL) 00488 CDB___os_freestr(real_name); 00489 return (ret); 00490 } 00491 /* 00492 * CDB___crdel_rename_recover -- 00493 * Recovery function for rename. 00494 * 00495 * PUBLIC: int CDB___crdel_rename_recover 00496 * PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops, void *)); 00497 */ 00498 int 00499 CDB___crdel_rename_recover(dbenv, dbtp, lsnp, op, info) 00500 DB_ENV *dbenv; 00501 DBT *dbtp; 00502 DB_LSN *lsnp; 00503 db_recops op; 00504 void *info; 00505 { 00506 DB *dbp; 00507 __crdel_rename_args *argp; 00508 char *new_name, *real_name; 00509 int ret, set; 00510 00511 COMPQUIET(info, NULL); 00512 00513 REC_PRINT(CDB___crdel_rename_print); 00514 if ((ret = CDB___crdel_rename_read(dbenv, dbtp->data, &argp)) != 0) 00515 goto out; 00516 00517 if ((ret = CDB___db_fileid_to_db(dbenv, &dbp, argp->fileid, 0)) != 0) 00518 goto out; 00519 if (DB_REDO(op)) { 00520 /* 00521 * We don't use the dbp parameter to CDB___log_filelist_update 00522 * in the rename case, so passing NULL for it is OK. 00523 */ 00524 if ((ret = CDB___log_filelist_update(dbenv, NULL, 00525 argp->fileid, argp->newname.data, &set)) != 0) 00526 goto out; 00527 if (set != 0) { 00528 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00529 NULL, argp->name.data, 0, NULL, &real_name)) != 0) 00530 goto out; 00531 if (CDB___os_exists(real_name, NULL) == 0) { 00532 if ((ret = CDB___db_appname(dbenv, 00533 DB_APP_DATA, NULL, argp->newname.data, 00534 0, NULL, &new_name)) != 0) 00535 goto out; 00536 /* 00537 * On Windows, the underlying file 00538 * must be closed to perform a remove. 00539 * The db will be closed by a 00540 * CDB_log_register record. Rename 00541 * has exclusive access to the db. 00542 */ 00543 (void)CDB___memp_fremove(dbp->mpf); 00544 if ((ret = CDB_memp_fclose(dbp->mpf)) != 0) 00545 goto out; 00546 dbp->mpf = NULL; 00547 if ((ret = CDB___os_rename(dbenv, 00548 real_name, new_name)) != 0) 00549 goto out; 00550 } 00551 } 00552 } else { 00553 /* 00554 * We don't use the dbp parameter to CDB___log_filelist_update 00555 * in the rename case, so passing NULL for it is OK. 00556 */ 00557 if ((ret = CDB___log_filelist_update(dbenv, NULL, 00558 argp->fileid, argp->name.data, &set)) != 0) 00559 goto out; 00560 if (set != 0) { 00561 if ((ret = CDB___db_appname(dbenv, DB_APP_DATA, 00562 NULL, argp->newname.data, 0, NULL, &new_name)) != 0) 00563 goto out; 00564 if (CDB___os_exists(new_name, NULL) == 0) { 00565 if ((ret = CDB___db_appname(dbenv, 00566 DB_APP_DATA, NULL, argp->name.data, 00567 0, NULL, &real_name)) != 0) 00568 goto out; 00569 /* 00570 * On Windows, the underlying file 00571 * must be closed to perform a remove. 00572 * The file may have already been closed 00573 * if we are aborting the transaction. 00574 */ 00575 if (dbp->mpf != NULL) { 00576 (void)CDB___memp_fremove(dbp->mpf); 00577 if ((ret = CDB_memp_fclose(dbp->mpf)) != 0) 00578 goto out; 00579 dbp->mpf = NULL; 00580 } 00581 if ((ret = CDB___os_rename(dbenv, 00582 new_name, real_name)) != 0) 00583 goto out; 00584 } 00585 } 00586 } 00587 00588 *lsnp = argp->prev_lsn; 00589 ret = 0; 00590 00591 out: if (argp != NULL) 00592 CDB___os_free(argp, sizeof(*argp)); 00593 return (ret); 00594 }