1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1999-2009 Oracle.  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/db_verify.h"
14#include "dbinc/db_am.h"
15#include "dbinc/mp.h"
16#include "dbinc/qam.h"
17/*
18 * __qam_vrfy_meta --
19 *	Verify the queue-specific part of a metadata page.
20 *
21 * PUBLIC: int __qam_vrfy_meta __P((DB *, VRFY_DBINFO *, QMETA *,
22 * PUBLIC:     db_pgno_t, u_int32_t));
23 */
24int
25__qam_vrfy_meta(dbp, vdp, meta, pgno, flags)
26	DB *dbp;
27	VRFY_DBINFO *vdp;
28	QMETA *meta;
29	db_pgno_t pgno;
30	u_int32_t flags;
31{
32	ENV *env;
33	QUEUE *qp;
34	VRFY_PAGEINFO *pip;
35	db_pgno_t *extents, extid, first, last;
36	size_t len;
37	int count, i, isbad, nextents, ret, t_ret;
38	char *buf, **names;
39
40	COMPQUIET(count, 0);
41
42	env = dbp->env;
43	qp = (QUEUE *)dbp->q_internal;
44	extents = NULL;
45	first = last = 0;
46	isbad = 0;
47	buf = NULL;
48	names = NULL;
49
50	if ((ret = __db_vrfy_getpageinfo(vdp, pgno, &pip)) != 0)
51		return (ret);
52
53	/*
54	 * Queue can't be used in subdatabases, so if this isn't set
55	 * something very odd is going on.
56	 */
57	if (!F_ISSET(pip, VRFY_INCOMPLETE))
58		EPRINT((env, "Page %lu: queue databases must be one-per-file",
59		    (u_long)pgno));
60
61	/*
62	 * Because the metapage pointers are rolled forward by
63	 * aborting transactions, the extent of the queue may
64	 * extend beyond the allocated pages, so we do
65	 * not check that meta_current is within the allocated
66	 * pages.
67	 */
68
69	/*
70	 * re_len:  If this is bad, we can't safely verify queue data pages, so
71	 * return DB_VERIFY_FATAL
72	 */
73	if (DB_ALIGN(meta->re_len + sizeof(QAMDATA) - 1, sizeof(u_int32_t)) *
74	    meta->rec_page + QPAGE_SZ(dbp) > dbp->pgsize) {
75		EPRINT((env,
76   "Page %lu: queue record length %lu too high for page size and recs/page",
77		    (u_long)pgno, (u_long)meta->re_len));
78		ret = DB_VERIFY_FATAL;
79		goto err;
80	} else {
81		/*
82		 * We initialize the Queue internal pointer;  we may need
83		 * it when handling extents.  It would get set up in open,
84		 * if we called open normally, but we don't.
85		 */
86		vdp->re_pad = meta->re_pad;
87		qp->re_pad = (int)meta->re_pad;
88		qp->re_len = vdp->re_len = meta->re_len;
89		qp->rec_page = vdp->rec_page = meta->rec_page;
90		qp->page_ext = vdp->page_ext = meta->page_ext;
91	}
92
93	/*
94	 * There's no formal maximum extentsize, and a 0 value represents
95	 * no extents, so there's nothing to verify.
96	 *
97	 * Note that since QUEUE databases can't have subdatabases, it's an
98	 * error to see more than one QUEUE metadata page in a single
99	 * verifier run.  Theoretically, this should really be a structure
100	 * rather than a per-page check, but since we're setting qp fields
101	 * here (and have only one qp to set) we raise the alarm now if
102	 * this assumption fails.  (We need the qp info to be reasonable
103	 * before we do per-page verification of queue extents.)
104	 */
105	if (F_ISSET(vdp, VRFY_QMETA_SET)) {
106		isbad = 1;
107		EPRINT((env,
108		    "Page %lu: database contains multiple Queue metadata pages",
109		    (u_long)pgno));
110		goto err;
111	}
112	F_SET(vdp, VRFY_QMETA_SET);
113	qp->page_ext = meta->page_ext;
114	dbp->pgsize = meta->dbmeta.pagesize;
115	qp->q_meta = pgno;
116	qp->q_root = pgno + 1;
117	vdp->first_recno = meta->first_recno;
118	vdp->last_recno = meta->cur_recno;
119	if (qp->page_ext != 0) {
120		first = QAM_RECNO_EXTENT(dbp, vdp->first_recno);
121		last = QAM_RECNO_EXTENT(dbp, vdp->last_recno);
122	}
123
124	/*
125	 * Look in the data directory to see if there are any extents
126	 * around that are not in the range of the queue.  If so,
127	 * then report that and look there if we are salvaging.
128	 */
129
130	if ((ret = __db_appname(env,
131	    DB_APP_DATA, qp->dir, NULL, &buf)) != 0)
132		goto err;
133	if ((ret = __os_dirlist(env, buf, 0, &names, &count)) != 0)
134		goto err;
135	__os_free(env, buf);
136	buf = NULL;
137
138	len = strlen(QUEUE_EXTENT_HEAD) + strlen(qp->name) + 1;
139	if ((ret = __os_malloc(env, len, &buf)) != 0)
140		goto err;
141	len = (size_t)snprintf(buf, len, QUEUE_EXTENT_HEAD, qp->name);
142	for (i = nextents = 0; i < count; i++) {
143		if (strncmp(names[i], buf, len) == 0) {
144			/* Only save extents out of bounds. */
145			extid = (db_pgno_t)strtoul(&names[i][len], NULL, 10);
146			if (qp->page_ext != 0 &&
147			    (last > first ?
148			    (extid >= first && extid <= last) :
149			    (extid >= first || extid <= last)))
150				continue;
151			if (extents == NULL && (ret = __os_malloc(
152			     env, (size_t)(count - i) * sizeof(extid),
153			     &extents)) != 0)
154				goto err;
155			extents[nextents] = extid;
156			nextents++;
157		}
158	}
159	if (nextents > 0)
160		__db_errx(env,
161		     "Warning: %d extra extent files found", nextents);
162	vdp->nextents = nextents;
163	vdp->extents = extents;
164
165err:	if ((t_ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
166		ret = t_ret;
167	if (names != NULL)
168		__os_dirfree(env, names, count);
169	if (buf != NULL)
170		__os_free(env, buf);
171	if (ret != 0 && extents != NULL)
172		__os_free(env, extents);
173	if (LF_ISSET(DB_SALVAGE) &&
174	   (t_ret = __db_salvage_markdone(vdp, pgno)) != 0 && ret == 0)
175		ret = t_ret;
176	return (ret == 0 && isbad == 1 ? DB_VERIFY_BAD : ret);
177}
178
179/*
180 * __qam_meta2pgset --
181 * For a given Queue meta page, add all of the db's pages to the pgset.  Dealing
182 * with extents complicates things, as it is possible for there to be gaps in
183 * the page number sequence (the user could have re-inserted record numbers that
184 * had been on deleted extents) so we test the existence of each extent before
185 * adding its pages to the pgset.  If there are no extents, just loop from
186 * first_recno to last_recno.
187 *
188 * PUBLIC: int __qam_meta2pgset __P((DB *, VRFY_DBINFO *, DB *));
189 */
190int
191__qam_meta2pgset(dbp, vdp, pgset)
192	DB *dbp;
193	VRFY_DBINFO *vdp;
194	DB *pgset;
195{
196	DBC *dbc;
197	PAGE *h;
198	db_pgno_t first, last, pgno, pg_ext, stop;
199	int ret, t_ret;
200	u_int32_t i;
201
202	ret = 0;
203	h = NULL;
204	if (vdp->last_recno <= vdp->first_recno)
205		return 0;
206
207	pg_ext = vdp->page_ext;
208
209	first = QAM_RECNO_PAGE(dbp, vdp->first_recno);
210
211	/*
212	 * last_recno gives the next recno to be allocated, we want the last
213	 * allocated recno.
214	 */
215	last = QAM_RECNO_PAGE(dbp, vdp->last_recno - 1);
216
217	if (first == PGNO_INVALID || last == PGNO_INVALID)
218		return (DB_VERIFY_BAD);
219
220	pgno = first;
221	if (first > last)
222		stop = QAM_RECNO_PAGE(dbp, UINT32_MAX);
223	else
224		stop = last;
225
226	/*
227	 * If this db doesn't have extents, just add all page numbers from first
228	 * to last.
229	 */
230	if (pg_ext == 0) {
231		for (pgno = first; pgno <= stop; pgno++)
232			if ((ret = __db_vrfy_pgset_inc(
233			    pgset, vdp->thread_info, pgno)) != 0)
234				break;
235		if (first > last)
236			for (pgno = 1; pgno <= last; pgno++)
237				if ((ret = __db_vrfy_pgset_inc(
238				    pgset, vdp->thread_info, pgno)) != 0)
239					break;
240
241		return ret;
242	}
243
244	if ((ret = __db_cursor(dbp, vdp->thread_info, NULL, &dbc, 0)) != 0)
245		return (ret);
246	/*
247	 * Check if we can get the first page of each extent.  If we can, then
248	 * add all of that extent's pages to the pgset.  If we can't, assume the
249	 * extent doesn't exist and don't add any pages, if we're wrong we'll
250	 * find the pages in __db_vrfy_walkpages.
251	 */
252begin:	for (; pgno <= stop; pgno += pg_ext) {
253		if ((ret = __qam_fget(dbc, &pgno, 0, &h)) != 0) {
254			if (ret == ENOENT || ret == DB_PAGE_NOTFOUND) {
255				ret = 0;
256				continue;
257			}
258			goto err;
259		}
260		if ((ret = __qam_fput(dbc, pgno, h, dbp->priority)) != 0)
261			goto err;
262
263		for (i = 0; i < pg_ext && pgno + i <= last; i++)
264			if ((ret = __db_vrfy_pgset_inc(
265			    pgset, vdp->thread_info, pgno + i)) != 0)
266				goto err;
267
268		/* The first recno won't always occur on the first page of the
269		 * extent.  Back up to the beginning of the extent before the
270		 * end of the loop so that the increment works correctly.
271		 */
272		if (pgno == first)
273			pgno = pgno % pg_ext + 1;
274	}
275
276	if (first > last) {
277		pgno = 1;
278		first = last;
279		stop = last;
280		goto begin;
281	}
282
283err:
284	if ((t_ret = __dbc_close(dbc)) != 0 && ret == 0)
285		ret = t_ret;
286
287	return ret;
288}
289
290/*
291 * __qam_vrfy_data --
292 *	Verify a queue data page.
293 *
294 * PUBLIC: int __qam_vrfy_data __P((DB *, VRFY_DBINFO *, QPAGE *,
295 * PUBLIC:     db_pgno_t, u_int32_t));
296 */
297int
298__qam_vrfy_data(dbp, vdp, h, pgno, flags)
299	DB *dbp;
300	VRFY_DBINFO *vdp;
301	QPAGE *h;
302	db_pgno_t pgno;
303	u_int32_t flags;
304{
305	DB fakedb;
306	struct __queue fakeq;
307	QAMDATA *qp;
308	db_recno_t i;
309
310	/*
311	 * Not much to do here, except make sure that flags are reasonable.
312	 *
313	 * QAM_GET_RECORD assumes a properly initialized q_internal
314	 * structure, however, and we don't have one, so we play
315	 * some gross games to fake it out.
316	 */
317	fakedb.q_internal = &fakeq;
318	fakedb.flags = dbp->flags;
319	fakeq.re_len = vdp->re_len;
320
321	for (i = 0; i < vdp->rec_page; i++) {
322		qp = QAM_GET_RECORD(&fakedb, h, i);
323		if ((u_int8_t *)qp >= (u_int8_t *)h + dbp->pgsize) {
324			EPRINT((dbp->env,
325		    "Page %lu: queue record %lu extends past end of page",
326			    (u_long)pgno, (u_long)i));
327			return (DB_VERIFY_BAD);
328		}
329
330		if (qp->flags & ~(QAM_VALID | QAM_SET)) {
331			EPRINT((dbp->env,
332			    "Page %lu: queue record %lu has bad flags (%#lx)",
333			    (u_long)pgno, (u_long)i, (u_long)qp->flags));
334			return (DB_VERIFY_BAD);
335		}
336	}
337
338	return (0);
339}
340
341/*
342 * __qam_vrfy_structure --
343 *	Verify a queue database structure, such as it is.
344 *
345 * PUBLIC: int __qam_vrfy_structure __P((DB *, VRFY_DBINFO *, u_int32_t));
346 */
347int
348__qam_vrfy_structure(dbp, vdp, flags)
349	DB *dbp;
350	VRFY_DBINFO *vdp;
351	u_int32_t flags;
352{
353	VRFY_PAGEINFO *pip;
354	db_pgno_t i;
355	int ret, isbad;
356
357	isbad = 0;
358
359	if ((ret = __db_vrfy_getpageinfo(vdp, PGNO_BASE_MD, &pip)) != 0)
360		return (ret);
361
362	if (pip->type != P_QAMMETA) {
363		EPRINT((dbp->env,
364		    "Page %lu: queue database has no meta page",
365		    (u_long)PGNO_BASE_MD));
366		isbad = 1;
367		goto err;
368	}
369
370	if ((ret = __db_vrfy_pgset_inc(vdp->pgset, vdp->thread_info, 0)) != 0)
371		goto err;
372
373	for (i = 1; i <= vdp->last_pgno; i++) {
374		/* Send feedback to the application about our progress. */
375		if (!LF_ISSET(DB_SALVAGE))
376			__db_vrfy_struct_feedback(dbp, vdp);
377
378		if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0 ||
379		    (ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
380			return (ret);
381		if (!F_ISSET(pip, VRFY_IS_ALLZEROES) &&
382		    pip->type != P_QAMDATA) {
383			EPRINT((dbp->env,
384		    "Page %lu: queue database page of incorrect type %lu",
385			    (u_long)i, (u_long)pip->type));
386			isbad = 1;
387			goto err;
388		} else if ((ret = __db_vrfy_pgset_inc(vdp->pgset,
389		    vdp->thread_info, i)) != 0)
390			goto err;
391	}
392
393err:	if ((ret = __db_vrfy_putpageinfo(dbp->env, vdp, pip)) != 0)
394		return (ret);
395	return (isbad == 1 ? DB_VERIFY_BAD : 0);
396}
397
398/*
399 * __qam_vrfy_walkqueue --
400 *    Do a "walkpages" per-page verification pass over the set of Queue
401 * extent pages.
402 *
403 * PUBLIC: int __qam_vrfy_walkqueue __P((DB *, VRFY_DBINFO *, void *,
404 * PUBLIC:    int (*)(void *, const void *), u_int32_t));
405 */
406int
407__qam_vrfy_walkqueue(dbp, vdp, handle, callback, flags)
408	DB *dbp;
409	VRFY_DBINFO *vdp;
410	void *handle;
411	int (*callback) __P((void *, const void *));
412	u_int32_t flags;
413{
414	DBC *dbc;
415	ENV *env;
416	PAGE *h;
417	QUEUE *qp;
418	VRFY_PAGEINFO *pip;
419	db_pgno_t first, i, last, pg_ext, stop;
420	int isbad, nextents, ret, t_ret;
421
422	COMPQUIET(h, NULL);
423
424	env = dbp->env;
425	qp = dbp->q_internal;
426	pip = NULL;
427	pg_ext = qp->page_ext;
428	isbad = ret = t_ret = 0;
429	h = NULL;
430
431	/* If this database has no extents, we've seen all the pages already. */
432	if (pg_ext == 0)
433		return (0);
434
435	first = QAM_RECNO_PAGE(dbp, vdp->first_recno);
436	last = QAM_RECNO_PAGE(dbp, vdp->last_recno);
437
438	i = first;
439	if (first > last)
440		stop = QAM_RECNO_PAGE(dbp, UINT32_MAX);
441	else
442		stop = last;
443	nextents = vdp->nextents;
444
445	/* Verify/salvage each page. */
446	if ((ret = __db_cursor(dbp, vdp->thread_info, NULL, &dbc, 0)) != 0)
447		return (ret);
448begin:	for (; i <= stop; i++) {
449		/*
450		 * If DB_SALVAGE is set, we inspect our database of completed
451		 * pages, and skip any we've already printed in the subdb pass.
452		 */
453		if (LF_ISSET(DB_SALVAGE) && (__db_salvage_isdone(vdp, i) != 0))
454			continue;
455		if ((t_ret = __qam_fget(dbc, &i, 0, &h)) != 0) {
456			if (t_ret == ENOENT || t_ret == DB_PAGE_NOTFOUND) {
457				i += (pg_ext - ((i - 1) % pg_ext)) - 1;
458				continue;
459			}
460
461			/*
462			 * If an individual page get fails, keep going iff
463			 * we're salvaging.
464			 */
465			if (LF_ISSET(DB_SALVAGE)) {
466				if (ret == 0)
467					ret = t_ret;
468				continue;
469			}
470			h = NULL;
471			ret = t_ret;
472			goto err;
473		}
474
475		if (LF_ISSET(DB_SALVAGE)) {
476			/*
477			 * We pretty much don't want to quit unless a
478			 * bomb hits.  May as well return that something
479			 * was screwy, however.
480			 */
481			if ((t_ret = __db_salvage_pg(dbp,
482			    vdp, i, h, handle, callback, flags)) != 0) {
483				if (ret == 0)
484					ret = t_ret;
485				isbad = 1;
486			}
487		} else {
488			/*
489			 * If we are not salvaging, and we get any error
490			 * other than DB_VERIFY_BAD, return immediately;
491			 * it may not be safe to proceed.  If we get
492			 * DB_VERIFY_BAD, keep going;  listing more errors
493			 * may make it easier to diagnose problems and
494			 * determine the magnitude of the corruption.
495			 */
496			if ((ret = __db_vrfy_common(dbp,
497			    vdp, h, i, flags)) == DB_VERIFY_BAD)
498				isbad = 1;
499			else if (ret != 0)
500				goto err;
501
502			__db_vrfy_struct_feedback(dbp, vdp);
503
504			if ((ret = __db_vrfy_getpageinfo(vdp, i, &pip)) != 0)
505				goto err;
506			if (F_ISSET(pip, VRFY_IS_ALLZEROES))
507				goto put;
508			if (pip->type != P_QAMDATA) {
509				EPRINT((env,
510		    "Page %lu: queue database page of incorrect type %lu",
511				    (u_long)i, (u_long)pip->type));
512				isbad = 1;
513				goto err;
514			}
515			if ((ret = __db_vrfy_pgset_inc(vdp->pgset,
516			    vdp->thread_info, i)) != 0)
517				goto err;
518			if ((ret = __qam_vrfy_data(dbp, vdp,
519			    (QPAGE *)h, i, flags)) == DB_VERIFY_BAD)
520				isbad = 1;
521			else if (ret != 0)
522				goto err;
523
524put:			if ((ret = __db_vrfy_putpageinfo(env, vdp, pip)) != 0)
525				goto err1;
526			pip = NULL;
527		}
528
529		/* Again, keep going iff we're salvaging. */
530		if ((t_ret = __qam_fput(dbc, i, h, dbp->priority)) != 0) {
531			if (LF_ISSET(DB_SALVAGE)) {
532				if (ret == 0)
533					ret = t_ret;
534				continue;
535			}
536			ret = t_ret;
537			goto err1;
538		}
539	}
540
541	if (first > last) {
542		i = 1;
543		stop = last;
544		first = last;
545		goto begin;
546	}
547
548	/*
549	 * Now check to see if there were any lingering
550	 * extents and dump their data.
551	 */
552	if (LF_ISSET(DB_SALVAGE) && nextents != 0) {
553		nextents--;
554		i = 1 +
555		    vdp->extents[nextents] * vdp->page_ext;
556		stop = i + vdp->page_ext;
557		goto begin;
558	}
559
560	if (0) {
561err:		if (h != NULL && (t_ret =
562		    __qam_fput(dbc, i, h, dbp->priority)) != 0 && ret == 0)
563			ret = t_ret;
564		if (pip != NULL && (t_ret =
565		    __db_vrfy_putpageinfo(env, vdp, pip)) != 0 && ret == 0)
566			ret = t_ret;
567	}
568err1:	if (dbc != NULL && (t_ret = __dbc_close(dbc)) != 0 && ret == 0)
569		ret = t_ret;
570	return ((isbad == 1 && ret == 0) ? DB_VERIFY_BAD : ret);
571}
572
573/*
574 * __qam_salvage --
575 *	Safely dump out all recnos and data on a queue page.
576 *
577 * PUBLIC: int __qam_salvage __P((DB *, VRFY_DBINFO *, db_pgno_t, PAGE *,
578 * PUBLIC:     void *, int (*)(void *, const void *), u_int32_t));
579 */
580int
581__qam_salvage(dbp, vdp, pgno, h, handle, callback, flags)
582	DB *dbp;
583	VRFY_DBINFO *vdp;
584	db_pgno_t pgno;
585	PAGE *h;
586	void *handle;
587	int (*callback) __P((void *, const void *));
588	u_int32_t flags;
589{
590	DBT dbt, key;
591	QAMDATA *qp, *qep;
592	db_recno_t recno;
593	int ret, err_ret, t_ret;
594	u_int32_t pagesize, qlen;
595	u_int32_t i;
596
597	memset(&dbt, 0, sizeof(DBT));
598	memset(&key, 0, sizeof(DBT));
599
600	err_ret = ret = 0;
601
602	pagesize = (u_int32_t)dbp->mpf->mfp->stat.st_pagesize;
603	qlen = ((QUEUE *)dbp->q_internal)->re_len;
604	dbt.size = qlen;
605	key.data = &recno;
606	key.size = sizeof(recno);
607	recno = (pgno - 1) * QAM_RECNO_PER_PAGE(dbp) + 1;
608	i = 0;
609	qep = (QAMDATA *)((u_int8_t *)h + pagesize - qlen);
610	for (qp = QAM_GET_RECORD(dbp, h, i); qp < qep;
611	    recno++, i++, qp = QAM_GET_RECORD(dbp, h, i)) {
612		if (F_ISSET(qp, ~(QAM_VALID|QAM_SET)))
613			continue;
614		if (!F_ISSET(qp, QAM_SET))
615			continue;
616
617		if (!LF_ISSET(DB_AGGRESSIVE) && !F_ISSET(qp, QAM_VALID))
618			continue;
619
620		dbt.data = qp->data;
621		if ((ret = __db_vrfy_prdbt(&key,
622		    0, " ", handle, callback, 1, vdp)) != 0)
623			err_ret = ret;
624
625		if ((ret = __db_vrfy_prdbt(&dbt,
626		    0, " ", handle, callback, 0, vdp)) != 0)
627			err_ret = ret;
628	}
629
630	if ((t_ret = __db_salvage_markdone(vdp, pgno)) != 0)
631		return (t_ret);
632	return ((ret == 0 && err_ret != 0) ? err_ret : ret);
633}
634