xref: /illumos-gate/usr/src/cmd/sendmail/db/btree/bt_rec.c (revision 7c478bd9)
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[] = "@(#)bt_rec.c	10.28 (Sleepycat) 9/27/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 "db_page.h"
23 #include "shqueue.h"
24 #include "hash.h"
25 #include "btree.h"
26 #include "log.h"
27 #include "common_ext.h"
28 
29 /*
30  * __bam_pg_alloc_recover --
31  *	Recovery function for pg_alloc.
32  *
33  * PUBLIC: int __bam_pg_alloc_recover
34  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
35  */
36 int
__bam_pg_alloc_recover(logp,dbtp,lsnp,redo,info)37 __bam_pg_alloc_recover(logp, dbtp, lsnp, redo, info)
38 	DB_LOG *logp;
39 	DBT *dbtp;
40 	DB_LSN *lsnp;
41 	int redo;
42 	void *info;
43 {
44 	__bam_pg_alloc_args *argp;
45 	BTMETA *meta;
46 	DB_MPOOLFILE *mpf;
47 	PAGE *pagep;
48 	DB *file_dbp;
49 	DBC *dbc;
50 	db_pgno_t pgno;
51 	int cmp_n, cmp_p, modified, ret;
52 
53 	REC_PRINT(__bam_pg_alloc_print);
54 	REC_INTRO(__bam_pg_alloc_read);
55 
56 	/*
57 	 * Fix up the allocated page.  If we're redoing the operation, we have
58 	 * to get the page (creating it if it doesn't exist), and update its
59 	 * LSN.  If we're undoing the operation, we have to reset the page's
60 	 * LSN and put it on the free list.
61 	 *
62 	 * Fix up the metadata page.  If we're redoing the operation, we have
63 	 * to get the metadata page and update its LSN and its free pointer.
64 	 * If we're undoing the operation and the page was ever created, we put
65 	 * it on the freelist.
66 	 */
67 	pgno = PGNO_METADATA;
68 	if ((ret = memp_fget(mpf, &pgno, 0, &meta)) != 0) {
69 		/* The metadata page must always exist. */
70 		(void)__db_pgerr(file_dbp, pgno);
71 		goto out;
72 	}
73 	if ((ret = memp_fget(mpf, &argp->pgno, DB_MPOOL_CREATE, &pagep)) != 0) {
74 		/*
75 		 * We specify creation and check for it later, because this
76 		 * operation was supposed to create the page, and even in
77 		 * the undo case it's going to get linked onto the freelist
78 		 * which we're also fixing up.
79 		 */
80 		(void)__db_pgerr(file_dbp, argp->pgno);
81 		(void)memp_fput(mpf, meta, 0);
82 		goto out;
83 	}
84 
85 	/* Fix up the allocated page. */
86 	modified = 0;
87 	cmp_n = log_compare(lsnp, &LSN(pagep));
88 	cmp_p = log_compare(&LSN(pagep), &argp->page_lsn);
89 	if (cmp_p == 0 && redo) {
90 		/* Need to redo update described. */
91 		P_INIT(pagep, file_dbp->pgsize,
92 		    argp->pgno, PGNO_INVALID, PGNO_INVALID, 0, argp->ptype);
93 
94 		pagep->lsn = *lsnp;
95 		modified = 1;
96 	} else if (cmp_n == 0 && !redo) {
97 		/* Need to undo update described. */
98 		P_INIT(pagep, file_dbp->pgsize,
99 		    argp->pgno, PGNO_INVALID, meta->free, 0, P_INVALID);
100 
101 		pagep->lsn = argp->page_lsn;
102 		modified = 1;
103 	}
104 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0) {
105 		(void)memp_fput(mpf, meta, 0);
106 		goto out;
107 	}
108 
109 	/* Fix up the metadata page. */
110 	modified = 0;
111 	cmp_n = log_compare(lsnp, &LSN(meta));
112 	cmp_p = log_compare(&LSN(meta), &argp->meta_lsn);
113 	if (cmp_p == 0 && redo) {
114 		/* Need to redo update described. */
115 		meta->lsn = *lsnp;
116 		meta->free = argp->next;
117 		modified = 1;
118 	} else if (cmp_n == 0 && !redo) {
119 		/* Need to undo update described. */
120 		meta->lsn = argp->meta_lsn;
121 		meta->free = argp->pgno;
122 		modified = 1;
123 	}
124 	if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0)) != 0)
125 		goto out;
126 
127 done:	*lsnp = argp->prev_lsn;
128 	ret = 0;
129 
130 out:	REC_CLOSE;
131 }
132 
133 /*
134  * __bam_pg_free_recover --
135  *	Recovery function for pg_free.
136  *
137  * PUBLIC: int __bam_pg_free_recover
138  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
139  */
140 int
__bam_pg_free_recover(logp,dbtp,lsnp,redo,info)141 __bam_pg_free_recover(logp, dbtp, lsnp, redo, info)
142 	DB_LOG *logp;
143 	DBT *dbtp;
144 	DB_LSN *lsnp;
145 	int redo;
146 	void *info;
147 {
148 	__bam_pg_free_args *argp;
149 	BTMETA *meta;
150 	DB *file_dbp;
151 	DBC *dbc;
152 	DB_MPOOLFILE *mpf;
153 	PAGE *pagep;
154 	db_pgno_t pgno;
155 	int cmp_n, cmp_p, modified, ret;
156 
157 	REC_PRINT(__bam_pg_free_print);
158 	REC_INTRO(__bam_pg_free_read);
159 
160 	/*
161 	 * Fix up the freed page.  If we're redoing the operation we get the
162 	 * page and explicitly discard its contents, then update its LSN.  If
163 	 * we're undoing the operation, we get the page and restore its header.
164 	 */
165 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
166 		/*
167 		 * We don't automatically create the page.  The only way the
168 		 * page might not exist is if the alloc never happened, and
169 		 * the only way the alloc might never have happened is if we
170 		 * are undoing, in which case there's no reason to create the
171 		 * page.
172 		 */
173 		if (!redo)
174 			goto done;
175 		(void)__db_pgerr(file_dbp, argp->pgno);
176 		goto out;
177 	}
178 	modified = 0;
179 	cmp_n = log_compare(lsnp, &LSN(pagep));
180 	cmp_p = log_compare(&LSN(pagep), &LSN(argp->header.data));
181 	if (cmp_p == 0 && redo) {
182 		/* Need to redo update described. */
183 		P_INIT(pagep, file_dbp->pgsize,
184 		    pagep->pgno, PGNO_INVALID, argp->next, 0, P_INVALID);
185 		pagep->lsn = *lsnp;
186 
187 		modified = 1;
188 	} else if (cmp_n == 0 && !redo) {
189 		/* Need to undo update described. */
190 		memcpy(pagep, argp->header.data, argp->header.size);
191 
192 		modified = 1;
193 	}
194 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
195 		goto out;
196 
197 	/*
198 	 * Fix up the metadata page.  If we're redoing or undoing the operation
199 	 * we get the page and update its LSN and free pointer.
200 	 */
201 	pgno = PGNO_METADATA;
202 	if ((ret = memp_fget(mpf, &pgno, 0, &meta)) != 0) {
203 		/* The metadata page must always exist. */
204 		(void)__db_pgerr(file_dbp, pgno);
205 		goto out;
206 	}
207 
208 	modified = 0;
209 	cmp_n = log_compare(lsnp, &LSN(meta));
210 	cmp_p = log_compare(&LSN(meta), &argp->meta_lsn);
211 	if (cmp_p == 0 && redo) {
212 		/* Need to redo update described. */
213 		meta->free = argp->pgno;
214 
215 		meta->lsn = *lsnp;
216 		modified = 1;
217 	} else if (cmp_n == 0 && !redo) {
218 		/* Need to undo update described. */
219 		meta->free = argp->next;
220 
221 		meta->lsn = argp->meta_lsn;
222 		modified = 1;
223 	}
224 	if ((ret = memp_fput(mpf, meta, modified ? DB_MPOOL_DIRTY : 0)) != 0)
225 		goto out;
226 
227 done:	*lsnp = argp->prev_lsn;
228 	ret = 0;
229 
230 out:	REC_CLOSE;
231 }
232 
233 /*
234  * __bam_split_recover --
235  *	Recovery function for split.
236  *
237  * PUBLIC: int __bam_split_recover
238  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
239  */
240 int
__bam_split_recover(logp,dbtp,lsnp,redo,info)241 __bam_split_recover(logp, dbtp, lsnp, redo, info)
242 	DB_LOG *logp;
243 	DBT *dbtp;
244 	DB_LSN *lsnp;
245 	int redo;
246 	void *info;
247 {
248 	__bam_split_args *argp;
249 	DB *file_dbp;
250 	DBC *dbc;
251 	DB_MPOOLFILE *mpf;
252 	PAGE *_lp, *lp, *np, *pp, *_rp, *rp, *sp;
253 	db_pgno_t pgno;
254 	int l_update, p_update, r_update, ret, rootsplit, t_ret;
255 
256 	REC_PRINT(__bam_split_print);
257 
258 	mpf = NULL;
259 	_lp = lp = np = pp = _rp = rp = NULL;
260 
261 	REC_INTRO(__bam_split_read);
262 
263 	/*
264 	 * There are two kinds of splits that we have to recover from.  The
265 	 * first is a root-page split, where the root page is split from a
266 	 * leaf page into an internal page and two new leaf pages are created.
267 	 * The second is where a page is split into two pages, and a new key
268 	 * is inserted into the parent page.
269 	 */
270 	sp = argp->pg.data;
271 	pgno = PGNO(sp);
272 	rootsplit = pgno == PGNO_ROOT;
273 	if (memp_fget(mpf, &argp->left, 0, &lp) != 0)
274 		lp = NULL;
275 	if (memp_fget(mpf, &argp->right, 0, &rp) != 0)
276 		rp = NULL;
277 
278 	if (redo) {
279 		l_update = r_update = p_update = 0;
280 		/*
281 		 * Decide if we need to resplit the page.
282 		 *
283 		 * If this is a root split, then the root has to exist, it's
284 		 * the page we're splitting and it gets modified.  If this is
285 		 * not a root split, then the left page has to exist, for the
286 		 * same reason.
287 		 */
288 		if (rootsplit) {
289 			if ((ret = memp_fget(mpf, &pgno, 0, &pp)) != 0) {
290 				(void)__db_pgerr(file_dbp, pgno);
291 				pp = NULL;
292 				goto out;
293 			}
294 			p_update =
295 			    log_compare(&LSN(pp), &LSN(argp->pg.data)) == 0;
296 		} else
297 			if (lp == NULL) {
298 				(void)__db_pgerr(file_dbp, argp->left);
299 				goto out;
300 			}
301 		if (lp == NULL || log_compare(&LSN(lp), &argp->llsn) == 0)
302 			l_update = 1;
303 		if (rp == NULL || log_compare(&LSN(rp), &argp->rlsn) == 0)
304 			r_update = 1;
305 		if (!p_update && !l_update && !r_update)
306 			goto done;
307 
308 		/* Allocate and initialize new left/right child pages. */
309 		if ((ret = __os_malloc(file_dbp->pgsize, NULL, &_lp)) != 0 ||
310 		    (ret = __os_malloc(file_dbp->pgsize, NULL, &_rp)) != 0)
311 			goto out;
312 		if (rootsplit) {
313 			P_INIT(_lp, file_dbp->pgsize, argp->left,
314 			    PGNO_INVALID,
315 			    ISINTERNAL(sp) ? PGNO_INVALID : argp->right,
316 			    LEVEL(sp), TYPE(sp));
317 			P_INIT(_rp, file_dbp->pgsize, argp->right,
318 			    ISINTERNAL(sp) ?  PGNO_INVALID : argp->left,
319 			    PGNO_INVALID, LEVEL(sp), TYPE(sp));
320 		} else {
321 			P_INIT(_lp, file_dbp->pgsize, PGNO(sp),
322 			    ISINTERNAL(sp) ? PGNO_INVALID : PREV_PGNO(sp),
323 			    ISINTERNAL(sp) ? PGNO_INVALID : argp->right,
324 			    LEVEL(sp), TYPE(sp));
325 			P_INIT(_rp, file_dbp->pgsize, argp->right,
326 			    ISINTERNAL(sp) ? PGNO_INVALID : sp->pgno,
327 			    ISINTERNAL(sp) ? PGNO_INVALID : NEXT_PGNO(sp),
328 			    LEVEL(sp), TYPE(sp));
329 		}
330 
331 		/* Split the page. */
332 		if ((ret = __bam_copy(file_dbp, sp, _lp, 0, argp->indx)) != 0 ||
333 		    (ret = __bam_copy(file_dbp, sp, _rp, argp->indx,
334 		    NUM_ENT(sp))) != 0)
335 			goto out;
336 
337 		/* If the left child is wrong, update it. */
338 		if (lp == NULL && (ret =
339 		    memp_fget(mpf, &argp->left, DB_MPOOL_CREATE, &lp)) != 0) {
340 			(void)__db_pgerr(file_dbp, argp->left);
341 			lp = NULL;
342 			goto out;
343 		}
344 		if (l_update) {
345 			memcpy(lp, _lp, file_dbp->pgsize);
346 			lp->lsn = *lsnp;
347 			if ((ret = memp_fput(mpf, lp, DB_MPOOL_DIRTY)) != 0)
348 				goto out;
349 			lp = NULL;
350 		}
351 
352 		/* If the right child is wrong, update it. */
353 		if (rp == NULL && (ret = memp_fget(mpf,
354 		    &argp->right, DB_MPOOL_CREATE, &rp)) != 0) {
355 			(void)__db_pgerr(file_dbp, argp->right);
356 			rp = NULL;
357 			goto out;
358 		}
359 		if (r_update) {
360 			memcpy(rp, _rp, file_dbp->pgsize);
361 			rp->lsn = *lsnp;
362 			if ((ret = memp_fput(mpf, rp, DB_MPOOL_DIRTY)) != 0)
363 				goto out;
364 			rp = NULL;
365 		}
366 
367 		/*
368 		 * If the parent page is wrong, update it.  This is of interest
369 		 * only if it was a root split, since root splits create parent
370 		 * pages.  All other splits modify a parent page, but those are
371 		 * separately logged and recovered.
372 		 */
373 		if (rootsplit && p_update) {
374 			if (file_dbp->type == DB_BTREE)
375 				P_INIT(pp, file_dbp->pgsize,
376 				    PGNO_ROOT, PGNO_INVALID, PGNO_INVALID,
377 				    _lp->level + 1, P_IBTREE);
378 			else
379 				P_INIT(pp, file_dbp->pgsize,
380 				    PGNO_ROOT, PGNO_INVALID, PGNO_INVALID,
381 				    _lp->level + 1, P_IRECNO);
382 			RE_NREC_SET(pp,
383 			    file_dbp->type == DB_RECNO ||
384 			    F_ISSET(file_dbp, DB_BT_RECNUM) ?
385 			    __bam_total(_lp) + __bam_total(_rp) : 0);
386 			pp->lsn = *lsnp;
387 			if ((ret = memp_fput(mpf, pp, DB_MPOOL_DIRTY)) != 0)
388 				goto out;
389 			pp = NULL;
390 		}
391 
392 		/*
393 		 * Finally, redo the next-page link if necessary.  This is of
394 		 * interest only if it wasn't a root split -- inserting a new
395 		 * page in the tree requires that any following page have its
396 		 * previous-page pointer updated to our new page.  The next
397 		 * page must exist because we're redoing the operation.
398 		 */
399 		if (!rootsplit && !IS_ZERO_LSN(argp->nlsn)) {
400 			if ((ret = memp_fget(mpf, &argp->npgno, 0, &np)) != 0) {
401 				(void)__db_pgerr(file_dbp, argp->npgno);
402 				np = NULL;
403 				goto out;
404 			}
405 			if (log_compare(&LSN(np), &argp->nlsn) == 0) {
406 				PREV_PGNO(np) = argp->right;
407 				np->lsn = *lsnp;
408 				if ((ret =
409 				    memp_fput(mpf, np, DB_MPOOL_DIRTY)) != 0)
410 					goto out;
411 				np = NULL;
412 			}
413 		}
414 	} else {
415 		/*
416 		 * If the split page is wrong, replace its contents with the
417 		 * logged page contents.  If the page doesn't exist, it means
418 		 * that the create of the page never happened, nor did any of
419 		 * the adds onto the page that caused the split, and there's
420 		 * really no undo-ing to be done.
421 		 */
422 		if ((ret = memp_fget(mpf, &pgno, 0, &pp)) != 0) {
423 			pp = NULL;
424 			goto lrundo;
425 		}
426 		if (log_compare(lsnp, &LSN(pp)) == 0) {
427 			memcpy(pp, argp->pg.data, argp->pg.size);
428 			if ((ret = memp_fput(mpf, pp, DB_MPOOL_DIRTY)) != 0)
429 				goto out;
430 			pp = NULL;
431 		}
432 
433 		/*
434 		 * If it's a root split and the left child ever existed, update
435 		 * its LSN.  (If it's not a root split, we've updated the left
436 		 * page already -- it's the same as the split page.) If the
437 		 * right child ever existed, root split or not, update its LSN.
438 		 * The undo of the page allocation(s) will restore them to the
439 		 * free list.
440 		 */
441 lrundo:		if ((rootsplit && lp != NULL) || rp != NULL) {
442 			if (rootsplit && lp != NULL &&
443 			    log_compare(lsnp, &LSN(lp)) == 0) {
444 				lp->lsn = argp->llsn;
445 				if ((ret =
446 				    memp_fput(mpf, lp, DB_MPOOL_DIRTY)) != 0)
447 					goto out;
448 				lp = NULL;
449 			}
450 			if (rp != NULL &&
451 			    log_compare(lsnp, &LSN(rp)) == 0) {
452 				rp->lsn = argp->rlsn;
453 				if ((ret =
454 				    memp_fput(mpf, rp, DB_MPOOL_DIRTY)) != 0)
455 					goto out;
456 				rp = NULL;
457 			}
458 		}
459 
460 		/*
461 		 * Finally, undo the next-page link if necessary.  This is of
462 		 * interest only if it wasn't a root split -- inserting a new
463 		 * page in the tree requires that any following page have its
464 		 * previous-page pointer updated to our new page.  Since it's
465 		 * possible that the next-page never existed, we ignore it as
466 		 * if there's nothing to undo.
467 		 */
468 		if (!rootsplit && !IS_ZERO_LSN(argp->nlsn)) {
469 			if ((ret = memp_fget(mpf, &argp->npgno, 0, &np)) != 0) {
470 				np = NULL;
471 				goto done;
472 			}
473 			if (log_compare(lsnp, &LSN(np)) == 0) {
474 				PREV_PGNO(np) = argp->left;
475 				np->lsn = argp->nlsn;
476 				if (memp_fput(mpf, np, DB_MPOOL_DIRTY))
477 					goto out;
478 				np = NULL;
479 			}
480 		}
481 	}
482 
483 done:	*lsnp = argp->prev_lsn;
484 	ret = 0;
485 
486 out:	/* Free any pages that weren't dirtied. */
487 	if (pp != NULL && (t_ret = memp_fput(mpf, pp, 0)) != 0 && ret == 0)
488 		ret = t_ret;
489 	if (lp != NULL && (t_ret = memp_fput(mpf, lp, 0)) != 0 && ret == 0)
490 		ret = t_ret;
491 	if (np != NULL && (t_ret = memp_fput(mpf, np, 0)) != 0 && ret == 0)
492 		ret = t_ret;
493 	if (rp != NULL && (t_ret = memp_fput(mpf, rp, 0)) != 0 && ret == 0)
494 		ret = t_ret;
495 
496 	/* Free any allocated space. */
497 	if (_lp != NULL)
498 		__os_free(_lp, file_dbp->pgsize);
499 	if (_rp != NULL)
500 		__os_free(_rp, file_dbp->pgsize);
501 
502 	REC_CLOSE;
503 }
504 
505 /*
506  * __bam_rsplit_recover --
507  *	Recovery function for a reverse split.
508  *
509  * PUBLIC: int __bam_rsplit_recover
510  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
511  */
512 int
__bam_rsplit_recover(logp,dbtp,lsnp,redo,info)513 __bam_rsplit_recover(logp, dbtp, lsnp, redo, info)
514 	DB_LOG *logp;
515 	DBT *dbtp;
516 	DB_LSN *lsnp;
517 	int redo;
518 	void *info;
519 {
520 	__bam_rsplit_args *argp;
521 	DB *file_dbp;
522 	DBC *dbc;
523 	DB_MPOOLFILE *mpf;
524 	PAGE *pagep;
525 	db_pgno_t pgno;
526 	int cmp_n, cmp_p, modified, ret;
527 
528 	REC_PRINT(__bam_rsplit_print);
529 	REC_INTRO(__bam_rsplit_read);
530 
531 	/* Fix the root page. */
532 	pgno = PGNO_ROOT;
533 	if ((ret = memp_fget(mpf, &pgno, 0, &pagep)) != 0) {
534 		/* The root page must always exist. */
535 		__db_pgerr(file_dbp, pgno);
536 		goto out;
537 	}
538 	modified = 0;
539 	cmp_n = log_compare(lsnp, &LSN(pagep));
540 	cmp_p = log_compare(&LSN(pagep), &argp->rootlsn);
541 	if (cmp_p == 0 && redo) {
542 		/* Need to redo update described. */
543 		memcpy(pagep, argp->pgdbt.data, argp->pgdbt.size);
544 		pagep->pgno = PGNO_ROOT;
545 		pagep->lsn = *lsnp;
546 		modified = 1;
547 	} else if (cmp_n == 0 && !redo) {
548 		/* Need to undo update described. */
549 		P_INIT(pagep, file_dbp->pgsize, PGNO_ROOT,
550 		    argp->nrec, PGNO_INVALID, pagep->level + 1,
551 		    file_dbp->type == DB_BTREE ? P_IBTREE : P_IRECNO);
552 		if ((ret = __db_pitem(dbc, pagep, 0,
553 		    argp->rootent.size, &argp->rootent, NULL)) != 0)
554 			goto out;
555 		pagep->lsn = argp->rootlsn;
556 		modified = 1;
557 	}
558 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
559 		goto out;
560 
561 	/*
562 	 * Fix the page copied over the root page.  It's possible that the
563 	 * page never made it to disk, so if we're undo-ing and the page
564 	 * doesn't exist, it's okay and there's nothing further to do.
565 	 */
566 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
567 		if (!redo)
568 			goto done;
569 		(void)__db_pgerr(file_dbp, argp->pgno);
570 		goto out;
571 	}
572 	modified = 0;
573 	cmp_n = log_compare(lsnp, &LSN(pagep));
574 	cmp_p = log_compare(&LSN(pagep), &LSN(argp->pgdbt.data));
575 	if (cmp_p == 0 && redo) {
576 		/* Need to redo update described. */
577 		pagep->lsn = *lsnp;
578 		modified = 1;
579 	} else if (cmp_n == 0 && !redo) {
580 		/* Need to undo update described. */
581 		memcpy(pagep, argp->pgdbt.data, argp->pgdbt.size);
582 		modified = 1;
583 	}
584 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
585 		goto out;
586 
587 done:	*lsnp = argp->prev_lsn;
588 	ret = 0;
589 
590 out:	REC_CLOSE;
591 }
592 
593 /*
594  * __bam_adj_recover --
595  *	Recovery function for adj.
596  *
597  * PUBLIC: int __bam_adj_recover
598  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
599  */
600 int
__bam_adj_recover(logp,dbtp,lsnp,redo,info)601 __bam_adj_recover(logp, dbtp, lsnp, redo, info)
602 	DB_LOG *logp;
603 	DBT *dbtp;
604 	DB_LSN *lsnp;
605 	int redo;
606 	void *info;
607 {
608 	__bam_adj_args *argp;
609 	DB *file_dbp;
610 	DBC *dbc;
611 	DB_MPOOLFILE *mpf;
612 	PAGE *pagep;
613 	int cmp_n, cmp_p, modified, ret;
614 
615 	REC_PRINT(__bam_adj_print);
616 	REC_INTRO(__bam_adj_read);
617 
618 	/* Get the page; if it never existed and we're undoing, we're done. */
619 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
620 		if (!redo)
621 			goto done;
622 		(void)__db_pgerr(file_dbp, argp->pgno);
623 		goto out;
624 	}
625 
626 	modified = 0;
627 	cmp_n = log_compare(lsnp, &LSN(pagep));
628 	cmp_p = log_compare(&LSN(pagep), &argp->lsn);
629 	if (cmp_p == 0 && redo) {
630 		/* Need to redo update described. */
631 		if ((ret = __bam_adjindx(dbc,
632 		    pagep, argp->indx, argp->indx_copy, argp->is_insert)) != 0)
633 			goto err;
634 
635 		LSN(pagep) = *lsnp;
636 		modified = 1;
637 	} else if (cmp_n == 0 && !redo) {
638 		/* Need to undo update described. */
639 		if ((ret = __bam_adjindx(dbc,
640 		    pagep, argp->indx, argp->indx_copy, !argp->is_insert)) != 0)
641 			goto err;
642 
643 		LSN(pagep) = argp->lsn;
644 		modified = 1;
645 	}
646 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
647 		goto out;
648 
649 done:	*lsnp = argp->prev_lsn;
650 	ret = 0;
651 
652 	if (0) {
653 err:		(void)memp_fput(mpf, pagep, 0);
654 	}
655 out:	REC_CLOSE;
656 }
657 
658 /*
659  * __bam_cadjust_recover --
660  *	Recovery function for the adjust of a count change in an internal
661  *	page.
662  *
663  * PUBLIC: int __bam_cadjust_recover
664  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
665  */
666 int
__bam_cadjust_recover(logp,dbtp,lsnp,redo,info)667 __bam_cadjust_recover(logp, dbtp, lsnp, redo, info)
668 	DB_LOG *logp;
669 	DBT *dbtp;
670 	DB_LSN *lsnp;
671 	int redo;
672 	void *info;
673 {
674 	__bam_cadjust_args *argp;
675 	DB *file_dbp;
676 	DBC *dbc;
677 	DB_MPOOLFILE *mpf;
678 	PAGE *pagep;
679 	int cmp_n, cmp_p, modified, ret;
680 
681 	REC_PRINT(__bam_cadjust_print);
682 	REC_INTRO(__bam_cadjust_read);
683 
684 	/* Get the page; if it never existed and we're undoing, we're done. */
685 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
686 		if (!redo)
687 			goto done;
688 		(void)__db_pgerr(file_dbp, argp->pgno);
689 		goto out;
690 	}
691 
692 	modified = 0;
693 	cmp_n = log_compare(lsnp, &LSN(pagep));
694 	cmp_p = log_compare(&LSN(pagep), &argp->lsn);
695 	if (cmp_p == 0 && redo) {
696 		/* Need to redo update described. */
697 		if (file_dbp->type == DB_BTREE &&
698 		    F_ISSET(file_dbp, DB_BT_RECNUM)) {
699 			GET_BINTERNAL(pagep, argp->indx)->nrecs += argp->adjust;
700 			if (argp->total && PGNO(pagep) == PGNO_ROOT)
701 				RE_NREC_ADJ(pagep, argp->adjust);
702 		}
703 		if (file_dbp->type == DB_RECNO) {
704 			GET_RINTERNAL(pagep, argp->indx)->nrecs += argp->adjust;
705 			if (argp->total && PGNO(pagep) == PGNO_ROOT)
706 				RE_NREC_ADJ(pagep, argp->adjust);
707 		}
708 
709 		LSN(pagep) = *lsnp;
710 		modified = 1;
711 	} else if (cmp_n == 0 && !redo) {
712 		/* Need to undo update described. */
713 		if (file_dbp->type == DB_BTREE &&
714 		    F_ISSET(file_dbp, DB_BT_RECNUM)) {
715 			GET_BINTERNAL(pagep, argp->indx)->nrecs -= argp->adjust;
716 			if (argp->total && PGNO(pagep) == PGNO_ROOT)
717 				RE_NREC_ADJ(pagep, argp->adjust);
718 		}
719 		if (file_dbp->type == DB_RECNO) {
720 			GET_RINTERNAL(pagep, argp->indx)->nrecs -= argp->adjust;
721 			if (argp->total && PGNO(pagep) == PGNO_ROOT)
722 				RE_NREC_ADJ(pagep, -(argp->adjust));
723 		}
724 		LSN(pagep) = argp->lsn;
725 		modified = 1;
726 	}
727 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
728 		goto out;
729 
730 done:	*lsnp = argp->prev_lsn;
731 	ret = 0;
732 
733 out:	REC_CLOSE;
734 }
735 
736 /*
737  * __bam_cdel_recover --
738  *	Recovery function for the intent-to-delete of a cursor record.
739  *
740  * PUBLIC: int __bam_cdel_recover
741  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
742  */
743 int
__bam_cdel_recover(logp,dbtp,lsnp,redo,info)744 __bam_cdel_recover(logp, dbtp, lsnp, redo, info)
745 	DB_LOG *logp;
746 	DBT *dbtp;
747 	DB_LSN *lsnp;
748 	int redo;
749 	void *info;
750 {
751 	__bam_cdel_args *argp;
752 	DB *file_dbp;
753 	DBC *dbc;
754 	DB_MPOOLFILE *mpf;
755 	PAGE *pagep;
756 	int cmp_n, cmp_p, modified, ret;
757 
758 	REC_PRINT(__bam_cdel_print);
759 	REC_INTRO(__bam_cdel_read);
760 
761 	/* Get the page; if it never existed and we're undoing, we're done. */
762 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
763 		if (!redo)
764 			goto done;
765 		(void)__db_pgerr(file_dbp, argp->pgno);
766 		goto out;
767 	}
768 
769 	modified = 0;
770 	cmp_n = log_compare(lsnp, &LSN(pagep));
771 	cmp_p = log_compare(&LSN(pagep), &argp->lsn);
772 	if (cmp_p == 0 && redo) {
773 		/* Need to redo update described. */
774 		if (pagep->type == P_DUPLICATE)
775 			B_DSET(GET_BKEYDATA(pagep, argp->indx)->type);
776 		else
777 			B_DSET(GET_BKEYDATA(pagep, argp->indx + O_INDX)->type);
778 
779 		LSN(pagep) = *lsnp;
780 		modified = 1;
781 	} else if (cmp_n == 0 && !redo) {
782 		/* Need to undo update described. */
783 		if (pagep->type == P_DUPLICATE)
784 			B_DCLR(GET_BKEYDATA(pagep, argp->indx)->type);
785 		else
786 			B_DCLR(GET_BKEYDATA(pagep, argp->indx + O_INDX)->type);
787 
788 		LSN(pagep) = argp->lsn;
789 		modified = 1;
790 	}
791 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
792 		goto out;
793 
794 done:	*lsnp = argp->prev_lsn;
795 	ret = 0;
796 
797 out:	REC_CLOSE;
798 }
799 
800 /*
801  * __bam_repl_recover --
802  *	Recovery function for page item replacement.
803  *
804  * PUBLIC: int __bam_repl_recover
805  * PUBLIC:   __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
806  */
807 int
__bam_repl_recover(logp,dbtp,lsnp,redo,info)808 __bam_repl_recover(logp, dbtp, lsnp, redo, info)
809 	DB_LOG *logp;
810 	DBT *dbtp;
811 	DB_LSN *lsnp;
812 	int redo;
813 	void *info;
814 {
815 	__bam_repl_args *argp;
816 	BKEYDATA *bk;
817 	DB *file_dbp;
818 	DBC *dbc;
819 	DBT dbt;
820 	DB_MPOOLFILE *mpf;
821 	PAGE *pagep;
822 	int cmp_n, cmp_p, modified, ret;
823 	u_int8_t *p;
824 
825 	REC_PRINT(__bam_repl_print);
826 	REC_INTRO(__bam_repl_read);
827 
828 	/* Get the page; if it never existed and we're undoing, we're done. */
829 	if ((ret = memp_fget(mpf, &argp->pgno, 0, &pagep)) != 0) {
830 		if (!redo)
831 			goto done;
832 		(void)__db_pgerr(file_dbp, argp->pgno);
833 		goto out;
834 	}
835 	bk = GET_BKEYDATA(pagep, argp->indx);
836 
837 	modified = 0;
838 	cmp_n = log_compare(lsnp, &LSN(pagep));
839 	cmp_p = log_compare(&LSN(pagep), &argp->lsn);
840 	if (cmp_p == 0 && redo) {
841 		/*
842 		 * Need to redo update described.
843 		 *
844 		 * Re-build the replacement item.
845 		 */
846 		memset(&dbt, 0, sizeof(dbt));
847 		dbt.size = argp->prefix + argp->suffix + argp->repl.size;
848 		if ((ret = __os_malloc(dbt.size, NULL, &dbt.data)) != 0)
849 			goto err;
850 		p = dbt.data;
851 		memcpy(p, bk->data, argp->prefix);
852 		p += argp->prefix;
853 		memcpy(p, argp->repl.data, argp->repl.size);
854 		p += argp->repl.size;
855 		memcpy(p, bk->data + (bk->len - argp->suffix), argp->suffix);
856 
857 		ret = __bam_ritem(dbc, pagep, argp->indx, &dbt);
858 		__os_free(dbt.data, dbt.size);
859 		if (ret != 0)
860 			goto err;
861 
862 		LSN(pagep) = *lsnp;
863 		modified = 1;
864 	} else if (cmp_n == 0 && !redo) {
865 		/*
866 		 * Need to undo update described.
867 		 *
868 		 * Re-build the original item.
869 		 */
870 		memset(&dbt, 0, sizeof(dbt));
871 		dbt.size = argp->prefix + argp->suffix + argp->orig.size;
872 		if ((ret = __os_malloc(dbt.size, NULL, &dbt.data)) != 0)
873 			goto err;
874 		p = dbt.data;
875 		memcpy(p, bk->data, argp->prefix);
876 		p += argp->prefix;
877 		memcpy(p, argp->orig.data, argp->orig.size);
878 		p += argp->orig.size;
879 		memcpy(p, bk->data + (bk->len - argp->suffix), argp->suffix);
880 
881 		ret = __bam_ritem(dbc, pagep, argp->indx, &dbt);
882 		__os_free(dbt.data, dbt.size);
883 		if (ret != 0)
884 			goto err;
885 
886 		/* Reset the deleted flag, if necessary. */
887 		if (argp->isdeleted)
888 			B_DSET(GET_BKEYDATA(pagep, argp->indx)->type);
889 
890 		LSN(pagep) = argp->lsn;
891 		modified = 1;
892 	}
893 	if ((ret = memp_fput(mpf, pagep, modified ? DB_MPOOL_DIRTY : 0)) != 0)
894 		goto out;
895 
896 done:	*lsnp = argp->prev_lsn;
897 	ret = 0;
898 
899 	if (0) {
900 err:		(void)memp_fput(mpf, pagep, 0);
901 	}
902 out:	REC_CLOSE;
903 }
904