1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_verify.c,v 12.13 2008/01/08 20:58:17 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13#ifndef lint
14static const char copyright[] =
15    "Copyright (c) 1996,2008 Oracle.  All rights reserved.\n";
16#endif
17
18int db_verify_main __P((int, char *[]));
19int db_verify_usage __P((void));
20int db_verify_version_check __P((void));
21
22const char *progname;
23
24int
25db_verify(args)
26	char *args;
27{
28	int argc;
29	char **argv;
30
31	__db_util_arg("db_verify", args, &argc, &argv);
32	return (db_verify_main(argc, argv) ? EXIT_FAILURE : EXIT_SUCCESS);
33}
34
35#include <stdio.h>
36#define	ERROR_RETURN	ERROR
37
38int
39db_verify_main(argc, argv)
40	int argc;
41	char *argv[];
42{
43	extern char *optarg;
44	extern int optind, __db_getopt_reset;
45	DB *dbp, *dbp1;
46	DB_ENV *dbenv;
47	u_int32_t flags, cache;
48	int ch, exitval, nflag, private;
49	int quiet, resize, ret;
50	char *home, *passwd;
51
52	if ((progname = __db_rpath(argv[0])) == NULL)
53		progname = argv[0];
54	else
55		++progname;
56
57	if ((ret = db_verify_version_check()) != 0)
58		return (ret);
59
60	dbenv = NULL;
61	dbp = NULL;
62	cache = MEGABYTE;
63	exitval = nflag = quiet = 0;
64	flags = 0;
65	home = passwd = NULL;
66	__db_getopt_reset = 1;
67	while ((ch = getopt(argc, argv, "h:NoP:quV")) != EOF)
68		switch (ch) {
69		case 'h':
70			home = optarg;
71			break;
72		case 'N':
73			nflag = 1;
74			break;
75		case 'P':
76			passwd = strdup(optarg);
77			memset(optarg, 0, strlen(optarg));
78			if (passwd == NULL) {
79				fprintf(stderr, "%s: strdup: %s\n",
80				    progname, strerror(errno));
81				return (EXIT_FAILURE);
82			}
83			break;
84		case 'o':
85			LF_SET(DB_NOORDERCHK);
86			break;
87		case 'q':
88			quiet = 1;
89			break;
90		case 'u':			/* Undocumented. */
91			LF_SET(DB_UNREF);
92			break;
93		case 'V':
94			printf("%s\n", db_version(NULL, NULL, NULL));
95			return (EXIT_SUCCESS);
96		case '?':
97		default:
98			return (db_verify_usage());
99		}
100	argc -= optind;
101	argv += optind;
102
103	if (argc <= 0)
104		return (db_verify_usage());
105
106	/* Handle possible interruptions. */
107	__db_util_siginit();
108
109	/*
110	 * Create an environment object and initialize it for error
111	 * reporting.
112	 */
113retry:	if ((ret = db_env_create(&dbenv, 0)) != 0) {
114		fprintf(stderr,
115		    "%s: db_env_create: %s\n", progname, db_strerror(ret));
116		goto shutdown;
117	}
118
119	if (!quiet) {
120		dbenv->set_errfile(dbenv, stderr);
121		dbenv->set_errpfx(dbenv, progname);
122	}
123
124	if (nflag) {
125		if ((ret = dbenv->set_flags(dbenv, DB_NOLOCKING, 1)) != 0) {
126			dbenv->err(dbenv, ret, "set_flags: DB_NOLOCKING");
127			goto shutdown;
128		}
129		if ((ret = dbenv->set_flags(dbenv, DB_NOPANIC, 1)) != 0) {
130			dbenv->err(dbenv, ret, "set_flags: DB_NOPANIC");
131			goto shutdown;
132		}
133	}
134
135	if (passwd != NULL &&
136	    (ret = dbenv->set_encrypt(dbenv, passwd, DB_ENCRYPT_AES)) != 0) {
137		dbenv->err(dbenv, ret, "set_passwd");
138		goto shutdown;
139	}
140	/*
141	 * Attach to an mpool if it exists, but if that fails, attach to a
142	 * private region.  In the latter case, declare a reasonably large
143	 * cache so that we don't fail when verifying large databases.
144	 */
145	private = 0;
146	if ((ret =
147	    dbenv->open(dbenv, home, DB_INIT_MPOOL | DB_USE_ENVIRON, 0)) != 0) {
148		if (ret != DB_VERSION_MISMATCH) {
149			if ((ret =
150			    dbenv->set_cachesize(dbenv, 0, cache, 1)) != 0) {
151				dbenv->err(dbenv, ret, "set_cachesize");
152				goto shutdown;
153			}
154			private = 1;
155			ret = dbenv->open(dbenv, home, DB_CREATE |
156			    DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0);
157		}
158		if (ret != 0) {
159			dbenv->err(dbenv, ret, "DB_ENV->open");
160			goto shutdown;
161		}
162	}
163
164	/*
165	 * Find out if we have a transactional environment so that we can
166	 * make sure that we don't open the verify database with logging
167	 * enabled.
168	 */
169	for (; !__db_util_interrupted() && argv[0] != NULL; ++argv) {
170		if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
171			dbenv->err(dbenv, ret, "%s: db_create", progname);
172			goto shutdown;
173		}
174
175		if (TXN_ON(dbenv->env) &&
176		    (ret = dbp->set_flags(dbp, DB_TXN_NOT_DURABLE)) != 0) {
177			dbenv->err(
178			    dbenv, ret, "%s: db_set_flags", progname);
179			goto shutdown;
180		}
181
182		/*
183		 * We create a 2nd dbp to this database to get its pagesize
184		 * because the dbp we're using for verify cannot be opened.
185		 *
186		 * If the database is corrupted, we may not be able to open
187		 * it, of course.  In that case, just continue, using the
188		 * cache size we have.
189		 */
190		if (private) {
191			if ((ret = db_create(&dbp1, dbenv, 0)) != 0) {
192				dbenv->err(
193				    dbenv, ret, "%s: db_create", progname);
194				goto shutdown;
195			}
196
197			if (TXN_ON(dbenv->env) && (ret =
198			    dbp1->set_flags(dbp1, DB_TXN_NOT_DURABLE)) != 0) {
199				dbenv->err(
200				    dbenv, ret, "%s: db_set_flags", progname);
201				goto shutdown;
202			}
203
204			ret = dbp1->open(dbp1,
205			    NULL, argv[0], NULL, DB_UNKNOWN, DB_RDONLY, 0);
206
207			/*
208			 * If we get here, we can check the cache/page.
209			 * !!!
210			 * If we have to retry with an env with a larger
211			 * cache, we jump out of this loop.  However, we
212			 * will still be working on the same argv when we
213			 * get back into the for-loop.
214			 */
215			if (ret == 0) {
216				if (__db_util_cache(
217				    dbp1, &cache, &resize) == 0 && resize) {
218					(void)dbp1->close(dbp1, 0);
219					(void)dbp->close(dbp, 0);
220					dbp = NULL;
221
222					(void)dbenv->close(dbenv, 0);
223					dbenv = NULL;
224					goto retry;
225				}
226			}
227			(void)dbp1->close(dbp1, 0);
228		}
229
230		/* The verify method is a destructor. */
231		ret = dbp->verify(dbp, argv[0], NULL, NULL, flags);
232		dbp = NULL;
233		if (ret != 0)
234			goto shutdown;
235	}
236
237	if (0) {
238shutdown:	exitval = 1;
239	}
240
241	if (dbp != NULL && (ret = dbp->close(dbp, 0)) != 0) {
242		exitval = 1;
243		dbenv->err(dbenv, ret, "close");
244	}
245	if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
246		exitval = 1;
247		fprintf(stderr,
248		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
249	}
250
251	if (passwd != NULL)
252		free(passwd);
253
254	/* Resend any caught signal. */
255	__db_util_sigresend();
256
257	return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
258}
259
260int
261db_verify_usage()
262{
263	fprintf(stderr, "usage: %s %s\n", progname,
264	    "[-NoqV] [-h home] [-P password] db_file ...");
265	return (EXIT_FAILURE);
266}
267
268int
269db_verify_version_check()
270{
271	int v_major, v_minor, v_patch;
272
273	/* Make sure we're loaded with the right version of the DB library. */
274	(void)db_version(&v_major, &v_minor, &v_patch);
275	if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
276		fprintf(stderr,
277	"%s: version %d.%d doesn't match library version %d.%d\n",
278		    progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
279		    v_major, v_minor);
280		return (EXIT_FAILURE);
281	}
282	return (0);
283}
284