1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 2010 Oracle and/or its affiliates.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12#include "dbinc/db_page.h"
13#include "dbinc/log.h"
14#include "dbinc/mp.h"
15#include "dbinc/hash.h"
16
17static int __db_pg_free_recover_int __P((ENV *, DB_THREAD_INFO *,
18    __db_pg_freedata_args *, DB *, DB_LSN *, DB_MPOOLFILE *, db_recops, int));
19static int __db_pg_free_recover_42_int __P((ENV *, DB_THREAD_INFO *,
20    __db_pg_freedata_42_args *,
21    DB *, DB_LSN *, DB_MPOOLFILE *, db_recops, int));
22
23/*
24 * PUBLIC: int __db_addrem_recover
25 * PUBLIC:    __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
26 *
27 * This log message is generated whenever we add or remove a duplicate
28 * to/from a duplicate page.  On recover, we just do the opposite.
29 */
30int
31__db_addrem_recover(env, dbtp, lsnp, op, info)
32	ENV *env;
33	DBT *dbtp;
34	DB_LSN *lsnp;
35	db_recops op;
36	void *info;
37{
38	__db_addrem_args *argp;
39	DB_THREAD_INFO *ip;
40	DB *file_dbp;
41	DBC *dbc;
42	DB_MPOOLFILE *mpf;
43	PAGE *pagep;
44	int cmp_n, cmp_p, modified, ret;
45
46	ip = ((DB_TXNHEAD *)info)->thread_info;
47	pagep = NULL;
48	REC_PRINT(__db_addrem_print);
49	REC_INTRO(__db_addrem_read, ip, 1);
50
51	REC_FGET(mpf, ip, argp->pgno, &pagep, done);
52	modified = 0;
53
54	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
55	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->pagelsn);
56	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->pagelsn);
57	CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
58	if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_DUP) ||
59	    (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_REM_DUP)) {
60		/* Need to redo an add, or undo a delete. */
61		REC_DIRTY(mpf, ip, dbc->priority, &pagep);
62		if ((ret = __db_pitem(dbc, pagep, argp->indx, argp->nbytes,
63		    argp->hdr.size == 0 ? NULL : &argp->hdr,
64		    argp->dbt.size == 0 ? NULL : &argp->dbt)) != 0)
65			goto out;
66		modified = 1;
67
68	} else if ((cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_ADD_DUP) ||
69	    (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_REM_DUP)) {
70		/* Need to undo an add, or redo a delete. */
71		REC_DIRTY(mpf, ip, dbc->priority, &pagep);
72		if ((ret = __db_ditem(dbc,
73		    pagep, argp->indx, argp->nbytes)) != 0)
74			goto out;
75		modified = 1;
76	}
77
78	if (modified) {
79		if (DB_REDO(op))
80			LSN(pagep) = *lsnp;
81		else
82			LSN(pagep) = argp->pagelsn;
83	}
84
85	if ((ret = __memp_fput(mpf, ip, pagep, dbc->priority)) != 0)
86		goto out;
87	pagep = NULL;
88
89done:	*lsnp = argp->prev_lsn;
90	ret = 0;
91
92out:	if (pagep != NULL)
93		(void)__memp_fput(mpf, ip, pagep, dbc->priority);
94	REC_CLOSE;
95}
96
97/*
98 * PUBLIC: int __db_big_recover
99 * PUBLIC:     __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
100 */
101int
102__db_big_recover(env, dbtp, lsnp, op, info)
103	ENV *env;
104	DBT *dbtp;
105	DB_LSN *lsnp;
106	db_recops op;
107	void *info;
108{
109	__db_big_args *argp;
110	DB_THREAD_INFO *ip;
111	DB *file_dbp;
112	DBC *dbc;
113	DB_MPOOLFILE *mpf;
114	PAGE *pagep;
115	int cmp_n, cmp_p, modified, ret;
116
117	ip = ((DB_TXNHEAD *)info)->thread_info;
118	pagep = NULL;
119	REC_PRINT(__db_big_print);
120	REC_INTRO(__db_big_read, ip, 0);
121
122	REC_FGET(mpf, ip, argp->pgno, &pagep, ppage);
123	modified = 0;
124
125	/*
126	 * There are three pages we need to check.  The one on which we are
127	 * adding data, the previous one whose next_pointer may have
128	 * been updated, and the next one whose prev_pointer may have
129	 * been updated.
130	 */
131	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
132	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->pagelsn);
133	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->pagelsn);
134	CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
135	if ((cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_BIG) ||
136	    (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_REM_BIG)) {
137		/* We are either redo-ing an add, or undoing a delete. */
138		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
139		P_INIT(pagep, file_dbp->pgsize, argp->pgno, argp->prev_pgno,
140			argp->next_pgno, 0, P_OVERFLOW);
141		OV_LEN(pagep) = argp->dbt.size;
142		OV_REF(pagep) = 1;
143		memcpy((u_int8_t *)pagep + P_OVERHEAD(file_dbp), argp->dbt.data,
144		    argp->dbt.size);
145		PREV_PGNO(pagep) = argp->prev_pgno;
146		modified = 1;
147	} else if ((cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_ADD_BIG) ||
148	    (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_REM_BIG)) {
149		/*
150		 * We are either undo-ing an add or redo-ing a delete.
151		 * The page is about to be reclaimed in either case, so
152		 * there really isn't anything to do here.
153		 */
154		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
155		modified = 1;
156	} else if (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_APPEND_BIG) {
157		/* We are redoing an append. */
158		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
159		memcpy((u_int8_t *)pagep + P_OVERHEAD(file_dbp) +
160		    OV_LEN(pagep), argp->dbt.data, argp->dbt.size);
161		OV_LEN(pagep) += argp->dbt.size;
162		modified = 1;
163	} else if (cmp_n == 0 && DB_UNDO(op) && argp->opcode == DB_APPEND_BIG) {
164		/* We are undoing an append. */
165		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
166		OV_LEN(pagep) -= argp->dbt.size;
167		memset((u_int8_t *)pagep + P_OVERHEAD(file_dbp) +
168		    OV_LEN(pagep), 0, argp->dbt.size);
169		modified = 1;
170	}
171	if (modified)
172		LSN(pagep) = DB_REDO(op) ? *lsnp : argp->pagelsn;
173
174	ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
175	pagep = NULL;
176	if (ret != 0)
177		goto out;
178
179	/*
180	 * We only delete a whole chain of overflow items, and appends only
181	 * apply to a single page.  Adding a page is the only case that
182	 * needs to update the chain.
183	 */
184	if (argp->opcode != DB_ADD_BIG)
185		goto done;
186
187	/* Now check the previous page. */
188ppage:	if (argp->prev_pgno != PGNO_INVALID) {
189		REC_FGET(mpf, ip, argp->prev_pgno, &pagep, npage);
190		modified = 0;
191
192		cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
193		cmp_p = LOG_COMPARE(&LSN(pagep), &argp->prevlsn);
194		CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->prevlsn);
195		CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
196
197		if (cmp_p == 0 && DB_REDO(op) && argp->opcode == DB_ADD_BIG) {
198			/* Redo add, undo delete. */
199			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
200			NEXT_PGNO(pagep) = argp->pgno;
201			modified = 1;
202		} else if (cmp_n == 0 &&
203		    DB_UNDO(op) && argp->opcode == DB_ADD_BIG) {
204			/* Redo delete, undo add. */
205			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
206			NEXT_PGNO(pagep) = argp->next_pgno;
207			modified = 1;
208		}
209		if (modified)
210			LSN(pagep) = DB_REDO(op) ? *lsnp : argp->prevlsn;
211		ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
212		pagep = NULL;
213		if (ret != 0)
214			goto out;
215	}
216	pagep = NULL;
217
218	/* Now check the next page.  Can only be set on a delete. */
219npage:	if (argp->next_pgno != PGNO_INVALID) {
220		REC_FGET(mpf, ip, argp->next_pgno, &pagep, done);
221		modified = 0;
222
223		cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
224		cmp_p = LOG_COMPARE(&LSN(pagep), &argp->nextlsn);
225		CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->nextlsn);
226		CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
227		if (cmp_p == 0 && DB_REDO(op)) {
228			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
229			PREV_PGNO(pagep) = PGNO_INVALID;
230			modified = 1;
231		} else if (cmp_n == 0 && DB_UNDO(op)) {
232			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
233			PREV_PGNO(pagep) = argp->pgno;
234			modified = 1;
235		}
236		if (modified)
237			LSN(pagep) = DB_REDO(op) ? *lsnp : argp->nextlsn;
238		ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
239		pagep = NULL;
240		if (ret != 0)
241			goto out;
242	}
243	pagep = NULL;
244
245done:	*lsnp = argp->prev_lsn;
246	ret = 0;
247
248out:	if (pagep != NULL)
249		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
250	REC_CLOSE;
251}
252
253/*
254 * __db_ovref_recover --
255 *	Recovery function for __db_ovref().
256 *
257 * PUBLIC: int __db_ovref_recover
258 * PUBLIC:     __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
259 */
260int
261__db_ovref_recover(env, dbtp, lsnp, op, info)
262	ENV *env;
263	DBT *dbtp;
264	DB_LSN *lsnp;
265	db_recops op;
266	void *info;
267{
268	__db_ovref_args *argp;
269	DB_THREAD_INFO *ip;
270	DB *file_dbp;
271	DBC *dbc;
272	DB_MPOOLFILE *mpf;
273	PAGE *pagep;
274	int cmp, ret;
275
276	ip = ((DB_TXNHEAD *)info)->thread_info;
277	pagep = NULL;
278	REC_PRINT(__db_ovref_print);
279	REC_INTRO(__db_ovref_read, ip, 0);
280
281	REC_FGET(mpf, ip, argp->pgno, &pagep, done);
282
283	cmp = LOG_COMPARE(&LSN(pagep), &argp->lsn);
284	CHECK_LSN(env, op, cmp, &LSN(pagep), &argp->lsn);
285	if (cmp == 0 && DB_REDO(op)) {
286		/* Need to redo update described. */
287		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
288		OV_REF(pagep) += argp->adjust;
289		pagep->lsn = *lsnp;
290	} else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) {
291		/* Need to undo update described. */
292		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
293		OV_REF(pagep) -= argp->adjust;
294		pagep->lsn = argp->lsn;
295	}
296	ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
297	pagep = NULL;
298	if (ret != 0)
299		goto out;
300	pagep = NULL;
301
302done:	*lsnp = argp->prev_lsn;
303	ret = 0;
304
305out:	if (pagep != NULL)
306		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
307	REC_CLOSE;
308}
309
310/*
311 * __db_debug_recover --
312 *	Recovery function for debug.
313 *
314 * PUBLIC: int __db_debug_recover __P((ENV *,
315 * PUBLIC:     DBT *, DB_LSN *, db_recops, void *));
316 */
317int
318__db_debug_recover(env, dbtp, lsnp, op, info)
319	ENV *env;
320	DBT *dbtp;
321	DB_LSN *lsnp;
322	db_recops op;
323	void *info;
324{
325	__db_debug_args *argp;
326	int ret;
327
328	COMPQUIET(op, DB_TXN_ABORT);
329	COMPQUIET(info, NULL);
330
331	REC_PRINT(__db_debug_print);
332	REC_NOOP_INTRO(__db_debug_read);
333
334	*lsnp = argp->prev_lsn;
335	ret = 0;
336
337	REC_NOOP_CLOSE;
338}
339
340/*
341 * __db_noop_recover --
342 *	Recovery function for noop.
343 *
344 * PUBLIC: int __db_noop_recover __P((ENV *,
345 * PUBLIC:      DBT *, DB_LSN *, db_recops, void *));
346 */
347int
348__db_noop_recover(env, dbtp, lsnp, op, info)
349	ENV *env;
350	DBT *dbtp;
351	DB_LSN *lsnp;
352	db_recops op;
353	void *info;
354{
355	__db_noop_args *argp;
356	DB_THREAD_INFO *ip;
357	DB *file_dbp;
358	DBC *dbc;
359	DB_MPOOLFILE *mpf;
360	PAGE *pagep;
361	int cmp_n, cmp_p, ret;
362
363	ip = ((DB_TXNHEAD *)info)->thread_info;
364	pagep = NULL;
365	REC_PRINT(__db_noop_print);
366	REC_INTRO(__db_noop_read, ip, 0);
367
368	REC_FGET(mpf, ip, argp->pgno, &pagep, done);
369
370	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
371	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->prevlsn);
372	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->prevlsn);
373	CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
374	if (cmp_p == 0 && DB_REDO(op)) {
375		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
376		LSN(pagep) = *lsnp;
377	} else if (cmp_n == 0 && DB_UNDO(op)) {
378		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
379		LSN(pagep) = argp->prevlsn;
380	}
381	ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
382	pagep = NULL;
383
384done:	*lsnp = argp->prev_lsn;
385out:	if (pagep != NULL)
386		(void)__memp_fput(mpf,
387		    ip, pagep, file_dbp->priority);
388	REC_CLOSE;
389}
390
391/*
392 * __db_pg_alloc_recover --
393 *	Recovery function for pg_alloc.
394 *
395 * PUBLIC: int __db_pg_alloc_recover
396 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
397 */
398int
399__db_pg_alloc_recover(env, dbtp, lsnp, op, info)
400	ENV *env;
401	DBT *dbtp;
402	DB_LSN *lsnp;
403	db_recops op;
404	void *info;
405{
406	__db_pg_alloc_args *argp;
407	DB_THREAD_INFO *ip;
408	DB *file_dbp;
409	DBC *dbc;
410	DBMETA *meta;
411	DB_MPOOLFILE *mpf;
412	PAGE *pagep;
413	db_pgno_t pgno;
414	int cmp_n, cmp_p, created, level, ret;
415
416	ip = ((DB_TXNHEAD *)info)->thread_info;
417	meta = NULL;
418	pagep = NULL;
419	created = 0;
420	REC_PRINT(__db_pg_alloc_print);
421	REC_INTRO(__db_pg_alloc_read, ip, 0);
422
423	/*
424	 * Fix up the metadata page.  If we're redoing the operation, we have
425	 * to get the metadata page and update its LSN and its free pointer.
426	 * If we're undoing the operation and the page was ever created, we put
427	 * it on the freelist.
428	 */
429	pgno = PGNO_BASE_MD;
430	if ((ret = __memp_fget(mpf, &pgno, ip, NULL, 0, &meta)) != 0) {
431		/* The metadata page must always exist on redo. */
432		if (DB_REDO(op)) {
433			ret = __db_pgerr(file_dbp, pgno, ret);
434			goto out;
435		} else
436			goto done;
437	}
438	cmp_n = LOG_COMPARE(lsnp, &LSN(meta));
439	cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn);
440	CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn);
441	CHECK_ABORT(env, op, cmp_n, &LSN(meta), lsnp);
442	if (cmp_p == 0 && DB_REDO(op)) {
443		/* Need to redo update described. */
444		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
445		LSN(meta) = *lsnp;
446		meta->free = argp->next;
447		if (argp->pgno > meta->last_pgno)
448			meta->last_pgno = argp->pgno;
449	} else if (cmp_n == 0 && DB_UNDO(op)) {
450		/* Need to undo update described. */
451		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
452		LSN(meta) = argp->meta_lsn;
453		/*
454		 * If the page has a zero LSN then its newly created and
455		 * will be truncated rather than go on the free list.
456		 */
457		if (!IS_ZERO_LSN(argp->page_lsn))
458			meta->free = argp->pgno;
459		meta->last_pgno = argp->last_pgno;
460	}
461
462#ifdef HAVE_FTRUNCATE
463	/*
464	 * check to see if we are keeping a sorted freelist, if so put
465	 * this back in the in memory list.  It must be the first element.
466	 */
467	if (op == DB_TXN_ABORT && !IS_ZERO_LSN(argp->page_lsn)) {
468		db_pgno_t *list;
469		u_int32_t nelem;
470
471		if ((ret = __memp_get_freelist(mpf, &nelem, &list)) != 0)
472			goto out;
473		if (list != NULL && (nelem == 0 || *list != argp->pgno)) {
474			if ((ret =
475			    __memp_extend_freelist(mpf, nelem + 1, &list)) != 0)
476				goto out;
477			if (nelem != 0)
478				memmove(list + 1, list, nelem * sizeof(*list));
479			*list = argp->pgno;
480		}
481	}
482#endif
483
484	/*
485	 * Fix up the allocated page. If the page does not exist
486	 * and we can truncate it then don't create it.
487	 * Otherwise if we're redoing the operation, we have
488	 * to get the page (creating it if it doesn't exist), and update its
489	 * LSN.  If we're undoing the operation, we have to reset the page's
490	 * LSN and put it on the free list.
491	 */
492	if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) {
493		/*
494		 * We have to be able to identify if a page was newly
495		 * created so we can recover it properly.  We cannot simply
496		 * look for an empty header, because hash uses a pgin
497		 * function that will set the header.  Instead, we explicitly
498		 * try for the page without CREATE and if that fails, then
499		 * create it.
500		 */
501		if (DB_UNDO(op))
502			goto do_truncate;
503		if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL,
504		    DB_MPOOL_CREATE, &pagep)) != 0) {
505			if (DB_UNDO(op) && ret == ENOSPC)
506				goto do_truncate;
507			ret = __db_pgerr(file_dbp, argp->pgno, ret);
508			goto out;
509		}
510		created = 1;
511	}
512
513	/* Fix up the allocated page. */
514	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
515	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->page_lsn);
516
517	/*
518	 * If an initial allocation is aborted and then reallocated during
519	 * an archival restore the log record will have an LSN for the page
520	 * but the page will be empty.
521	 */
522	if (IS_ZERO_LSN(LSN(pagep)))
523		cmp_p = 0;
524
525	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->page_lsn);
526	/*
527	 * Another special case we have to handle is if we ended up with a
528	 * page of all 0's which can happen if we abort between allocating a
529	 * page in mpool and initializing it.  In that case, even if we're
530	 * undoing, we need to re-initialize the page.
531	 */
532	if (DB_REDO(op) && cmp_p == 0) {
533		/* Need to redo update described. */
534		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
535		switch (argp->ptype) {
536		case P_LBTREE:
537		case P_LRECNO:
538		case P_LDUP:
539			level = LEAFLEVEL;
540			break;
541		default:
542			level = 0;
543			break;
544		}
545		P_INIT(pagep, file_dbp->pgsize,
546		    argp->pgno, PGNO_INVALID, PGNO_INVALID, level, argp->ptype);
547
548		pagep->lsn = *lsnp;
549	} else if (DB_UNDO(op) && (cmp_n == 0 || created)) {
550		/*
551		 * This is where we handle the case of a 0'd page (pagep->pgno
552		 * is equal to PGNO_INVALID).
553		 * Undo the allocation, reinitialize the page and
554		 * link its next pointer to the free list.
555		 */
556		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
557		P_INIT(pagep, file_dbp->pgsize,
558		    argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
559
560		pagep->lsn = argp->page_lsn;
561	}
562
563do_truncate:
564	/*
565	 * If the page was newly created, give it back.
566	 */
567	if ((pagep == NULL || IS_ZERO_LSN(LSN(pagep))) &&
568	    IS_ZERO_LSN(argp->page_lsn) && DB_UNDO(op)) {
569		/* Discard the page. */
570		if (pagep != NULL) {
571			if ((ret = __memp_fput(mpf, ip,
572			    pagep, DB_PRIORITY_VERY_LOW)) != 0)
573				goto out;
574			pagep = NULL;
575		}
576		/* Give the page back to the OS. */
577		if (meta->last_pgno <= argp->pgno && (ret = __memp_ftruncate(
578		    mpf, NULL, ip, argp->pgno, MP_TRUNC_RECOVER)) != 0)
579			goto out;
580	}
581
582	if (pagep != NULL) {
583		ret = __memp_fput(mpf, ip, pagep, file_dbp->priority);
584		pagep = NULL;
585		if (ret != 0)
586			goto out;
587	}
588
589	ret = __memp_fput(mpf, ip, meta, file_dbp->priority);
590	meta = NULL;
591	if (ret != 0)
592		goto out;
593
594done:	*lsnp = argp->prev_lsn;
595	ret = 0;
596
597out:	if (pagep != NULL)
598		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
599	if (meta != NULL)
600		(void)__memp_fput(mpf, ip, meta, file_dbp->priority);
601	REC_CLOSE;
602}
603
604/*
605 * __db_pg_free_recover_int --
606 */
607static int
608__db_pg_free_recover_int(env, ip, argp, file_dbp, lsnp, mpf, op, data)
609	ENV *env;
610	DB_THREAD_INFO *ip;
611	__db_pg_freedata_args *argp;
612	DB *file_dbp;
613	DB_LSN *lsnp;
614	DB_MPOOLFILE *mpf;
615	db_recops op;
616	int data;
617{
618	DBMETA *meta;
619	DB_LSN copy_lsn;
620	PAGE *pagep, *prevp;
621	int cmp_n, cmp_p, is_meta, ret;
622
623	meta = NULL;
624	pagep = prevp = NULL;
625
626	/*
627	 * Get the "metapage".  This will either be the metapage
628	 * or the previous page in the free list if we are doing
629	 * sorted allocations.  If its a previous page then
630	 * we will not be truncating.
631	 */
632	is_meta = argp->meta_pgno == PGNO_BASE_MD;
633
634	REC_FGET(mpf, ip, argp->meta_pgno, &meta, check_meta);
635
636	if (argp->meta_pgno != PGNO_BASE_MD)
637		prevp = (PAGE *)meta;
638
639	cmp_n = LOG_COMPARE(lsnp, &LSN(meta));
640	cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn);
641	CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn);
642	CHECK_ABORT(env, op, cmp_n, &LSN(meta), lsnp);
643
644	/*
645	 * Fix up the metadata page.  If we're redoing or undoing the operation
646	 * we get the page and update its LSN, last and free pointer.
647	 */
648	if (cmp_p == 0 && DB_REDO(op)) {
649		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
650		/*
651		 * If we are at the end of the file truncate, otherwise
652		 * put on the free list.
653		*/
654		if (argp->pgno == argp->last_pgno)
655			meta->last_pgno = argp->pgno - 1;
656		else if (is_meta)
657			meta->free = argp->pgno;
658		else
659			NEXT_PGNO(prevp) = argp->pgno;
660		LSN(meta) = *lsnp;
661	} else if (cmp_n == 0 && DB_UNDO(op)) {
662		/* Need to undo the deallocation. */
663		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
664		if (is_meta) {
665			if (meta->last_pgno < argp->pgno)
666				meta->last_pgno = argp->pgno;
667			meta->free = argp->next;
668		} else
669			NEXT_PGNO(prevp) = argp->next;
670		LSN(meta) = argp->meta_lsn;
671	}
672
673check_meta:
674	if (ret != 0 && is_meta) {
675		/* The metadata page must always exist. */
676		ret = __db_pgerr(file_dbp, argp->meta_pgno, ret);
677		goto out;
678	}
679
680	/*
681	 * Get the freed page.  Don't create the page if we are going to
682	 * free it.  If we're redoing the operation we get the page and
683	 * explicitly discard its contents, then update its LSN. If we're
684	 * undoing the operation, we get the page and restore its header.
685	 */
686	if (DB_REDO(op) || (is_meta && meta->last_pgno < argp->pgno)) {
687		if ((ret = __memp_fget(mpf, &argp->pgno,
688		    ip, NULL, 0, &pagep)) != 0) {
689			if (ret != DB_PAGE_NOTFOUND)
690				goto out;
691			if (is_meta &&
692			    DB_REDO(op) && meta->last_pgno <= argp->pgno)
693				goto trunc;
694			goto done;
695		}
696	} else if ((ret = __memp_fget(mpf, &argp->pgno,
697	   ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0)
698		goto out;
699
700	(void)__ua_memcpy(&copy_lsn, &LSN(argp->header.data), sizeof(DB_LSN));
701	cmp_n = IS_ZERO_LSN(LSN(pagep)) ? 0 : LOG_COMPARE(lsnp, &LSN(pagep));
702	cmp_p = LOG_COMPARE(&LSN(pagep), &copy_lsn);
703
704	/*
705	 * This page got extended by a later allocation,
706	 * but its allocation was not in the scope of this
707	 * recovery pass.
708	 */
709	if (IS_ZERO_LSN(LSN(pagep)))
710		cmp_p = 0;
711
712	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &copy_lsn);
713	if (DB_REDO(op) &&
714	    (cmp_p == 0 ||
715	    (IS_ZERO_LSN(copy_lsn) &&
716	    LOG_COMPARE(&LSN(pagep), &argp->meta_lsn) <= 0))) {
717		/* Need to redo the deallocation. */
718		/*
719		 * The page can be truncated if it was truncated at runtime
720		 * and the current metapage reflects the truncation.
721		 */
722		if (is_meta && meta->last_pgno <= argp->pgno &&
723		    argp->last_pgno <= argp->pgno) {
724			if ((ret = __memp_fput(mpf, ip,
725			    pagep, DB_PRIORITY_VERY_LOW)) != 0)
726				goto out;
727			pagep = NULL;
728trunc:			if ((ret = __memp_ftruncate(mpf, NULL, ip,
729			    argp->pgno, MP_TRUNC_RECOVER)) != 0)
730				goto out;
731		} else if (argp->last_pgno == argp->pgno) {
732			/* The page was truncated at runtime, zero it out. */
733			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
734			P_INIT(pagep, 0, PGNO_INVALID,
735			    PGNO_INVALID, PGNO_INVALID, 0, P_INVALID);
736			ZERO_LSN(pagep->lsn);
737		} else {
738			REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
739			P_INIT(pagep, file_dbp->pgsize,
740			    argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
741			pagep->lsn = *lsnp;
742
743		}
744	} else if (cmp_n == 0 && DB_UNDO(op)) {
745		/* Need to reallocate the page. */
746		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
747		memcpy(pagep, argp->header.data, argp->header.size);
748		if (data)
749			memcpy((u_int8_t*)pagep + HOFFSET(pagep),
750			     argp->data.data, argp->data.size);
751	}
752	if (pagep != NULL &&
753	    (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
754		goto out;
755
756	pagep = NULL;
757#ifdef HAVE_FTRUNCATE
758	/*
759	 * If we are keeping an in memory free list remove this
760	 * element from the list.
761	 */
762	if (op == DB_TXN_ABORT && argp->pgno != argp->last_pgno) {
763		db_pgno_t *lp;
764		u_int32_t nelem, pos;
765
766		if ((ret = __memp_get_freelist(mpf, &nelem, &lp)) != 0)
767			goto out;
768		if (lp != NULL) {
769			pos = 0;
770			if (!is_meta) {
771				__db_freelist_pos(argp->pgno, lp, nelem, &pos);
772
773				/*
774				 * If we aborted after logging but before
775				 * updating the free list don't do anything.
776				*/
777				if (argp->pgno != lp[pos]) {
778					DB_ASSERT(env,
779					    argp->meta_pgno == lp[pos]);
780					goto done;
781				}
782				DB_ASSERT(env,
783				    argp->meta_pgno == lp[pos - 1]);
784			} else if (nelem != 0 && argp->pgno != lp[pos])
785				goto done;
786
787			if (pos < nelem)
788				memmove(&lp[pos], &lp[pos + 1],
789				    ((nelem - pos) - 1) * sizeof(*lp));
790
791			/* Shrink the list */
792			if ((ret =
793			    __memp_extend_freelist(mpf, nelem - 1, &lp)) != 0)
794				goto out;
795		}
796	}
797#endif
798done:
799	if (meta != NULL &&
800	     (ret = __memp_fput(mpf, ip,  meta, file_dbp->priority)) != 0)
801		goto out;
802	meta = NULL;
803	ret = 0;
804
805out:	if (pagep != NULL)
806		(void)__memp_fput(mpf, ip,  pagep, file_dbp->priority);
807	if (meta != NULL)
808		(void)__memp_fput(mpf, ip,  meta, file_dbp->priority);
809
810	return (ret);
811}
812
813/*
814 * __db_pg_free_recover --
815 *	Recovery function for pg_free.
816 *
817 * PUBLIC: int __db_pg_free_recover
818 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
819 */
820int
821__db_pg_free_recover(env, dbtp, lsnp, op, info)
822	ENV *env;
823	DBT *dbtp;
824	DB_LSN *lsnp;
825	db_recops op;
826	void *info;
827{
828	__db_pg_free_args *argp;
829	DB *file_dbp;
830	DBC *dbc;
831	DB_MPOOLFILE *mpf;
832	DB_THREAD_INFO *ip;
833	int ret;
834
835	ip = ((DB_TXNHEAD *)info)->thread_info;
836	REC_PRINT(__db_pg_free_print);
837	REC_INTRO(__db_pg_free_read, ip, 0);
838
839	ret = __db_pg_free_recover_int(env, ip,
840	     (__db_pg_freedata_args *)argp, file_dbp, lsnp, mpf, op, 0);
841
842done:	*lsnp = argp->prev_lsn;
843out:
844	REC_CLOSE;
845}
846
847/*
848 * __db_pg_freedata_recover --
849 *	Recovery function for pg_freedata.
850 *
851 * PUBLIC: int __db_pg_freedata_recover
852 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
853 */
854int
855__db_pg_freedata_recover(env, dbtp, lsnp, op, info)
856	ENV *env;
857	DBT *dbtp;
858	DB_LSN *lsnp;
859	db_recops op;
860	void *info;
861{
862	__db_pg_freedata_args *argp;
863	DB *file_dbp;
864	DBC *dbc;
865	DB_MPOOLFILE *mpf;
866	DB_THREAD_INFO *ip;
867	int ret;
868
869	ip = ((DB_TXNHEAD *)info)->thread_info;
870	REC_PRINT(__db_pg_freedata_print);
871	REC_INTRO(__db_pg_freedata_read, ip, 0);
872
873	ret = __db_pg_free_recover_int(env,
874	    ip, argp, file_dbp, lsnp, mpf, op, 1);
875
876done:	*lsnp = argp->prev_lsn;
877out:
878	REC_CLOSE;
879}
880
881/*
882 * __db_cksum_recover --
883 *	Recovery function for checksum failure log record.
884 *
885 * PUBLIC: int __db_cksum_recover __P((ENV *,
886 * PUBLIC:      DBT *, DB_LSN *, db_recops, void *));
887 */
888int
889__db_cksum_recover(env, dbtp, lsnp, op, info)
890	ENV *env;
891	DBT *dbtp;
892	DB_LSN *lsnp;
893	db_recops op;
894	void *info;
895{
896	__db_cksum_args *argp;
897	int ret;
898
899	COMPQUIET(info, NULL);
900	COMPQUIET(lsnp, NULL);
901	COMPQUIET(op, DB_TXN_ABORT);
902
903	REC_PRINT(__db_cksum_print);
904
905	if ((ret = __db_cksum_read(env, dbtp->data, &argp)) != 0)
906		return (ret);
907
908	/*
909	 * We had a checksum failure -- the only option is to run catastrophic
910	 * recovery.
911	 */
912	if (F_ISSET(env, ENV_RECOVER_FATAL))
913		ret = 0;
914	else {
915		__db_errx(env,
916		    "Checksum failure requires catastrophic recovery");
917		ret = __env_panic(env, DB_RUNRECOVERY);
918	}
919
920	__os_free(env, argp);
921	return (ret);
922}
923
924/*
925 * __db_pg_init_recover --
926 *	Recovery function to reinit pages after truncation.
927 *
928 * PUBLIC: int __db_pg_init_recover
929 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
930 */
931int
932__db_pg_init_recover(env, dbtp, lsnp, op, info)
933	ENV *env;
934	DBT *dbtp;
935	DB_LSN *lsnp;
936	db_recops op;
937	void *info;
938{
939	__db_pg_init_args *argp;
940	DB_THREAD_INFO *ip;
941	DB *file_dbp;
942	DBC *dbc;
943	DB_LSN copy_lsn;
944	DB_MPOOLFILE *mpf;
945	PAGE *pagep;
946	int cmp_n, cmp_p, ret, type;
947
948	ip = ((DB_TXNHEAD *)info)->thread_info;
949	REC_PRINT(__db_pg_init_print);
950	REC_INTRO(__db_pg_init_read, ip, 0);
951
952	mpf = file_dbp->mpf;
953	if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) {
954		if (DB_UNDO(op)) {
955			if (ret == DB_PAGE_NOTFOUND)
956				goto done;
957			else {
958				ret = __db_pgerr(file_dbp, argp->pgno, ret);
959				goto out;
960			}
961		}
962
963		/*
964		 * This page was truncated and may simply not have
965		 * had an item written to it yet.  This should only
966		 * happen on hash databases, so confirm that.
967		 */
968		DB_ASSERT(env, file_dbp->type == DB_HASH);
969		if ((ret = __memp_fget(mpf, &argp->pgno,
970		    ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) {
971			ret = __db_pgerr(file_dbp, argp->pgno, ret);
972			goto out;
973		}
974	}
975
976	(void)__ua_memcpy(&copy_lsn, &LSN(argp->header.data), sizeof(DB_LSN));
977	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
978	cmp_p = LOG_COMPARE(&LSN(pagep), &copy_lsn);
979	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &copy_lsn);
980	CHECK_ABORT(env, op, cmp_n, &LSN(pagep), lsnp);
981
982	if (cmp_p == 0 && DB_REDO(op)) {
983		if (TYPE(pagep) == P_HASH)
984			type = P_HASH;
985		else
986			type = file_dbp->type == DB_RECNO ? P_LRECNO : P_LBTREE;
987		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
988		P_INIT(pagep, file_dbp->pgsize, PGNO(pagep), PGNO_INVALID,
989		    PGNO_INVALID, TYPE(pagep) == P_HASH ? 0 : 1, type);
990		pagep->lsn = *lsnp;
991	} else if (cmp_n == 0 && DB_UNDO(op)) {
992		/* Put the data back on the page. */
993		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
994		memcpy(pagep, argp->header.data, argp->header.size);
995		if (argp->data.size > 0)
996			memcpy((u_int8_t*)pagep + HOFFSET(pagep),
997			     argp->data.data, argp->data.size);
998	}
999	if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1000		goto out;
1001
1002done:	*lsnp = argp->prev_lsn;
1003out:
1004	REC_CLOSE;
1005}
1006
1007/*
1008 * __db_pg_trunc_recover --
1009 *	Recovery function for pg_trunc.
1010 *
1011 * PUBLIC: int __db_pg_trunc_recover
1012 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1013 */
1014int
1015__db_pg_trunc_recover(env, dbtp, lsnp, op, info)
1016	ENV *env;
1017	DBT *dbtp;
1018	DB_LSN *lsnp;
1019	db_recops op;
1020	void *info;
1021{
1022#ifdef HAVE_FTRUNCATE
1023	__db_pg_trunc_args *argp;
1024	DB_THREAD_INFO *ip;
1025	DB *file_dbp;
1026	DBC *dbc;
1027	DBMETA *meta;
1028	DB_MPOOLFILE *mpf;
1029	PAGE *pagep;
1030	db_pglist_t *pglist, *lp;
1031	db_pgno_t last_pgno, *list;
1032	u_int32_t felem, nelem, pos;
1033	int ret;
1034
1035	ip = ((DB_TXNHEAD *)info)->thread_info;
1036	REC_PRINT(__db_pg_trunc_print);
1037	REC_INTRO(__db_pg_trunc_read, ip, 1);
1038
1039	pglist = (db_pglist_t *) argp->list.data;
1040	nelem = argp->list.size / sizeof(db_pglist_t);
1041	if (DB_REDO(op)) {
1042		/*
1043		 * First call __db_pg_truncate to find the truncation
1044		 * point, truncate the file and return the new last_pgno.
1045		 */
1046		last_pgno = argp->last_pgno;
1047		if ((ret = __db_pg_truncate(dbc, NULL, pglist,
1048		    NULL, &nelem, argp->next_free, &last_pgno, lsnp, 1)) != 0)
1049			goto out;
1050
1051		if (argp->last_free != PGNO_INVALID) {
1052			/*
1053			 * Update the next pointer of the last page in
1054			 * the freelist.  If the truncation point is
1055			 * beyond next_free then this is still in the freelist
1056			 * otherwise the last_free page is at the end.
1057			 */
1058			if ((ret = __memp_fget(mpf,
1059			    &argp->last_free, ip, NULL, 0, &meta)) == 0) {
1060				if (LOG_COMPARE(&LSN(meta),
1061				     &argp->last_lsn) == 0) {
1062					REC_DIRTY(mpf,
1063					    ip, dbc->priority, &meta);
1064					if (pglist->pgno > last_pgno)
1065						NEXT_PGNO(meta) = PGNO_INVALID;
1066					else
1067						NEXT_PGNO(meta) = pglist->pgno;
1068					LSN(meta) = *lsnp;
1069				}
1070				if ((ret = __memp_fput(mpf, ip,
1071				    meta, file_dbp->priority)) != 0)
1072					goto out;
1073				meta = NULL;
1074			} else if (ret != DB_PAGE_NOTFOUND)
1075				goto out;
1076		}
1077		if ((ret = __memp_fget(mpf, &argp->meta, ip, NULL,
1078		    0, &meta)) != 0)
1079			goto out;
1080		if (LOG_COMPARE(&LSN(meta), &argp->meta_lsn) == 0) {
1081			REC_DIRTY(mpf, ip, dbc->priority, &meta);
1082			if (argp->last_free == PGNO_INVALID) {
1083				if (nelem == 0)
1084					meta->free = PGNO_INVALID;
1085				else
1086					meta->free = pglist->pgno;
1087			}
1088			meta->last_pgno = last_pgno;
1089			LSN(meta) = *lsnp;
1090		}
1091	} else {
1092		/* Put the free list back in its original order. */
1093		for (lp = pglist; lp < &pglist[nelem]; lp++) {
1094			if ((ret = __memp_fget(mpf, &lp->pgno, ip,
1095			    NULL, DB_MPOOL_CREATE, &pagep)) != 0)
1096				goto out;
1097			if (IS_ZERO_LSN(LSN(pagep)) ||
1098			     LOG_COMPARE(&LSN(pagep), lsnp) == 0) {
1099				REC_DIRTY(mpf, ip, dbc->priority, &pagep);
1100				P_INIT(pagep, file_dbp->pgsize, lp->pgno,
1101				    PGNO_INVALID, lp->next_pgno, 0, P_INVALID);
1102				LSN(pagep) = lp->lsn;
1103			}
1104			if ((ret = __memp_fput(mpf,
1105			    ip, pagep, file_dbp->priority)) != 0)
1106				goto out;
1107		}
1108		/*
1109		 * Link the truncated part back into the free list.
1110		 * Its either after the last_free page or direclty
1111		 * linked to the metadata page.
1112		 */
1113		if (argp->last_free != PGNO_INVALID) {
1114			if ((ret = __memp_fget(mpf, &argp->last_free,
1115			    ip, NULL, DB_MPOOL_EDIT, &meta)) == 0) {
1116				if (LOG_COMPARE(&LSN(meta), lsnp) == 0) {
1117					NEXT_PGNO(meta) = argp->next_free;
1118					LSN(meta) = argp->last_lsn;
1119				}
1120				if ((ret = __memp_fput(mpf, ip,
1121				    meta, file_dbp->priority)) != 0)
1122					goto out;
1123			} else if (ret != DB_PAGE_NOTFOUND)
1124				goto out;
1125			meta = NULL;
1126		}
1127		if ((ret = __memp_fget(mpf, &argp->meta,
1128		    ip, NULL, DB_MPOOL_EDIT, &meta)) != 0)
1129			goto out;
1130		if (LOG_COMPARE(&LSN(meta), lsnp) == 0) {
1131			REC_DIRTY(mpf, ip, dbc->priority, &meta);
1132			/*
1133			 * If we had to break up the list last_pgno
1134			 * may only represent the end of the block.
1135			 */
1136			if (meta->last_pgno < argp->last_pgno)
1137				meta->last_pgno = argp->last_pgno;
1138			if (argp->last_free == PGNO_INVALID)
1139				meta->free = argp->next_free;
1140			LSN(meta) = argp->meta_lsn;
1141		}
1142	}
1143
1144	if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0)
1145		goto out;
1146
1147	if (op == DB_TXN_ABORT) {
1148		/*
1149		 * Put the pages back on the in memory free list.
1150		 * If this is part of a multi-record truncate then
1151		 * we need to find this batch, it may not be at the end.
1152		 * If we aborted while writing one of the log records
1153		 * then this set may still be in the list.
1154		 */
1155		if ((ret = __memp_get_freelist(mpf, &felem, &list)) != 0)
1156			goto out;
1157		if (list != NULL) {
1158			if (felem != 0 && list[felem - 1] > pglist->pgno) {
1159				__db_freelist_pos(
1160				    pglist->pgno, list, felem, &pos);
1161				DB_ASSERT(env, pos < felem);
1162				if (pglist->pgno == list[pos])
1163					goto done;
1164				pos++;
1165			} else if (felem != 0 &&
1166			    list[felem - 1] == pglist->pgno)
1167				goto done;
1168			else
1169				pos = felem;
1170			if ((ret = __memp_extend_freelist(
1171			    mpf, felem + nelem, &list)) != 0)
1172				goto out;
1173			if (pos != felem)
1174				memmove(&list[nelem + pos], &list[pos],
1175				    sizeof(*list) * (felem - pos));
1176			for (lp = pglist; lp < &pglist[nelem]; lp++)
1177				list[pos++] = lp->pgno;
1178		}
1179	}
1180
1181done:	*lsnp = argp->prev_lsn;
1182	ret = 0;
1183
1184out:	REC_CLOSE;
1185#else
1186	/*
1187	 * If HAVE_FTRUNCATE is not defined, we'll never see pg_trunc records
1188	 * to recover.
1189	 */
1190	COMPQUIET(env, NULL);
1191	COMPQUIET(dbtp, NULL);
1192	COMPQUIET(lsnp, NULL);
1193	COMPQUIET(op,  DB_TXN_ABORT);
1194	COMPQUIET(info, NULL);
1195	return (EINVAL);
1196#endif
1197}
1198/*
1199 * __db_pg_sort_44_recover --
1200 *	Recovery function for pg_sort.
1201 * This is deprecated and kept for replication upgrades.
1202 *
1203 * PUBLIC: int __db_pg_sort_44_recover
1204 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1205 */
1206int
1207__db_pg_sort_44_recover(env, dbtp, lsnp, op, info)
1208	ENV *env;
1209	DBT *dbtp;
1210	DB_LSN *lsnp;
1211	db_recops op;
1212	void *info;
1213{
1214#ifdef HAVE_FTRUNCATE
1215	__db_pg_sort_44_args *argp;
1216	DB_THREAD_INFO *ip;
1217	DB *file_dbp;
1218	DBC *dbc;
1219	DBMETA *meta;
1220	DB_MPOOLFILE *mpf;
1221	PAGE *pagep;
1222	db_pglist_t *pglist, *lp;
1223	db_pgno_t pgno, *list;
1224	u_int32_t felem, nelem;
1225	int ret;
1226
1227	ip = ((DB_TXNHEAD *)info)->thread_info;
1228	REC_PRINT(__db_pg_sort_44_print);
1229	REC_INTRO(__db_pg_sort_44_read, ip, 1);
1230
1231	pglist = (db_pglist_t *) argp->list.data;
1232	nelem = argp->list.size / sizeof(db_pglist_t);
1233	if (DB_REDO(op)) {
1234		pgno = argp->last_pgno;
1235		__db_freelist_sort(pglist, nelem);
1236		if ((ret = __db_pg_truncate(dbc, NULL,
1237		    pglist, NULL, &nelem, PGNO_INVALID, &pgno, lsnp, 1)) != 0)
1238			goto out;
1239
1240		if (argp->last_free != PGNO_INVALID) {
1241			if ((ret = __memp_fget(mpf,
1242			    &argp->last_free, ip, NULL, 0, &meta)) == 0) {
1243				if (LOG_COMPARE(&LSN(meta),
1244				     &argp->last_lsn) == 0) {
1245					REC_DIRTY(mpf,
1246					    ip, dbc->priority, &meta);
1247					NEXT_PGNO(meta) = PGNO_INVALID;
1248					LSN(meta) = *lsnp;
1249				}
1250				if ((ret = __memp_fput(mpf, ip,
1251				    meta, file_dbp->priority)) != 0)
1252					goto out;
1253				meta = NULL;
1254			} else if (ret != DB_PAGE_NOTFOUND)
1255				goto out;
1256		}
1257		if ((ret = __memp_fget(mpf, &argp->meta, ip, NULL,
1258		    0, &meta)) != 0)
1259			goto out;
1260		if (LOG_COMPARE(&LSN(meta), &argp->meta_lsn) == 0) {
1261			REC_DIRTY(mpf, ip, dbc->priority, &meta);
1262			if (argp->last_free == PGNO_INVALID) {
1263				if (nelem == 0)
1264					meta->free = PGNO_INVALID;
1265				else
1266					meta->free = pglist->pgno;
1267			}
1268			meta->last_pgno = pgno;
1269			LSN(meta) = *lsnp;
1270		}
1271	} else {
1272		/* Put the free list back in its original order. */
1273		for (lp = pglist; lp < &pglist[nelem]; lp++) {
1274			if ((ret = __memp_fget(mpf, &lp->pgno, ip,
1275			    NULL, DB_MPOOL_CREATE, &pagep)) != 0)
1276				goto out;
1277			if (IS_ZERO_LSN(LSN(pagep)) ||
1278			     LOG_COMPARE(&LSN(pagep), lsnp) == 0) {
1279				REC_DIRTY(mpf, ip, dbc->priority, &pagep);
1280				if (lp == &pglist[nelem - 1])
1281					pgno = PGNO_INVALID;
1282				else
1283					pgno = lp[1].pgno;
1284
1285				P_INIT(pagep, file_dbp->pgsize,
1286				    lp->pgno, PGNO_INVALID, pgno, 0, P_INVALID);
1287				LSN(pagep) = lp->lsn;
1288			}
1289			if ((ret = __memp_fput(mpf,
1290			    ip, pagep, file_dbp->priority)) != 0)
1291				goto out;
1292		}
1293		if (argp->last_free != PGNO_INVALID) {
1294			if ((ret = __memp_fget(mpf, &argp->last_free,
1295			    ip, NULL, DB_MPOOL_EDIT, &meta)) == 0) {
1296				if (LOG_COMPARE(&LSN(meta), lsnp) == 0) {
1297					NEXT_PGNO(meta) = pglist->pgno;
1298					LSN(meta) = argp->last_lsn;
1299				}
1300				if ((ret = __memp_fput(mpf, ip,
1301				    meta, file_dbp->priority)) != 0)
1302					goto out;
1303			} else if (ret != DB_PAGE_NOTFOUND)
1304				goto out;
1305			meta = NULL;
1306		}
1307		if ((ret = __memp_fget(mpf, &argp->meta,
1308		    ip, NULL, DB_MPOOL_EDIT, &meta)) != 0)
1309			goto out;
1310		if (LOG_COMPARE(&LSN(meta), lsnp) == 0) {
1311			REC_DIRTY(mpf, ip, dbc->priority, &meta);
1312			meta->last_pgno = argp->last_pgno;
1313			if (argp->last_free == PGNO_INVALID)
1314				meta->free = pglist->pgno;
1315			LSN(meta) = argp->meta_lsn;
1316		}
1317	}
1318	if (op == DB_TXN_ABORT) {
1319		if ((ret = __memp_get_freelist(mpf, &felem, &list)) != 0)
1320			goto out;
1321		if (list != NULL) {
1322			DB_ASSERT(env, felem == 0 ||
1323			    argp->last_free == list[felem - 1]);
1324			if ((ret = __memp_extend_freelist(
1325			    mpf, felem + nelem, &list)) != 0)
1326				goto out;
1327			for (lp = pglist; lp < &pglist[nelem]; lp++)
1328				list[felem++] = lp->pgno;
1329		}
1330	}
1331
1332	if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0)
1333		goto out;
1334
1335done:	*lsnp = argp->prev_lsn;
1336	ret = 0;
1337
1338out:	REC_CLOSE;
1339#else
1340	/*
1341	 * If HAVE_FTRUNCATE is not defined, we'll never see pg_sort records
1342	 * to recover.
1343	 */
1344	COMPQUIET(env, NULL);
1345	COMPQUIET(dbtp, NULL);
1346	COMPQUIET(lsnp, NULL);
1347	COMPQUIET(op,  DB_TXN_ABORT);
1348	COMPQUIET(info, NULL);
1349	return (EINVAL);
1350#endif
1351}
1352
1353/*
1354 * __db_pg_alloc_42_recover --
1355 *	Recovery function for pg_alloc.
1356 *
1357 * PUBLIC: int __db_pg_alloc_42_recover
1358 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1359 */
1360int
1361__db_pg_alloc_42_recover(env, dbtp, lsnp, op, info)
1362	ENV *env;
1363	DBT *dbtp;
1364	DB_LSN *lsnp;
1365	db_recops op;
1366	void *info;
1367{
1368	__db_pg_alloc_42_args *argp;
1369	DB_THREAD_INFO *ip;
1370	DB *file_dbp;
1371	DBC *dbc;
1372	DBMETA *meta;
1373	DB_MPOOLFILE *mpf;
1374	PAGE *pagep;
1375	db_pgno_t pgno;
1376	int cmp_n, cmp_p, created, level, ret;
1377
1378	ip = ((DB_TXNHEAD *)info)->thread_info;
1379	meta = NULL;
1380	pagep = NULL;
1381	created = 0;
1382	REC_PRINT(__db_pg_alloc_42_print);
1383	REC_INTRO(__db_pg_alloc_42_read, ip, 0);
1384
1385	/*
1386	 * Fix up the metadata page.  If we're redoing the operation, we have
1387	 * to get the metadata page and update its LSN and its free pointer.
1388	 * If we're undoing the operation and the page was ever created, we put
1389	 * it on the freelist.
1390	 */
1391	pgno = PGNO_BASE_MD;
1392	if ((ret = __memp_fget(mpf, &pgno, ip, NULL, 0, &meta)) != 0) {
1393		/* The metadata page must always exist on redo. */
1394		if (DB_REDO(op)) {
1395			ret = __db_pgerr(file_dbp, pgno, ret);
1396			goto out;
1397		} else
1398			goto done;
1399	}
1400	cmp_n = LOG_COMPARE(lsnp, &LSN(meta));
1401	cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn);
1402	CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn);
1403	if (cmp_p == 0 && DB_REDO(op)) {
1404		/* Need to redo update described. */
1405		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
1406		LSN(meta) = *lsnp;
1407		meta->free = argp->next;
1408		if (argp->pgno > meta->last_pgno)
1409			meta->last_pgno = argp->pgno;
1410	} else if (cmp_n == 0 && DB_UNDO(op)) {
1411		goto no_rollback;
1412	}
1413
1414	/*
1415	 * Fix up the allocated page. If the page does not exist
1416	 * and we can truncate it then don't create it.
1417	 * Otherwise if we're redoing the operation, we have
1418	 * to get the page (creating it if it doesn't exist), and update its
1419	 * LSN.  If we're undoing the operation, we have to reset the page's
1420	 * LSN and put it on the free list, or truncate it.
1421	 */
1422	if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) {
1423		/*
1424		 * We have to be able to identify if a page was newly
1425		 * created so we can recover it properly.  We cannot simply
1426		 * look for an empty header, because hash uses a pgin
1427		 * function that will set the header.  Instead, we explicitly
1428		 * try for the page without CREATE and if that fails, then
1429		 * create it.
1430		 */
1431		if ((ret = __memp_fget(mpf, &argp->pgno,
1432		    ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0) {
1433			if (DB_UNDO(op) && ret == ENOSPC)
1434				goto do_truncate;
1435			ret = __db_pgerr(file_dbp, argp->pgno, ret);
1436			goto out;
1437		}
1438		created = 1;
1439	}
1440
1441	/* Fix up the allocated page. */
1442	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
1443	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->page_lsn);
1444
1445	/*
1446	 * If an initial allocation is aborted and then reallocated during
1447	 * an archival restore the log record will have an LSN for the page
1448	 * but the page will be empty.
1449	 */
1450	if (IS_ZERO_LSN(LSN(pagep)) ||
1451	    (IS_ZERO_LSN(argp->page_lsn) && IS_INIT_LSN(LSN(pagep))))
1452		cmp_p = 0;
1453
1454	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->page_lsn);
1455	/*
1456	 * Another special case we have to handle is if we ended up with a
1457	 * page of all 0's which can happen if we abort between allocating a
1458	 * page in mpool and initializing it.  In that case, even if we're
1459	 * undoing, we need to re-initialize the page.
1460	 */
1461	if (DB_REDO(op) && cmp_p == 0) {
1462		/* Need to redo update described. */
1463		switch (argp->ptype) {
1464		case P_LBTREE:
1465		case P_LRECNO:
1466		case P_LDUP:
1467			level = LEAFLEVEL;
1468			break;
1469		default:
1470			level = 0;
1471			break;
1472		}
1473		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1474		P_INIT(pagep, file_dbp->pgsize,
1475		    argp->pgno, PGNO_INVALID, PGNO_INVALID, level, argp->ptype);
1476
1477		pagep->lsn = *lsnp;
1478	} else if (DB_UNDO(op) && (cmp_n == 0 || created)) {
1479		/*
1480		 * This is where we handle the case of a 0'd page (pagep->pgno
1481		 * is equal to PGNO_INVALID).
1482		 * Undo the allocation, reinitialize the page and
1483		 * link its next pointer to the free list.
1484		 */
1485		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1486		P_INIT(pagep, file_dbp->pgsize,
1487		    argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
1488
1489		pagep->lsn = argp->page_lsn;
1490	}
1491
1492do_truncate:
1493	/*
1494	 * We cannot undo things from 4.2 land, because we nolonger
1495	 * have limbo processing.
1496	 */
1497	if ((pagep == NULL || IS_ZERO_LSN(LSN(pagep))) &&
1498	    IS_ZERO_LSN(argp->page_lsn) && DB_UNDO(op)) {
1499no_rollback:	__db_errx(env,
1500"Cannot replicate prepared transactions from master running release 4.2 ");
1501		ret = __env_panic(env, EINVAL);
1502	}
1503
1504	if (pagep != NULL &&
1505	    (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1506		goto out;
1507	pagep = NULL;
1508
1509	if ((ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0)
1510		goto out;
1511	meta = NULL;
1512
1513done:	*lsnp = argp->prev_lsn;
1514	ret = 0;
1515
1516out:	if (pagep != NULL)
1517		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
1518	if (meta != NULL)
1519		(void)__memp_fput(mpf, ip, meta, file_dbp->priority);
1520	REC_CLOSE;
1521}
1522
1523/*
1524 * __db_pg_free_recover_42_int --
1525 */
1526static int
1527__db_pg_free_recover_42_int(env, ip, argp, file_dbp, lsnp, mpf, op, data)
1528	ENV *env;
1529	DB_THREAD_INFO *ip;
1530	__db_pg_freedata_42_args *argp;
1531	DB *file_dbp;
1532	DB_LSN *lsnp;
1533	DB_MPOOLFILE *mpf;
1534	db_recops op;
1535	int data;
1536{
1537	DBMETA *meta;
1538	DB_LSN copy_lsn;
1539	PAGE *pagep, *prevp;
1540	int cmp_n, cmp_p, is_meta, ret;
1541
1542	meta = NULL;
1543	pagep = NULL;
1544	prevp = NULL;
1545
1546	/*
1547	 * Get the "metapage".  This will either be the metapage
1548	 * or the previous page in the free list if we are doing
1549	 * sorted allocations.  If its a previous page then
1550	 * we will not be truncating.
1551	 */
1552	is_meta = argp->meta_pgno == PGNO_BASE_MD;
1553
1554	REC_FGET(mpf, ip, argp->meta_pgno, &meta, check_meta);
1555
1556	if (argp->meta_pgno != PGNO_BASE_MD)
1557		prevp = (PAGE *)meta;
1558
1559	cmp_n = LOG_COMPARE(lsnp, &LSN(meta));
1560	cmp_p = LOG_COMPARE(&LSN(meta), &argp->meta_lsn);
1561	CHECK_LSN(env, op, cmp_p, &LSN(meta), &argp->meta_lsn);
1562
1563	/*
1564	 * Fix up the metadata page.  If we're redoing or undoing the operation
1565	 * we get the page and update its LSN, last and free pointer.
1566	 */
1567	if (cmp_p == 0 && DB_REDO(op)) {
1568		/* Need to redo the deallocation. */
1569		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
1570		if (prevp == NULL)
1571			meta->free = argp->pgno;
1572		else
1573			NEXT_PGNO(prevp) = argp->pgno;
1574		/*
1575		 * If this was a compensating transaction and
1576		 * we are a replica, then we never executed the
1577		 * original allocation which incremented meta->free.
1578		 */
1579		if (prevp == NULL && meta->last_pgno < meta->free)
1580			meta->last_pgno = meta->free;
1581		LSN(meta) = *lsnp;
1582	} else if (cmp_n == 0 && DB_UNDO(op)) {
1583		/* Need to undo the deallocation. */
1584		REC_DIRTY(mpf, ip, file_dbp->priority, &meta);
1585		if (prevp == NULL)
1586			meta->free = argp->next;
1587		else
1588			NEXT_PGNO(prevp) = argp->next;
1589		LSN(meta) = argp->meta_lsn;
1590		if (prevp == NULL && meta->last_pgno < argp->pgno)
1591			meta->last_pgno = argp->pgno;
1592	}
1593
1594check_meta:
1595	if (ret != 0 && is_meta) {
1596		/* The metadata page must always exist. */
1597		ret = __db_pgerr(file_dbp, argp->meta_pgno, ret);
1598		goto out;
1599	}
1600
1601	/*
1602	 * Get the freed page.  If we support truncate then don't
1603	 * create the page if we are going to free it.  If we're
1604	 * redoing the operation we get the page and explicitly discard
1605	 * its contents, then update its LSN.  If we're undoing the
1606	 * operation, we get the page and restore its header.
1607	 * If we don't support truncate, then we must create the page
1608	 * and roll it back.
1609	 */
1610	if ((ret = __memp_fget(mpf, &argp->pgno,
1611	    ip, NULL, DB_MPOOL_CREATE, &pagep)) != 0)
1612		goto out;
1613
1614	(void)__ua_memcpy(&copy_lsn, &LSN(argp->header.data), sizeof(DB_LSN));
1615	cmp_n = IS_ZERO_LSN(LSN(pagep)) ? 0 : LOG_COMPARE(lsnp, &LSN(pagep));
1616	cmp_p = LOG_COMPARE(&LSN(pagep), &copy_lsn);
1617
1618	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &copy_lsn);
1619	if (DB_REDO(op) &&
1620	    (cmp_p == 0 ||
1621	    (IS_ZERO_LSN(copy_lsn) &&
1622	    LOG_COMPARE(&LSN(pagep), &argp->meta_lsn) <= 0))) {
1623		/* Need to redo the deallocation. */
1624		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1625		P_INIT(pagep, file_dbp->pgsize,
1626		    argp->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
1627		pagep->lsn = *lsnp;
1628	} else if (cmp_n == 0 && DB_UNDO(op)) {
1629		/* Need to reallocate the page. */
1630		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1631		memcpy(pagep, argp->header.data, argp->header.size);
1632		if (data)
1633			memcpy((u_int8_t*)pagep + HOFFSET(pagep),
1634			     argp->data.data, argp->data.size);
1635	}
1636	if (pagep != NULL &&
1637	    (ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1638		goto out;
1639
1640	pagep = NULL;
1641	if (meta != NULL &&
1642	    (ret = __memp_fput(mpf, ip, meta, file_dbp->priority)) != 0)
1643		goto out;
1644	meta = NULL;
1645
1646	ret = 0;
1647
1648out:	if (pagep != NULL)
1649		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
1650	if (meta != NULL)
1651		(void)__memp_fput(mpf, ip, meta, file_dbp->priority);
1652
1653	return (ret);
1654}
1655
1656/*
1657 * __db_pg_free_42_recover --
1658 *	Recovery function for pg_free.
1659 *
1660 * PUBLIC: int __db_pg_free_42_recover
1661 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1662 */
1663int
1664__db_pg_free_42_recover(env, dbtp, lsnp, op, info)
1665	ENV *env;
1666	DBT *dbtp;
1667	DB_LSN *lsnp;
1668	db_recops op;
1669	void *info;
1670{
1671	__db_pg_free_42_args *argp;
1672	DB *file_dbp;
1673	DBC *dbc;
1674	DB_MPOOLFILE *mpf;
1675	DB_THREAD_INFO *ip;
1676	int ret;
1677
1678	ip = ((DB_TXNHEAD *)info)->thread_info;
1679	REC_PRINT(__db_pg_free_42_print);
1680	REC_INTRO(__db_pg_free_42_read, ip, 0);
1681
1682	ret = __db_pg_free_recover_42_int(env, ip,
1683	     (__db_pg_freedata_42_args *)argp, file_dbp, lsnp, mpf, op, 0);
1684
1685done:	*lsnp = argp->prev_lsn;
1686out:
1687	REC_CLOSE;
1688}
1689
1690/*
1691 * __db_pg_freedata_42_recover --
1692 *	Recovery function for pg_freedata.
1693 *
1694 * PUBLIC: int __db_pg_freedata_42_recover
1695 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1696 */
1697int
1698__db_pg_freedata_42_recover(env, dbtp, lsnp, op, info)
1699	ENV *env;
1700	DBT *dbtp;
1701	DB_LSN *lsnp;
1702	db_recops op;
1703	void *info;
1704{
1705	__db_pg_freedata_42_args *argp;
1706	DB *file_dbp;
1707	DBC *dbc;
1708	DB_MPOOLFILE *mpf;
1709	DB_THREAD_INFO *ip;
1710	int ret;
1711
1712	ip = ((DB_TXNHEAD *)info)->thread_info;
1713	REC_PRINT(__db_pg_freedata_42_print);
1714	REC_INTRO(__db_pg_freedata_42_read, ip, 0);
1715
1716	ret = __db_pg_free_recover_42_int(
1717	    env, ip, argp, file_dbp, lsnp, mpf, op, 1);
1718
1719done:	*lsnp = argp->prev_lsn;
1720out:
1721	REC_CLOSE;
1722}
1723
1724/*
1725 * __db_relink_42_recover --
1726 *	Recovery function for relink.
1727 *
1728 * PUBLIC: int __db_relink_42_recover
1729 * PUBLIC:   __P((ENV *, DBT *, DB_LSN *, db_recops, void *));
1730 */
1731int
1732__db_relink_42_recover(env, dbtp, lsnp, op, info)
1733	ENV *env;
1734	DBT *dbtp;
1735	DB_LSN *lsnp;
1736	db_recops op;
1737	void *info;
1738{
1739	__db_relink_42_args *argp;
1740	DB_THREAD_INFO *ip;
1741	DB *file_dbp;
1742	DBC *dbc;
1743	DB_MPOOLFILE *mpf;
1744	PAGE *pagep;
1745	int cmp_n, cmp_p, modified, ret;
1746
1747	ip = ((DB_TXNHEAD *)info)->thread_info;
1748	pagep = NULL;
1749	REC_PRINT(__db_relink_42_print);
1750	REC_INTRO(__db_relink_42_read, ip, 0);
1751
1752	/*
1753	 * There are up to three pages we need to check -- the page, and the
1754	 * previous and next pages, if they existed.  For a page add operation,
1755	 * the current page is the result of a split and is being recovered
1756	 * elsewhere, so all we need do is recover the next page.
1757	 */
1758	if ((ret = __memp_fget(mpf, &argp->pgno, ip, NULL, 0, &pagep)) != 0) {
1759		if (DB_REDO(op)) {
1760			ret = __db_pgerr(file_dbp, argp->pgno, ret);
1761			goto out;
1762		}
1763		goto next2;
1764	}
1765	if (argp->opcode == DB_ADD_PAGE_COMPAT)
1766		goto next1;
1767
1768	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn);
1769	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn);
1770	if (cmp_p == 0 && DB_REDO(op)) {
1771		/* Redo the relink. */
1772		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1773		pagep->lsn = *lsnp;
1774	} else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) {
1775		/* Undo the relink. */
1776		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1777		pagep->next_pgno = argp->next;
1778		pagep->prev_pgno = argp->prev;
1779		pagep->lsn = argp->lsn;
1780	}
1781next1:	if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1782		goto out;
1783	pagep = NULL;
1784
1785next2:	if ((ret = __memp_fget(mpf, &argp->next, ip, NULL, 0, &pagep)) != 0) {
1786		if (DB_REDO(op)) {
1787			ret = __db_pgerr(file_dbp, argp->next, ret);
1788			goto out;
1789		}
1790		goto prev;
1791	}
1792	modified = 0;
1793	cmp_n = LOG_COMPARE(lsnp, &LSN(pagep));
1794	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn_next);
1795	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn_next);
1796	if ((argp->opcode == DB_REM_PAGE_COMPAT && cmp_p == 0 && DB_REDO(op)) ||
1797	    (argp->opcode == DB_ADD_PAGE_COMPAT && cmp_n == 0 && DB_UNDO(op))) {
1798		/* Redo the remove or undo the add. */
1799		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1800		pagep->prev_pgno = argp->prev;
1801		modified = 1;
1802	} else if ((argp->opcode == DB_REM_PAGE_COMPAT &&
1803	    cmp_n == 0 && DB_UNDO(op)) ||
1804	    (argp->opcode == DB_ADD_PAGE_COMPAT && cmp_p == 0 && DB_REDO(op))) {
1805		/* Undo the remove or redo the add. */
1806		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1807		pagep->prev_pgno = argp->pgno;
1808		modified = 1;
1809	}
1810	if (modified) {
1811		if (DB_UNDO(op))
1812			pagep->lsn = argp->lsn_next;
1813		else
1814			pagep->lsn = *lsnp;
1815	}
1816	if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1817		goto out;
1818	pagep = NULL;
1819	if (argp->opcode == DB_ADD_PAGE_COMPAT)
1820		goto done;
1821
1822prev:	if ((ret = __memp_fget(mpf, &argp->prev, ip, NULL, 0, &pagep)) != 0) {
1823		if (DB_REDO(op)) {
1824			ret = __db_pgerr(file_dbp, argp->prev, ret);
1825			goto out;
1826		}
1827		goto done;
1828	}
1829	modified = 0;
1830	cmp_p = LOG_COMPARE(&LSN(pagep), &argp->lsn_prev);
1831	CHECK_LSN(env, op, cmp_p, &LSN(pagep), &argp->lsn_prev);
1832	if (cmp_p == 0 && DB_REDO(op)) {
1833		/* Redo the relink. */
1834		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1835		pagep->next_pgno = argp->next;
1836		modified = 1;
1837	} else if (LOG_COMPARE(lsnp, &LSN(pagep)) == 0 && DB_UNDO(op)) {
1838		/* Undo the relink. */
1839		REC_DIRTY(mpf, ip, file_dbp->priority, &pagep);
1840		pagep->next_pgno = argp->pgno;
1841		modified = 1;
1842	}
1843	if (modified) {
1844		if (DB_UNDO(op))
1845			pagep->lsn = argp->lsn_prev;
1846		else
1847			pagep->lsn = *lsnp;
1848	}
1849	if ((ret = __memp_fput(mpf, ip, pagep, file_dbp->priority)) != 0)
1850		goto out;
1851	pagep = NULL;
1852
1853done:	*lsnp = argp->prev_lsn;
1854	ret = 0;
1855
1856out:	if (pagep != NULL)
1857		(void)__memp_fput(mpf, ip, pagep, file_dbp->priority);
1858	REC_CLOSE;
1859}
1860