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