1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 1996, 1997, 1998 5 * Sleepycat Software. All rights reserved. 6 */ 7 8 #include "config.h" 9 10 #ifndef lint 11 static const char sccsid[] = "@(#)log_findckp.c 10.17 (Sleepycat) 9/17/98"; 12 #endif /* not lint */ 13 14 #ifndef NO_SYSTEM_INCLUDES 15 #include <sys/types.h> 16 17 #include <errno.h> 18 #include <string.h> 19 #endif 20 21 #include "db_int.h" 22 #include "shqueue.h" 23 #include "log.h" 24 #include "txn.h" 25 #include "common_ext.h" 26 27 /* 28 * __log_findckp -- 29 * 30 * Looks for the most recent checkpoint that occurs before the most recent 31 * checkpoint LSN, subject to the constraint that there must be at least two 32 * checkpoints. The reason you need two checkpoints is that you might have 33 * crashed during the most recent one and may not have a copy of all the 34 * open files. This is the point from which recovery can start and the 35 * point up to which archival/truncation can take place. Checkpoints in 36 * the log look like: 37 * 38 * ------------------------------------------------------------------- 39 * | ckp A, ckplsn 100 | .... record .... | ckp B, ckplsn 600 | ... 40 * ------------------------------------------------------------------- 41 * LSN 500 LSN 1000 42 * 43 * If we read what log returns from using the DB_CKP parameter to logput, 44 * we'll get the record at LSN 1000. The checkpoint LSN there is 600. 45 * Now we have to scan backwards looking for a checkpoint before LSN 600. 46 * We find one at 500. This means that we can truncate the log before 47 * 500 or run recovery beginning at 500. 48 * 49 * Returns 0 if we find a suitable checkpoint or we retrieved the 50 * first record in the log from which to start. 51 * Returns DB_NOTFOUND if there are no log records. 52 * Returns errno on error. 53 * 54 * PUBLIC: int __log_findckp __P((DB_LOG *, DB_LSN *)); 55 */ 56 int 57 __log_findckp(lp, lsnp) 58 DB_LOG *lp; 59 DB_LSN *lsnp; 60 { 61 DBT data; 62 DB_LSN ckp_lsn, final_ckp, last_ckp, next_lsn; 63 __txn_ckp_args *ckp_args; 64 int ret, verbose; 65 66 verbose = lp->dbenv != NULL && lp->dbenv->db_verbose != 0; 67 68 /* 69 * Need to find the appropriate point from which to begin 70 * recovery. 71 */ 72 memset(&data, 0, sizeof(data)); 73 if (F_ISSET(lp, DB_AM_THREAD)) 74 F_SET(&data, DB_DBT_MALLOC); 75 ZERO_LSN(ckp_lsn); 76 if ((ret = log_get(lp, &last_ckp, &data, DB_CHECKPOINT)) != 0) 77 if (ret == ENOENT) 78 goto get_first; 79 else 80 return (ret); 81 82 final_ckp = last_ckp; 83 next_lsn = last_ckp; 84 do { 85 if (F_ISSET(lp, DB_AM_THREAD)) 86 __os_free(data.data, data.size); 87 88 if ((ret = log_get(lp, &next_lsn, &data, DB_SET)) != 0) 89 return (ret); 90 if ((ret = __txn_ckp_read(data.data, &ckp_args)) != 0) { 91 if (F_ISSET(lp, DB_AM_THREAD)) 92 __os_free(data.data, data.size); 93 return (ret); 94 } 95 if (IS_ZERO_LSN(ckp_lsn)) 96 ckp_lsn = ckp_args->ckp_lsn; 97 if (verbose) { 98 __db_err(lp->dbenv, "Checkpoint at: [%lu][%lu]", 99 (u_long)last_ckp.file, (u_long)last_ckp.offset); 100 __db_err(lp->dbenv, "Checkpoint LSN: [%lu][%lu]", 101 (u_long)ckp_args->ckp_lsn.file, 102 (u_long)ckp_args->ckp_lsn.offset); 103 __db_err(lp->dbenv, "Previous checkpoint: [%lu][%lu]", 104 (u_long)ckp_args->last_ckp.file, 105 (u_long)ckp_args->last_ckp.offset); 106 } 107 last_ckp = next_lsn; 108 next_lsn = ckp_args->last_ckp; 109 __os_free(ckp_args, sizeof(*ckp_args)); 110 111 /* 112 * Keep looping until either you 1) run out of checkpoints, 113 * 2) you've found a checkpoint before the most recent 114 * checkpoint's LSN and you have at least 2 checkpoints. 115 */ 116 } while (!IS_ZERO_LSN(next_lsn) && 117 (log_compare(&last_ckp, &ckp_lsn) > 0 || 118 log_compare(&final_ckp, &last_ckp) == 0)); 119 120 if (F_ISSET(lp, DB_AM_THREAD)) 121 __os_free(data.data, data.size); 122 123 /* 124 * At this point, either, next_lsn is ZERO or ckp_lsn is the 125 * checkpoint lsn and last_ckp is the LSN of the last checkpoint 126 * before ckp_lsn. If the compare in the loop is still true, then 127 * next_lsn must be 0 and we need to roll forward from the 128 * beginning of the log. 129 */ 130 if (log_compare(&last_ckp, &ckp_lsn) > 0 || 131 log_compare(&final_ckp, &last_ckp) == 0) { 132 get_first: if ((ret = log_get(lp, &last_ckp, &data, DB_FIRST)) != 0) 133 return (ret); 134 if (F_ISSET(lp, DB_AM_THREAD)) 135 __os_free(data.data, data.size); 136 } 137 *lsnp = last_ckp; 138 139 return (IS_ZERO_LSN(last_ckp) ? DB_NOTFOUND : 0); 140 } 141