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