1 /*-
2 * Copyright (c) 1996, 2020 Oracle and/or its affiliates. All rights reserved.
3 *
4 * See the file LICENSE for license information.
5 *
6 * $Id$
7 */
8
9 /*
10 * This file contains helper functions like data structure and in-memory db
11 * management, which are used to store various log verification information.
12 */
13 #include "db_config.h"
14 #include "db_int.h"
15
16 #include "dbinc/crypto.h"
17 #include "dbinc/db_page.h"
18 #include "dbinc/db_am.h"
19 #include "dbinc/btree.h"
20 #include "dbinc/hash.h"
21 #include "dbinc/qam.h"
22 #include "dbinc/mp.h"
23 #include "dbinc/txn.h"
24 #include "dbinc/fop.h"
25
26 #include "dbinc/log_verify.h"
27
28 #define BDBOP(op) do { \
29 ret = (op); \
30 if (ret != 0) { \
31 __lv_on_bdbop_err(ret); \
32 goto err; \
33 } \
34 } while (0)
35
36 #define BDBOP2(dbenv, op, funct) do { \
37 ret = (op); \
38 if (ret != 0) { \
39 __lv_on_bdbop_err(ret); \
40 __db_err(dbenv->env, ret, "\n%s", funct); \
41 return (ret); \
42 } \
43 } while (0)
44
45 #define BDBOP3(dbenv, op, excpt, funct) do { \
46 ret = (op); \
47 if (ret != 0) { \
48 __lv_on_bdbop_err(ret); \
49 if (ret != excpt) { \
50 __db_err(dbenv->env, ret, "\n%s", funct); \
51 return (ret); \
52 } \
53 } \
54 } while (0)
55
56 typedef int (*btcmp_funct)(DB *, const DBT *, const DBT *, size_t *);
57 typedef int (*dupcmp_funct)(DB *, const DBT *, const DBT *, size_t *);
58
59 static int __lv_add_recycle_handler __P((
60 DB_LOG_VRFY_INFO *, VRFY_TXN_INFO *, void *));
61 static int __lv_add_recycle_lsn __P((VRFY_TXN_INFO *, const DB_LSN *));
62 static size_t __lv_dbt_arrsz __P((const DBT *, u_int32_t));
63 static int __lv_fidpgno_cmp __P((DB *, const DBT *, const DBT *, size_t *));
64 static int __lv_i32_cmp __P((DB *, const DBT *, const DBT *, size_t *));
65 static int __lv_lsn_cmp __P((DB *, const DBT *, const DBT *, size_t *));
66 static void __lv_on_bdbop_err __P((int));
67 static int __lv_open_db __P((DB_ENV *, DB **, DB_THREAD_INFO *,
68 const char *, int, btcmp_funct, u_int32_t, dupcmp_funct));
69 static int __lv_pack_filereg __P((const VRFY_FILEREG_INFO *, DBT *));
70 static int __lv_pack_txn_vrfy_info __P((
71 const VRFY_TXN_INFO *, DBT *, DBT *data));
72 static int __lv_seccbk_fname __P((DB *, const DBT *, const DBT *, DBT *));
73 static int __lv_seccbk_lsn __P((DB *, const DBT *, const DBT *, DBT *));
74 static int __lv_seccbk_txnpg __P((DB *, const DBT *, const DBT *, DBT *));
75 static void __lv_setup_logtype_names __P((DB_LOG_VRFY_INFO *lvinfo));
76 static int __lv_txnrgns_lsn_cmp __P((DB *, const DBT *, const DBT *, size_t *));
77 static int __lv_ui32_cmp __P((DB *, const DBT *, const DBT *, size_t *));
78 static int __lv_unpack_txn_vrfy_info __P((VRFY_TXN_INFO **, const DBT *));
79 static int __lv_unpack_filereg __P((const DBT *, VRFY_FILEREG_INFO **));
80
__lv_on_bdbop_err(ret)81 static void __lv_on_bdbop_err(ret)
82 int ret;
83 {
84 /* Pass lint checks. We need the ret and this function for debugging. */
85 COMPQUIET(ret, 0);
86 }
87
88 /*
89 * __create_log_vrfy_info --
90 * Initialize and return a log verification handle to be used throughout
91 * a verification process.
92 *
93 * PUBLIC: int __create_log_vrfy_info __P((const DB_LOG_VERIFY_CONFIG *,
94 * PUBLIC: DB_LOG_VRFY_INFO **, DB_THREAD_INFO *));
95 */
96 int
__create_log_vrfy_info(cfg,lvinfopp,ip)97 __create_log_vrfy_info(cfg, lvinfopp, ip)
98 const DB_LOG_VERIFY_CONFIG *cfg;
99 DB_LOG_VRFY_INFO **lvinfopp;
100 DB_THREAD_INFO *ip;
101 {
102 const char *envhome;
103 int inmem, ret;
104 u_int32_t cachesz, envflags;
105 const char *dbf1, *dbf2, *dbf3, *dbf4, *dbf5, *dbf6, *dbf7, *dbf8,
106 *dbf9, *dbf10, *dbf11;
107 DB_LOG_VRFY_INFO *lvinfop;
108
109 dbf1 = "__db_log_vrfy_txninfo.db";
110 dbf2 = "__db_log_vrfy_fileregs.db";
111 dbf3 = "__db_log_vrfy_pgtxn.db";
112 dbf4 = "__db_log_vrfy_lsntime.db";
113 dbf5 = "__db_log_vrfy_timelsn.db";
114 dbf6 = "__db_log_vrfy_ckps.db";
115 dbf7 = "__db_log_vrfy_dbregids.db";
116 dbf8 = "__db_log_vrfy_fnameuid.db";
117 dbf9 = "__db_log_vrfy_timerange.db";
118 dbf10 = "__db_log_vrfy_txnaborts.db";
119 dbf11 = "__db_log_vrfy_txnpg.db";
120
121 envhome = cfg->temp_envhome;
122 lvinfop = NULL;
123 cachesz = cfg->cachesize;
124 if (cachesz== 0)
125 cachesz = 1024 * 1024 * 256;
126
127 BDBOP(__os_malloc(NULL, sizeof(DB_LOG_VRFY_INFO), &lvinfop));
128 memset(lvinfop, 0, sizeof(DB_LOG_VRFY_INFO));
129 lvinfop->ip = ip;
130 __lv_setup_logtype_names(lvinfop);
131 /* Avoid the VERIFY_PARTIAL bit being cleared if no ckp_lsn exists. */
132 lvinfop->valid_lsn.file = lvinfop->valid_lsn.offset = (u_int32_t)-1;
133
134 /*
135 * The envhome parameter determines if we will use an in-memory
136 * environment and databases.
137 */
138 if (envhome == NULL) {
139 envflags = DB_PRIVATE;
140 inmem = 1;
141 } else {
142 envflags = 0;
143 inmem = 0;
144 }
145
146 /* Create log verify internal database environment. */
147 BDBOP(db_env_create(&lvinfop->dbenv, 0));
148 BDBOP(__memp_set_cachesize(lvinfop->dbenv, 0, cachesz, 1));
149 /*
150 * Log verification internal db environment should be accessed
151 * single-threaded. No transaction semantics needed.
152 */
153 BDBOP(__env_open(lvinfop->dbenv, envhome,
154 envflags | DB_CREATE | DB_INIT_MPOOL, 0666));
155
156 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txninfo, ip, dbf1,
157 inmem, __lv_ui32_cmp, 0, NULL));
158 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->fileregs, ip, dbf2,
159 inmem, NULL, 0, NULL));
160
161 /* No dup allowed, always overwrite data with same key. */
162 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->dbregids, ip, dbf7,
163 inmem, __lv_i32_cmp, 0, NULL));
164 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->pgtxn, ip, dbf3,
165 inmem, __lv_fidpgno_cmp, 0, NULL));
166 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnpg, ip, dbf11,
167 inmem, __lv_ui32_cmp, DB_DUP | DB_DUPSORT, __lv_fidpgno_cmp));
168 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->lsntime, ip, dbf4,
169 inmem, __lv_lsn_cmp, 0, NULL));
170 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->timelsn, ip, dbf5,
171 inmem, __lv_i32_cmp, DB_DUP | DB_DUPSORT, __lv_lsn_cmp));
172 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnaborts, ip, dbf10,
173 inmem, __lv_lsn_cmp, 0, NULL));
174 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->ckps, ip, dbf6,
175 inmem, __lv_lsn_cmp, 0, NULL));
176 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->fnameuid, ip, dbf8,
177 inmem, NULL, 0, NULL));
178 BDBOP(__lv_open_db(lvinfop->dbenv, &lvinfop->txnrngs, ip, dbf9,
179 inmem, __lv_ui32_cmp, DB_DUP | DB_DUPSORT, __lv_txnrgns_lsn_cmp));
180
181 BDBOP(__db_associate(lvinfop->lsntime, ip, NULL,
182 lvinfop->timelsn, __lv_seccbk_lsn, DB_CREATE));
183 BDBOP(__db_associate(lvinfop->fileregs, ip, NULL,
184 lvinfop->fnameuid, __lv_seccbk_fname, DB_CREATE));
185 BDBOP(__db_associate(lvinfop->pgtxn, ip, NULL,
186 lvinfop->txnpg, __lv_seccbk_txnpg, DB_CREATE));
187
188 *lvinfopp = lvinfop;
189
190 return (0);
191 err:
192 if (lvinfop->dbenv && ret != 0)
193 __db_err(lvinfop->dbenv->env, ret, "__create_log_vrfy_info");
194 (void)__destroy_log_vrfy_info(lvinfop);
195
196 return (ret);
197 }
198
199 /*
200 * __destroy_log_vrfy_info --
201 * Destroy and free a log verification handle.
202 *
203 * PUBLIC: int __destroy_log_vrfy_info __P((DB_LOG_VRFY_INFO *));
204 */
205 int
__destroy_log_vrfy_info(lvinfop)206 __destroy_log_vrfy_info(lvinfop)
207 DB_LOG_VRFY_INFO *lvinfop;
208 {
209 int ret;
210
211 ret = 0;
212 if (lvinfop == NULL)
213 return (0);
214
215 if (lvinfop->txnaborts != NULL &&
216 (ret = __db_close(lvinfop->txnaborts, NULL, 0)) != 0)
217 goto err;
218 if (lvinfop->txninfo != NULL &&
219 (ret = __db_close(lvinfop->txninfo, NULL, 0)) != 0)
220 goto err;
221 if (lvinfop->dbregids != NULL &&
222 (ret = __db_close(lvinfop->dbregids, NULL, 0)) != 0)
223 goto err;
224 if (lvinfop->fileregs != NULL &&
225 (ret = __db_close(lvinfop->fileregs, NULL, 0)) != 0)
226 goto err;
227 if (lvinfop->pgtxn != NULL &&
228 (ret = __db_close(lvinfop->pgtxn, NULL, 0)) != 0)
229 goto err;
230 if (lvinfop->lsntime != NULL &&
231 (ret = __db_close(lvinfop->lsntime, NULL, 0)) != 0)
232 goto err;
233 if (lvinfop->ckps != NULL &&
234 (ret = __db_close(lvinfop->ckps, NULL, 0)) != 0)
235 goto err;
236 if (lvinfop->txnrngs != NULL &&
237 (ret = __db_close(lvinfop->txnrngs, NULL, 0)) != 0)
238 goto err;
239 if (lvinfop->fnameuid != NULL &&
240 (ret = __db_close(lvinfop->fnameuid, NULL, 0)) != 0)
241 goto err;
242 if (lvinfop->timelsn != NULL &&
243 (ret = __db_close(lvinfop->timelsn, NULL, 0)) != 0)
244 goto err;
245 if (lvinfop->txnpg != NULL &&
246 (ret = __db_close(lvinfop->txnpg, NULL, 0)) != 0)
247 goto err;
248 if (lvinfop->dbenv != NULL &&
249 (ret = __env_close(lvinfop->dbenv, 0)) != 0)
250 goto err;
251 err:
252 __os_free(NULL, lvinfop);
253
254 return (ret);
255 }
256
257 /* Secondary index callback function for DB_LOG_VRFY_INFO->timelsn. */
258 static int
__lv_seccbk_fname(secdb,key,data,result)259 __lv_seccbk_fname(secdb, key, data, result)
260 DB *secdb;
261 const DBT *key;
262 const DBT *data;
263 DBT *result;
264 {
265 int ret, tret;
266 VRFY_FILEREG_INFO *freg;
267 char *buf;
268 size_t buflen, slen;
269
270 ret = tret = 0;
271 freg = NULL;
272 COMPQUIET(key, NULL);
273 if ((ret = __lv_unpack_filereg(data, &freg)) != 0)
274 goto out;
275 if (freg->fname == NULL || (slen = strlen(freg->fname)) == 0) {
276 ret = DB_DONOTINDEX;
277 goto out;
278 }
279
280 buflen = (slen + 1) * sizeof(char);
281 if ((ret = __os_umalloc(secdb->dbenv->env, buflen, &buf)) != 0)
282 goto out;
283 (void)strcpy(buf, freg->fname);
284 result->size = (u_int32_t)buflen;
285 result->flags |= DB_DBT_APPMALLOC;
286 result->data = buf;
287 out:
288 if (freg != NULL && (tret = __free_filereg_info(freg)) != 0 && ret == 0)
289 ret = tret;
290 return (ret);
291 }
292
293 /* Secondary index callback function for DB_LOG_VRFY_INFO->txnpg. */
294 static int
__lv_seccbk_txnpg(secdb,key,data,result)295 __lv_seccbk_txnpg(secdb, key, data, result)
296 DB *secdb;
297 const DBT *key;
298 const DBT *data;
299 DBT *result;
300 {
301 COMPQUIET(key, NULL);
302 COMPQUIET(secdb, NULL);
303 /* Txnid is the secondary key, and it's all the data dbt has. */
304 result->data = data->data;
305 result->size = data->size;
306
307 return (0);
308 }
309
310 /* Secondary index callback function for DB_LOG_VRFY_INFO->timelsn. */
311 static int
__lv_seccbk_lsn(secdb,key,data,result)312 __lv_seccbk_lsn(secdb, key, data, result)
313 DB *secdb;
314 const DBT *key;
315 const DBT *data;
316 DBT *result;
317 {
318 VRFY_TIMESTAMP_INFO *lvti;
319
320 COMPQUIET(key, NULL);
321 COMPQUIET(secdb, NULL);
322
323 lvti = (VRFY_TIMESTAMP_INFO *)data->data;
324 result->data = &(lvti->timestamp);
325 result->size = sizeof(lvti->timestamp);
326
327 return (0);
328 }
329
330 /*
331 * Open a BTREE database handle, optionally set the btree compare function
332 * and flags if any.
333 */
334 static int
__lv_open_db(dbenv,dbpp,ip,name,inmem,cmpf,sflags,dupcmpf)335 __lv_open_db(dbenv, dbpp, ip, name, inmem, cmpf, sflags, dupcmpf)
336 DB_ENV *dbenv;
337 DB **dbpp;
338 const char *name;
339 int inmem;
340 btcmp_funct cmpf;
341 u_int32_t sflags;
342 dupcmp_funct dupcmpf;
343 DB_THREAD_INFO *ip;
344 {
345 int ret;
346 const char *dbfname, *dbname;
347 DB *dbp;
348
349 dbp = NULL;
350 ret = 0;
351 if (inmem) {
352 dbfname = NULL;
353 dbname = name;
354 } else {
355 dbfname = name;
356 dbname = NULL;
357 }
358
359 BDBOP(db_create(&dbp, dbenv, 0));
360
361 if (cmpf != NULL)
362 BDBOP(__bam_set_bt_compare(dbp, cmpf));
363 if (dupcmpf != NULL)
364 dbp->dup_compare = dupcmpf;
365 if (sflags != 0)
366 BDBOP(__db_set_flags(dbp, sflags));
367 /* No concurrency needed, a big page size reduces overflow pages. */
368 BDBOP(__db_set_pagesize(dbp, 16 * 1024));
369
370 BDBOP(__db_open(dbp, ip, NULL, dbfname, dbname, DB_BTREE, DB_CREATE,
371 0666, PGNO_BASE_MD));
372
373 *dbpp = dbp;
374
375 return (0);
376 err:
377 if (dbenv != NULL && ret != 0)
378 __db_err(dbenv->env, ret, "__lv_open_db");
379 if (dbp != NULL)
380 (void)__db_close(dbp, NULL, 0);
381
382 return (ret);
383 }
384
385 /* Btree compare function for a [fileid, pgno] key. */
386 static int
__lv_fidpgno_cmp(db,dbt1,dbt2,locp)387 __lv_fidpgno_cmp(db, dbt1, dbt2, locp)
388 DB *db;
389 const DBT *dbt1;
390 const DBT *dbt2;
391 size_t * locp;
392 {
393 db_pgno_t pgno1, pgno2;
394 int ret;
395 size_t len;
396
397 COMPQUIET(db, NULL);
398 COMPQUIET(locp, NULL);
399 len = DB_FILE_ID_LEN;
400 ret = memcmp(dbt1->data, dbt2->data, len);
401 if (ret == 0) {
402 memcpy(&pgno1, (u_int8_t *)dbt1->data + len,
403 sizeof(pgno1));
404 memcpy(&pgno2, (u_int8_t *)dbt2->data + len,
405 sizeof(pgno2));
406 ret = NUMCMP(pgno1, pgno2);
407 }
408
409 return (ret);
410 }
411
412 /* Btree compare function for a int32_t type of key. */
413 static int
__lv_i32_cmp(db,dbt1,dbt2,locp)414 __lv_i32_cmp(db, dbt1, dbt2, locp)
415 DB *db;
416 const DBT *dbt1;
417 const DBT *dbt2;
418 size_t *locp;
419 {
420 int32_t k1, k2;
421
422 COMPQUIET(db, NULL);
423 COMPQUIET(locp, NULL);
424 memcpy(&k1, dbt1->data, sizeof(k1));
425 memcpy(&k2, dbt2->data, sizeof(k2));
426
427 return (NUMCMP(k1, k2));
428 }
429
430 /* Btree compare function for a u_int32_t type of key. */
431 static int
__lv_ui32_cmp(db,dbt1,dbt2,locp)432 __lv_ui32_cmp(db, dbt1, dbt2, locp)
433 DB *db;
434 const DBT *dbt1;
435 const DBT *dbt2;
436 size_t *locp;
437 {
438 u_int32_t k1, k2;
439
440 COMPQUIET(db, NULL);
441 COMPQUIET(locp, NULL);
442 memcpy(&k1, dbt1->data, sizeof(k1));
443 memcpy(&k2, dbt2->data, sizeof(k2));
444
445 return (NUMCMP(k1, k2));
446 }
447
448 /* Btree compare function for a DB_LSN type of key. */
449 static int
__lv_lsn_cmp(db,dbt1,dbt2,locp)450 __lv_lsn_cmp(db, dbt1, dbt2, locp)
451 DB *db;
452 const DBT *dbt1;
453 const DBT *dbt2;
454 size_t *locp;
455 {
456 DB_LSN lsn1, lsn2;
457
458 COMPQUIET(locp, NULL);
459 DB_ASSERT(db->env, dbt1->size == sizeof(DB_LSN));
460 DB_ASSERT(db->env, dbt2->size == sizeof(DB_LSN));
461 memcpy(&lsn1, dbt1->data, sizeof(DB_LSN));
462 memcpy(&lsn2, dbt2->data, sizeof(DB_LSN));
463
464 COMPQUIET(db, NULL);
465 return (LOG_COMPARE(&lsn1, &lsn2));
466 }
467
468 /*
469 * Structure management routines. We keep each structure on a
470 * consecutive memory chunk.
471 *
472 * The get functions will allocate memory via __os_malloc, and callers
473 * should free the memory after use. The update functions for VRFY_TXN_INFO
474 * and VRFY_FILEREG_INFO may realloc the structure.
475 */
476
477 /*
478 * PUBLIC: int __put_txn_vrfy_info __P((const DB_LOG_VRFY_INFO *,
479 * PUBLIC: const VRFY_TXN_INFO *));
480 */
481 int
__put_txn_vrfy_info(lvinfo,txninfop)482 __put_txn_vrfy_info (lvinfo, txninfop)
483 const DB_LOG_VRFY_INFO *lvinfo;
484 const VRFY_TXN_INFO *txninfop;
485 {
486 int ret;
487 DBT key, data;
488
489 ret = __lv_pack_txn_vrfy_info(txninfop, &key, &data);
490 DB_ASSERT(lvinfo->dbenv->env, ret == 0);
491
492 BDBOP2(lvinfo->dbenv, __db_put(lvinfo->txninfo, lvinfo->ip, NULL,
493 &key, &data, 0), "__put_txn_vrfy_info");
494 __os_free(lvinfo->dbenv->env, data.data);
495
496 return (0);
497 }
498
499 /* Construct a key and data DBT from the structure. */
500 static int
__lv_pack_txn_vrfy_info(txninfop,key,data)501 __lv_pack_txn_vrfy_info(txninfop, key, data)
502 const VRFY_TXN_INFO *txninfop;
503 DBT *key, *data;
504 {
505 int ret;
506 char *buf, *p;
507 size_t bufsz, len;
508 u_int32_t i;
509 DBT *pdbt;
510
511 memset(key, 0, sizeof(DBT));
512 memset(data, 0, sizeof(DBT));
513 ret = 0;
514 bufsz = TXN_VERIFY_INFO_TOTSIZE(*txninfop);
515
516 if ((ret = __os_malloc(NULL, bufsz, &buf)) != 0)
517 goto err;
518 memset(buf, 0, bufsz);
519 memcpy(buf, txninfop, TXN_VERIFY_INFO_FIXSIZE);
520 p = buf + TXN_VERIFY_INFO_FIXSIZE;
521 memcpy(p, txninfop->recycle_lsns, len = sizeof(DB_LSN) *
522 txninfop->num_recycle);
523 p += len;
524
525 for (i = 0; i < txninfop->filenum; i++) {
526
527 pdbt = &(txninfop->fileups[i]);
528 memcpy(p, &(pdbt->size), sizeof(pdbt->size));
529 p += sizeof(pdbt->size);
530 memcpy(p, pdbt->data, pdbt->size);
531 p += pdbt->size;
532 }
533
534 key->data = (void *)&txninfop->txnid;
535 key->size = sizeof(txninfop->txnid);
536 data->data = buf;
537 data->size = (u_int32_t)bufsz;
538 data->flags |= DB_DBT_MALLOC;
539 err:
540 return (ret);
541 }
542
543 /* Calculate a DBT array's total number of bytes to store. */
544 static size_t
__lv_dbt_arrsz(arr,arrlen)545 __lv_dbt_arrsz(arr, arrlen)
546 const DBT *arr;
547 u_int32_t arrlen;
548 {
549 u_int32_t i;
550 size_t sz;
551
552 sz = 0;
553
554 /* For each DBT object, store its size and its data bytes. */
555 for (i = 0; i < arrlen; i++)
556 sz += arr[i].size + sizeof(arr[i].size);
557
558 return sz;
559 }
560
561 /*
562 * __get_txn_vrfy_info --
563 * Get a VRFY_TXN_INFO object from db by txnid. Callers should free the
564 * object by calling __free_txninfo.
565 *
566 * PUBLIC: int __get_txn_vrfy_info __P((const DB_LOG_VRFY_INFO *, u_int32_t,
567 * PUBLIC: VRFY_TXN_INFO **));
568 */
569 int
__get_txn_vrfy_info(lvinfo,txnid,txninfopp)570 __get_txn_vrfy_info (lvinfo, txnid, txninfopp)
571 const DB_LOG_VRFY_INFO *lvinfo;
572 u_int32_t txnid;
573 VRFY_TXN_INFO **txninfopp;
574 {
575 int ret;
576 DBT key, data;
577
578 memset(&key, 0, sizeof(DBT));
579 memset(&data, 0, sizeof(DBT));
580 key.data = &txnid;
581 key.size = sizeof(txnid);
582
583 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->txninfo, lvinfo->ip, NULL,
584 &key, &data, 0), DB_NOTFOUND, "__get_txn_vrfy_info");
585
586 if (ret != DB_NOTFOUND)
587 ret = __lv_unpack_txn_vrfy_info(txninfopp, &data);
588
589 return (ret);
590 }
591
592 /* Construct a structure from a DBT. */
593 static int
__lv_unpack_txn_vrfy_info(txninfopp,data)594 __lv_unpack_txn_vrfy_info(txninfopp, data)
595 VRFY_TXN_INFO **txninfopp;
596 const DBT *data;
597 {
598 size_t bufsz;
599 VRFY_TXN_INFO *buf, *txninfop;
600 DB_LSN *lsns, *p;
601 u_int32_t i, sz;
602 char *pb, *q;
603 int ret;
604
605 ret = 0;
606 i = sz = 0;
607 lsns = p = NULL;
608 pb = q = NULL;
609 txninfop = (VRFY_TXN_INFO *)data->data;
610 lsns = (DB_LSN *)((char *)data->data + TXN_VERIFY_INFO_FIXSIZE);
611 pb = (char *)lsns + txninfop->num_recycle * sizeof(DB_LSN);
612
613 if ((ret = __os_malloc(NULL, bufsz = sizeof(VRFY_TXN_INFO), &buf)) != 0)
614 goto err;
615 memset(buf, 0, bufsz);
616 memcpy(buf, data->data, TXN_VERIFY_INFO_FIXSIZE);
617
618 if (txninfop->num_recycle != 0) {
619 if ((ret = __os_malloc(NULL,
620 txninfop->num_recycle * sizeof(DB_LSN), &p)) != 0)
621 goto err;
622 memcpy(p, lsns, txninfop->num_recycle * sizeof(DB_LSN));
623 buf->recycle_lsns = p;
624 }
625
626 if (txninfop->filenum != 0) {
627 if ((ret = __os_malloc(NULL,
628 txninfop->filenum * sizeof(DBT), &q)) != 0)
629 goto err;
630 memset(q, 0, txninfop->filenum * sizeof(DBT));
631 buf->fileups = (DBT *)q;
632 for (i = 0; i < txninfop->filenum; i++) {
633 memcpy(&sz, pb, sizeof(sz));
634 pb += sizeof(sz);
635 if ((ret = __os_malloc(NULL, sz, &q)) != 0)
636 goto err;
637 memcpy(q, pb, sz);
638 pb += sz;
639
640 buf->fileups[i].data = q;
641 buf->fileups[i].size = sz;
642 }
643 }
644
645 *txninfopp = buf;
646 err:
647 return (ret);
648 }
649
650 static int
__lv_add_recycle_lsn(txninfop,lsn)651 __lv_add_recycle_lsn (txninfop, lsn)
652 VRFY_TXN_INFO *txninfop;
653 const DB_LSN *lsn;
654 {
655 int ret;
656
657 ret = 0;
658 txninfop->num_recycle++;
659 if ((ret = __os_realloc(NULL, txninfop->num_recycle * sizeof(DB_LSN),
660 &(txninfop->recycle_lsns))) != 0)
661 goto err;
662 txninfop->recycle_lsns[txninfop->num_recycle - 1] = *lsn;
663 err:
664 return (ret);
665 }
666
667 /*
668 * __add_recycle_lsn_range --
669 * Add recycle info for each txn within the recycled txnid range.
670 *
671 * PUBLIC: int __add_recycle_lsn_range __P((DB_LOG_VRFY_INFO *,
672 * PUBLIC: const DB_LSN *, u_int32_t, u_int32_t));
673 */
674 int
__add_recycle_lsn_range(lvinfo,lsn,min,max)675 __add_recycle_lsn_range(lvinfo, lsn, min, max)
676 DB_LOG_VRFY_INFO *lvinfo;
677 const DB_LSN *lsn;
678 u_int32_t min, max;
679 {
680 DBC *csr;
681 int ret, tret;
682 u_int32_t i;
683 DBT key2, data2;
684 struct __add_recycle_params param;
685
686 csr = NULL;
687 ret = tret = 0;
688 memset(&key2, 0, sizeof(DBT));
689 memset(&data2, 0, sizeof(DBT));
690 memset(¶m, 0, sizeof(param));
691
692 if ((ret = __os_malloc(lvinfo->dbenv->env, sizeof(VRFY_TXN_INFO *) *
693 (param.ti2ul = 1024), &(param.ti2u))) != 0)
694 goto err;
695 param.ti2ui = 0;
696 param.recycle_lsn = *lsn;
697 param.min = min;
698 param.max = max;
699
700 /* Iterate the specified range and process each transaction. */
701 if ((ret = __iterate_txninfo(lvinfo, min, max, __lv_add_recycle_handler,
702 ¶m)) != 0)
703 goto err;
704
705 /*
706 * Save updated txninfo structures. We can't do so in the above
707 * iteration, so we have to save them here.
708 */
709 BDBOP(__db_cursor(lvinfo->txninfo, lvinfo->ip, NULL, &csr, DBC_BULK));
710
711 for (i = 0; i < param.ti2ui; i++) {
712 ret = __lv_pack_txn_vrfy_info(param.ti2u[i], &key2, &data2);
713 DB_ASSERT(lvinfo->dbenv->env, ret == 0);
714 BDBOP(__dbc_put(csr, &key2, &data2, DB_KEYLAST));
715 /*
716 * key2.data refers to param.ti2u[i]'s memory, data2.data is
717 * freed by DB since we set DB_DBT_MALLOC.
718 */
719 if ((ret = __free_txninfo(param.ti2u[i])) != 0)
720 goto err;
721 }
722
723 err:
724 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
725 ret = tret;
726 __os_free(lvinfo->dbenv->env, param.ti2u);
727 if (ret != 0)
728 __db_err(lvinfo->dbenv->env, ret,
729 "__add_recycle_lsn_range");
730
731 return (ret);
732 }
733
734 /*
735 * __iterate_txninfo --
736 * Iterate throught the transaction info database as fast as possible,
737 * and process each key/data pair using a callback handler. Break the
738 * iteration if the handler returns non-zero values.
739 *
740 * PUBLIC: int __iterate_txninfo __P((DB_LOG_VRFY_INFO *, u_int32_t,
741 * PUBLIC: u_int32_t, TXNINFO_HANDLER, void *));
742 */
743 int
__iterate_txninfo(lvinfo,min,max,handler,param)744 __iterate_txninfo(lvinfo, min, max, handler, param)
745 DB_LOG_VRFY_INFO *lvinfo;
746 u_int32_t min, max;
747 TXNINFO_HANDLER handler;
748 void *param;
749 {
750 ENV *env;
751 VRFY_TXN_INFO *txninfop;
752 int ret, tret;
753 u_int32_t bufsz, pgsz, txnid;
754 size_t retkl, retdl;
755 char *btbuf;
756 u_int8_t *retk, *retd;
757 DBT key, data, data2;
758 DBC *csr;
759 void *p;
760
761 csr = NULL;
762 env = lvinfo->dbenv->env;
763 txninfop = NULL;
764 ret = tret = 0;
765 txnid = 0;
766 retkl = retdl = 0;
767 bufsz = 64 * 1024;
768 btbuf = NULL;
769 retk = retd = NULL;
770
771 memset(&key, 0, sizeof(DBT));
772 memset(&data, 0, sizeof(DBT));
773 memset(&data2, 0, sizeof(DBT));
774
775 pgsz = lvinfo->txninfo->pgsize;
776 DB_ASSERT(env, ret == 0);
777
778 if (bufsz % pgsz != 0)
779 bufsz = pgsz * (bufsz / pgsz);
780
781 if ((ret = __os_malloc(env, bufsz, &btbuf)) != 0)
782 goto err;
783
784 BDBOP(__db_cursor(lvinfo->txninfo, lvinfo->ip, NULL, &csr, DBC_BULK));
785
786 /*
787 * Use bulk retrieval to scan the database as fast as possible.
788 */
789 data.data = btbuf;
790 data.ulen = bufsz;
791 data.flags |= DB_DBT_USERMEM;
792
793 for (ret = __dbc_get(csr, &key, &data, DB_FIRST | DB_MULTIPLE_KEY) ;;
794 ret = __dbc_get(csr, &key, &data, DB_NEXT | DB_MULTIPLE_KEY)) {
795 switch (ret) {
796 case 0:
797 break;
798 case DB_NOTFOUND:
799 goto out;
800 /* No break statement allowed by lint here. */
801 case DB_BUFFER_SMALL:
802 if ((ret = __os_realloc(lvinfo->dbenv->env,
803 bufsz *= 2, &btbuf)) != 0)
804 goto out;
805 data.ulen = bufsz;
806 data.data = btbuf;
807 continue;/* Continue the for-loop. */
808 /* No break statement allowed by lint here. */
809 default:
810 goto err;
811 }
812
813 /*
814 * Do bulk get. Some txninfo objects may be updated by the
815 * handler, but we can't store them immediately in the same
816 * loop because we wouldn't be able to continue the bulk get
817 * using the same cursor; and we can't use another cursor
818 * otherwise we may self-block. In the handler we need to
819 * store the updated objects and store them to db when we get
820 * out of this loop.
821 */
822 DB_MULTIPLE_INIT(p, &data);
823 while (1) {
824 DB_MULTIPLE_KEY_NEXT(p, &data,
825 retk, retkl, retd, retdl);
826 if (p == NULL)
827 break;
828 DB_ASSERT(env, retkl == sizeof(txnid) && retk != NULL);
829 memcpy(&txnid, retk, retkl);
830 /*
831 * Process it if txnid in range or no range specified.
832 * The range must be a closed one.
833 */
834 if ((min != 0 && txnid >= min && max != 0 &&
835 txnid <= max) || (min == 0 && max == 0)) {
836 data2.data = retd;
837 data2.size = (u_int32_t)retdl;
838
839 if ((ret = __lv_unpack_txn_vrfy_info(
840 &txninfop, &data2)) != 0)
841 goto out;
842 if ((ret = handler(lvinfo, txninfop,
843 param)) != 0)
844 /* Stop the iteration on error. */
845 goto out;
846 }
847 }
848
849 }
850 out:
851 if (ret == DB_NOTFOUND)
852 ret = 0;
853 err:
854 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
855 ret = tret;
856 __os_free(lvinfo->dbenv->env, btbuf);
857 return (ret);
858 }
859
860 /* Txninfo iteration handler to add recycle info for affected txns. */
861 static int
__lv_add_recycle_handler(lvinfo,txninfop,params)862 __lv_add_recycle_handler(lvinfo, txninfop, params)
863 DB_LOG_VRFY_INFO *lvinfo;
864 VRFY_TXN_INFO *txninfop;
865 void *params;
866 {
867 int ret;
868 struct __add_recycle_params *param;
869
870 ret = 0;
871 param = (struct __add_recycle_params *)params;
872
873 /*
874 * If the txnid is reused, update its recycle info and note it for
875 * later update, otherwise free the txninfop structure.
876 */
877 if (txninfop->txnid < param->min && txninfop->txnid > param->max) {
878 ret = __free_txninfo(txninfop);
879 return (ret);
880 }
881
882 ret = __lv_add_recycle_lsn(txninfop, &(param->recycle_lsn));
883
884 if (ret != 0)
885 goto err;
886 /*
887 * Below is one way to tell if a txn is aborted without doing another
888 * backward pass of the log. However if the txn id is not in the
889 * chosen recycled txn id range, we can't tell, until all the log
890 * records are passed --- the remaining active txns are the aborted
891 * txns.
892 * No longer needed since we did another backward pass of the log
893 * and have all the txn lifetimes.
894 if (txninfop->status == TXN_STAT_ACTIVE)
895 __on_txn_abort(lvinfo, txninfop);
896 */
897 if (txninfop->status == TXN_STAT_PREPARE) {
898 __db_errx(lvinfo->dbenv->env,
899 "[ERROR] Transaction with ID %u is prepared and not "
900 "committed, but its ID is recycled by log record [%u, %u].",
901 txninfop->txnid, param->recycle_lsn.file,
902 param->recycle_lsn.offset);
903 }
904 /* Note down to store later. */
905 param->ti2u[(param->ti2ui)++] = txninfop;
906 if (param->ti2ui == param->ti2ul)
907 BDBOP(__os_realloc(lvinfo->dbenv->env,
908 sizeof(VRFY_TXN_INFO *) * (param->ti2ul *= 2),
909 &(param->ti2u)));
910 err:
911 return (ret);
912
913 }
914 /*
915 * PUBLIC: int __rem_last_recycle_lsn __P((VRFY_TXN_INFO *));
916 */
917 int
__rem_last_recycle_lsn(txninfop)918 __rem_last_recycle_lsn(txninfop)
919 VRFY_TXN_INFO *txninfop;
920 {
921 int ret;
922
923 ret = 0;
924 if (txninfop->num_recycle == 0)
925 return (0);
926 txninfop->num_recycle--;
927 if (txninfop->num_recycle > 0)
928 BDBOP(__os_realloc(NULL, txninfop->num_recycle * sizeof(DB_LSN),
929 &(txninfop->recycle_lsns)));
930 else {
931 __os_free(NULL, txninfop->recycle_lsns);
932 txninfop->recycle_lsns = NULL;
933 }
934 err:
935 return (ret);
936
937 }
938
939 /*
940 * __add_file_updated --
941 * Add a file's dbregid and uid to the updating txn if it's not yet
942 * recorded.
943 *
944 * PUBLIC: int __add_file_updated __P((VRFY_TXN_INFO *, const DBT *, int32_t));
945 */
946 int
__add_file_updated(txninfop,fileid,dbregid)947 __add_file_updated (txninfop, fileid, dbregid)
948 VRFY_TXN_INFO *txninfop;
949 const DBT *fileid;
950 int32_t dbregid;
951 {
952 int ret;
953 DBT *pdbt, *p;
954 u_int32_t found, i;
955
956 ret = 0;
957 p = pdbt = NULL;
958
959 for (found = 0, i = 0; i < txninfop->filenum; i++) {
960 p = &(txninfop->fileups[i]);
961 if (p->size == fileid->size &&
962 memcmp(p->data, fileid->data, p->size) == 0) {
963 found = 1;
964 break;
965 }
966 }
967
968 if (found)
969 return (0);
970
971 /* Add file's uid into the array, deep copy from fileid. */
972 txninfop->filenum++;
973 if ((ret = __os_realloc(NULL, txninfop->filenum *
974 sizeof(DBT), &(txninfop->fileups))) != 0)
975 goto err;
976
977 pdbt = &(txninfop->fileups[txninfop->filenum - 1]);
978 memset(pdbt, 0, sizeof(DBT));
979 if ((ret = __os_malloc(NULL,
980 pdbt->size = fileid->size, &(pdbt->data))) != 0)
981 goto err;
982 memcpy(pdbt->data, fileid->data, fileid->size);
983
984 /* Add file dbregid into the array. */
985 BDBOP(__os_realloc(NULL, txninfop->filenum *
986 sizeof(int32_t), &(txninfop->dbregid)));
987 txninfop->dbregid[txninfop->filenum - 1] = dbregid;
988 err:
989 return (ret);
990 }
991
992 /*
993 * PUBLIC: int __del_file_updated __P((VRFY_TXN_INFO *, const DBT *));
994 */
995 int
__del_file_updated(txninfop,fileid)996 __del_file_updated (txninfop, fileid)
997 VRFY_TXN_INFO *txninfop;
998 const DBT *fileid;
999 {
1000 u_int32_t found, i;
1001 int ret;
1002 DBT *p;
1003 void *pdbtdata;
1004
1005 ret = 0;
1006
1007 if (txninfop->filenum == 0)
1008 return (0);
1009
1010 /*
1011 * If the array has an element identical to fileid, remove it. fileid
1012 * itself is intact after this function call.
1013 */
1014 for (found = 0, i = 0, pdbtdata = NULL; i < txninfop->filenum; i++) {
1015 p = &(txninfop->fileups[i]);
1016 if (p->size == fileid->size &&
1017 memcmp(p->data, fileid->data, p->size) == 0) {
1018 pdbtdata = p->data;
1019 if (txninfop->filenum > 1) {
1020 memmove(txninfop->fileups + i, txninfop->
1021 fileups + i + 1, sizeof(DBT) * (txninfop->
1022 filenum - (i + 1)));
1023 memmove(txninfop->dbregid + i, txninfop->
1024 dbregid + i + 1, sizeof(int32_t) *
1025 (txninfop->filenum - (i + 1)));
1026 } else {
1027 __os_free(NULL, txninfop->fileups);
1028 __os_free(NULL, txninfop->dbregid);
1029 txninfop->fileups = NULL;
1030 txninfop->dbregid = NULL;
1031 }
1032 found = 1;
1033 break;
1034 }
1035 }
1036
1037 if (found) {
1038 txninfop->filenum--;
1039 if (txninfop->filenum) {
1040 BDBOP(__os_realloc(NULL, sizeof(DBT) *
1041 txninfop->filenum, &(txninfop->fileups)));
1042 BDBOP(__os_realloc(NULL, sizeof(int32_t) *
1043 txninfop->filenum, &(txninfop->dbregid)));
1044 }
1045 __os_free(NULL, pdbtdata);
1046 }
1047 err:
1048 return (ret);
1049 }
1050
1051 /*
1052 * PUBLIC: int __clear_fileups __P((VRFY_TXN_INFO *));
1053 */
1054 int
__clear_fileups(txninfop)1055 __clear_fileups(txninfop)
1056 VRFY_TXN_INFO *txninfop;
1057 {
1058 u_int32_t i;
1059
1060 for (i = 0; i < txninfop->filenum; i++)
1061 __os_free(NULL, txninfop->fileups[i].data);
1062
1063 __os_free(NULL, txninfop->fileups);
1064 __os_free(NULL, txninfop->dbregid);
1065 txninfop->fileups = NULL;
1066 txninfop->dbregid = NULL;
1067 txninfop->filenum = 0;
1068
1069 return (0);
1070 }
1071
1072 /*
1073 * __free_txninfo_stack --
1074 * The object is on stack, only free its internal memory, not itself.
1075 * PUBLIC: int __free_txninfo_stack __P((VRFY_TXN_INFO *));
1076 */
1077 int
__free_txninfo_stack(p)1078 __free_txninfo_stack (p)
1079 VRFY_TXN_INFO *p;
1080 {
1081 u_int32_t i;
1082
1083 if (p == NULL)
1084 return (0);
1085
1086 if (p->fileups != NULL) {
1087 for (i = 0; i < p->filenum; i++)
1088 __os_free(NULL, p->fileups[i].data);
1089 __os_free(NULL, p->fileups);
1090 }
1091
1092 if (p->dbregid != NULL)
1093 __os_free(NULL, p->dbregid);
1094
1095 if (p->recycle_lsns != NULL)
1096 __os_free(NULL, p->recycle_lsns);
1097
1098 return (0);
1099 }
1100 /*
1101 * PUBLIC: int __free_txninfo __P((VRFY_TXN_INFO *));
1102 */
1103 int
__free_txninfo(p)1104 __free_txninfo(p)
1105 VRFY_TXN_INFO *p;
1106 {
1107 (void)__free_txninfo_stack(p);
1108 __os_free(NULL, p);
1109
1110 return (0);
1111 }
1112
1113 /* Construct a key and data DBT from the structure. */
1114 static int
__lv_pack_filereg(freginfo,data)1115 __lv_pack_filereg(freginfo, data)
1116 const VRFY_FILEREG_INFO *freginfo;
1117 DBT *data;
1118 {
1119 char *buf, *p;
1120 size_t bufsz, offset;
1121 int ret;
1122
1123 ret = 0;
1124 if ((ret = __os_malloc(NULL,
1125 bufsz = FILE_REG_INFO_TOTSIZE(*freginfo), &buf)) != 0)
1126 goto err;
1127 memset(buf, 0, bufsz);
1128
1129 memcpy(buf, freginfo, FILE_REG_INFO_FIXSIZE);
1130 p = buf + FILE_REG_INFO_FIXSIZE;
1131
1132 offset = sizeof(int32_t) * freginfo->regcnt;
1133 memcpy(p, freginfo->dbregids, offset);
1134 p += offset;
1135
1136 memcpy(p, &(freginfo->fileid.size), sizeof(freginfo->fileid.size));
1137 p += sizeof(freginfo->fileid.size);
1138 memcpy(p, freginfo->fileid.data, freginfo->fileid.size);
1139 p += freginfo->fileid.size;
1140 (void)strcpy(p, freginfo->fname);
1141
1142 data->data = buf;
1143 data->size = (u_int32_t)bufsz;
1144 err:
1145 return (ret);
1146 }
1147
1148 /*
1149 * PUBLIC: int __put_filereg_info __P((const DB_LOG_VRFY_INFO *,
1150 * PUBLIC: const VRFY_FILEREG_INFO *));
1151 */
__put_filereg_info(lvinfo,freginfo)1152 int __put_filereg_info (lvinfo, freginfo)
1153 const DB_LOG_VRFY_INFO *lvinfo;
1154 const VRFY_FILEREG_INFO *freginfo;
1155 {
1156
1157 int ret;
1158 DBT data;
1159
1160 memset(&data, 0, sizeof(DBT));
1161
1162 if ((ret = __lv_pack_filereg(freginfo, &data)) != 0)
1163 goto err;
1164
1165 /*
1166 * We store dbregid-filereg map into dbregids.db, but we can't make
1167 * dbregids.db the sec db of fileregs.db, because dbregid is only
1168 * valid when a db file is open, we want to delete data with same
1169 * key in dbregids.db, but we want to keep all filereg_info data in
1170 * fileregs.db to track all db file lifetime and status.
1171 *
1172 * Consequently we will store dbregid-file_uid in dbregs.db, so that we
1173 * can delete dbregid when the db handle is closed, and we can
1174 * use the dbregid to get the currently open db file's uid.
1175 */
1176
1177 BDBOP2(lvinfo->dbenv, __db_put(lvinfo->fileregs, lvinfo->ip, NULL,
1178 (DBT *)&(freginfo->fileid), &data, 0), "__put_filereg_info");
1179
1180 err:
1181 if (data.data != NULL)
1182 __os_free(lvinfo->dbenv->env, data.data);
1183
1184 return (ret);
1185 }
1186
1187 /*
1188 * PUBLIC: int __del_filelife __P((const DB_LOG_VRFY_INFO *, int32_t));
1189 */
1190 int
__del_filelife(lvinfo,dbregid)1191 __del_filelife(lvinfo, dbregid)
1192 const DB_LOG_VRFY_INFO *lvinfo;
1193 int32_t dbregid;
1194 {
1195 int ret;
1196 DBT key;
1197
1198 memset(&key, 0, sizeof(DBT));
1199 key.data = &(dbregid);
1200 key.size = sizeof(dbregid);
1201
1202 if ((ret = __db_del(lvinfo->dbregids, lvinfo->ip, NULL,
1203 &key, 0)) != 0)
1204 goto err;
1205
1206 err:
1207 return (ret);
1208 }
1209
1210 /*
1211 * PUBLIC: int __put_filelife __P((const DB_LOG_VRFY_INFO *, VRFY_FILELIFE *));
1212 */
1213 int
__put_filelife(lvinfo,pflife)1214 __put_filelife (lvinfo, pflife)
1215 const DB_LOG_VRFY_INFO *lvinfo;
1216 VRFY_FILELIFE *pflife;
1217 {
1218 int ret;
1219 DBT key, data;
1220
1221 memset(&key, 0, sizeof(DBT));
1222 memset(&data, 0, sizeof(DBT));
1223 key.data = &(pflife->dbregid);
1224 key.size = sizeof(pflife->dbregid);
1225 data.data = pflife;
1226 data.size = sizeof(VRFY_FILELIFE);
1227
1228 if ((ret = __db_put(lvinfo->dbregids, lvinfo->ip, NULL,
1229 &key, &data, 0)) != 0)
1230 goto err;
1231
1232 err:
1233 return (ret);
1234 }
1235
1236 /*
1237 * PUBLIC: int __get_filelife __P((const DB_LOG_VRFY_INFO *,
1238 * PUBLIC: int32_t, VRFY_FILELIFE **));
1239 */
1240 int
__get_filelife(lvinfo,dbregid,flifepp)1241 __get_filelife (lvinfo, dbregid, flifepp)
1242 const DB_LOG_VRFY_INFO *lvinfo;
1243 int32_t dbregid;
1244 VRFY_FILELIFE **flifepp;
1245 {
1246 int ret;
1247 DBT key, data;
1248 VRFY_FILELIFE *flifep;
1249
1250 ret = 0;
1251 flifep = NULL;
1252 memset(&key, 0, sizeof(DBT));
1253 memset(&data, 0, sizeof(DBT));
1254
1255 key.data = &dbregid;
1256 key.size = sizeof(dbregid);
1257 if ((ret = __db_get(lvinfo->dbregids, lvinfo->ip, NULL,
1258 &key, &data, 0)) != 0)
1259 goto err;
1260 if ((ret = __os_malloc(lvinfo->dbenv->env,
1261 sizeof(VRFY_FILELIFE), &flifep)) != 0)
1262 goto err;
1263 DB_ASSERT(lvinfo->dbenv->env, flifep != NULL);
1264 memcpy(flifep, data.data, sizeof(VRFY_FILELIFE));
1265 *flifepp = flifep;
1266 err:
1267 return (ret);
1268 }
1269
1270 /*
1271 * PUBLIC: int __get_filereg_by_dbregid __P((const DB_LOG_VRFY_INFO *,
1272 * PUBLIC: int32_t, VRFY_FILEREG_INFO **));
1273 */
1274 int
__get_filereg_by_dbregid(lvinfo,dbregid,freginfopp)1275 __get_filereg_by_dbregid(lvinfo, dbregid, freginfopp)
1276 const DB_LOG_VRFY_INFO *lvinfo;
1277 int32_t dbregid;
1278 VRFY_FILEREG_INFO **freginfopp;
1279 {
1280 int ret;
1281 DBT key, data;
1282 char uid[DB_FILE_ID_LEN];
1283 VRFY_FILELIFE *pflife;
1284
1285 memset(&data, 0, sizeof(DBT));
1286 memset(&key, 0, sizeof(DBT));
1287 key.data = &dbregid;
1288 key.size = sizeof(dbregid);
1289
1290 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->dbregids, lvinfo->ip, NULL,
1291 &key, &data, 0), DB_NOTFOUND, "__get_filereg_by_dbregid");
1292 if (ret == DB_NOTFOUND)
1293 goto err;
1294
1295 /* Use the file-uid as key to retrieve from fileregs.db. */
1296 pflife = (VRFY_FILELIFE *)data.data;
1297 memcpy((void *)uid, (void *)pflife->fileid, key.size = DB_FILE_ID_LEN);
1298
1299 key.data = (void *)uid;
1300 memset(&data, 0, sizeof(DBT));
1301
1302 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->fileregs, lvinfo->ip, NULL,
1303 &key, &data, 0), DB_NOTFOUND, "__get_filereg_by_dbregid");
1304 if (ret == DB_NOTFOUND)
1305 goto err;
1306 if ((ret = __lv_unpack_filereg(&data, freginfopp)) != 0)
1307 goto err;
1308
1309 err:
1310 return (ret);
1311 }
1312
1313 /*
1314 * PUBLIC: int __add_dbregid __P((DB_LOG_VRFY_INFO *, VRFY_FILEREG_INFO *,
1315 * PUBLIC: int32_t, u_int32_t, DB_LSN, DBTYPE, db_pgno_t, int *));
1316 */
1317 int
__add_dbregid(lvh,freg,dbregid,opcode,lsn,dbtype,meta_pgno,addp)1318 __add_dbregid(lvh, freg, dbregid, opcode, lsn, dbtype, meta_pgno, addp)
1319 DB_LOG_VRFY_INFO *lvh;
1320 VRFY_FILEREG_INFO *freg;
1321 int32_t dbregid;
1322 u_int32_t opcode;
1323 DB_LSN lsn;
1324 DBTYPE dbtype;
1325 db_pgno_t meta_pgno;
1326 int *addp;
1327 {
1328 int inarray, ret, tret;
1329 u_int32_t i, j;
1330 VRFY_FILELIFE flife;
1331
1332 inarray = ret = tret = 0;
1333 for (i = 0; i < freg->regcnt; i++) {
1334 if (freg->dbregids[i] == dbregid) {
1335 if (!IS_DBREG_CLOSE(opcode)) {
1336 /* Opening an open dbreg id. */
1337 if (IS_DBREG_OPEN(opcode) &&
1338 (opcode != DBREG_CHKPNT &&
1339 opcode != DBREG_XCHKPNT)) {
1340 tret = 2;
1341 goto err;
1342 }
1343 tret = 0;
1344 inarray = 1;
1345 } else
1346 /* Found the dbregid; gonna remove it. */
1347 tret = -1;
1348 break;
1349 }
1350 }
1351
1352 if (IS_DBREG_OPEN(opcode))
1353 tret = 1;/* dbregid not in the array, gonna add 1. */
1354
1355 /*
1356 * Remove closed dbregid. dbregid can be recycled, not unique to a db
1357 * file, it's dynamically allocated for each db handle.
1358 */
1359 if (tret == -1) {
1360 for (j = i; j < freg->regcnt - 1; j++)
1361 freg->dbregids[j] = freg->dbregids[j + 1];
1362 freg->regcnt--;
1363 BDBOP(__os_realloc(lvh->dbenv->env,
1364 sizeof(int32_t) * freg->regcnt, &(freg->dbregids)));
1365 /* Don't remove dbregid life info from dbregids db. */
1366 } else if (tret == 1) {
1367 if (!inarray) {
1368 freg->regcnt++;
1369 BDBOP(__os_realloc(lvh->dbenv->env,
1370 sizeof(int32_t) * freg->regcnt, &(freg->dbregids)));
1371 freg->dbregids[freg->regcnt - 1] = dbregid;
1372 }
1373 flife.dbregid = dbregid;
1374 memcpy(flife.fileid, freg->fileid.data, freg->fileid.size);
1375 flife.lifetime = opcode;
1376 flife.dbtype = dbtype;
1377 flife.lsn = lsn;
1378 flife.meta_pgno = meta_pgno;
1379 if ((ret = __put_filelife(lvh, &flife)) != 0)
1380 goto err;
1381 }
1382
1383 err:
1384 *addp = tret;
1385 return (ret);
1386
1387 }
1388
1389 /*
1390 * PUBLIC: int __get_filereg_info __P((const DB_LOG_VRFY_INFO *, const DBT *,
1391 * PUBLIC: VRFY_FILEREG_INFO **));
1392 */
1393 int
__get_filereg_info(lvinfo,fuid,freginfopp)1394 __get_filereg_info (lvinfo, fuid, freginfopp)
1395 const DB_LOG_VRFY_INFO *lvinfo;
1396 const DBT *fuid;
1397 VRFY_FILEREG_INFO **freginfopp;
1398 {
1399 int ret;
1400 DBT data;
1401
1402 memset(&data, 0, sizeof(DBT));
1403
1404 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->fileregs, lvinfo->ip, NULL,
1405 (DBT *)fuid, &data, 0), DB_NOTFOUND, "__get_filereg_info");
1406 if (ret == DB_NOTFOUND)
1407 goto err;
1408 if ((ret = __lv_unpack_filereg(&data, freginfopp)) != 0)
1409 goto err;
1410
1411 err:
1412 return (ret);
1413 }
1414
1415 static int
__lv_unpack_filereg(data,freginfopp)1416 __lv_unpack_filereg(data, freginfopp)
1417 const DBT *data;
1418 VRFY_FILEREG_INFO **freginfopp;
1419 {
1420 char *p, *q;
1421 u_int32_t fidsz, arrsz;
1422 VRFY_FILEREG_INFO *buf;
1423 int ret;
1424
1425 ret = 0;
1426 p = q = NULL;
1427 fidsz = arrsz = 0;
1428 buf = NULL;
1429
1430 if ((ret = __os_malloc(NULL, sizeof(VRFY_FILEREG_INFO), &buf)) != 0)
1431 goto err;
1432 memset(buf, 0, sizeof(VRFY_FILEREG_INFO));
1433
1434 memcpy(buf, data->data, FILE_REG_INFO_FIXSIZE);
1435 *freginfopp = (VRFY_FILEREG_INFO *)buf;
1436 p = ((char *)(data->data)) + FILE_REG_INFO_FIXSIZE;
1437
1438 if ((ret = __os_malloc(NULL, arrsz = (*freginfopp)->regcnt *
1439 sizeof(int32_t), &((*freginfopp)->dbregids))) != 0)
1440 goto err;
1441 memcpy((*freginfopp)->dbregids, p, arrsz);
1442 p += arrsz;
1443
1444 memcpy(&fidsz, p, sizeof(fidsz));
1445 p += sizeof(fidsz);
1446 if ((ret = __os_malloc(NULL, fidsz, &q)) != 0)
1447 goto err;
1448 memcpy(q, p, fidsz);
1449 (*freginfopp)->fileid.data = q;
1450 (*freginfopp)->fileid.size = fidsz;
1451 p += fidsz;
1452
1453 if ((ret = __os_malloc(NULL, sizeof(char) * (strlen(p) + 1), &q)) != 0)
1454 goto err;
1455 (void)strcpy(q, p);
1456
1457 (*freginfopp)->fname = q;
1458 err:
1459 return (ret);
1460 }
1461
1462 /*
1463 * PUBLIC: int __free_filereg_info __P((VRFY_FILEREG_INFO *));
1464 */
1465 int
__free_filereg_info(p)1466 __free_filereg_info(p)
1467 VRFY_FILEREG_INFO *p;
1468 {
1469 if (p == NULL)
1470 return (0);
1471 if (p ->fname != NULL)
1472 __os_free(NULL, (void *)(p->fname));
1473 if (p->fileid.data != NULL)
1474 __os_free(NULL, p->fileid.data);
1475 if (p->dbregids != NULL)
1476 __os_free(NULL, p->dbregids);
1477 __os_free(NULL, p);
1478
1479 return (0);
1480 }
1481
1482 /*
1483 * PUBLIC: int __get_ckp_info __P((const DB_LOG_VRFY_INFO *, DB_LSN,
1484 * PUBLIC: VRFY_CKP_INFO **));
1485 */
1486 int
__get_ckp_info(lvinfo,lsn,ckpinfopp)1487 __get_ckp_info (lvinfo, lsn, ckpinfopp)
1488 const DB_LOG_VRFY_INFO *lvinfo;
1489 DB_LSN lsn;
1490 VRFY_CKP_INFO **ckpinfopp;
1491 {
1492 int ret;
1493 DBT key, data;
1494 VRFY_CKP_INFO *ckpinfo;
1495
1496 memset(&key, 0, sizeof(DBT));
1497 memset(&data, 0, sizeof(DBT));
1498 key.data = &lsn;
1499 key.size = sizeof(DB_LSN);
1500 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->ckps, lvinfo->ip, NULL,
1501 &key, &data, 0), DB_NOTFOUND, "__get_ckp_info");
1502
1503 if (ret == DB_NOTFOUND)
1504 goto err;
1505
1506 if ((ret = __os_malloc(lvinfo->dbenv->env,
1507 sizeof(VRFY_CKP_INFO), &ckpinfo)) != 0)
1508 goto err;
1509 memcpy(ckpinfo, data.data, sizeof(VRFY_CKP_INFO));
1510 *ckpinfopp = ckpinfo;
1511 err:
1512 return (ret);
1513
1514 }
1515
1516 /*
1517 * PUBLIC: int __get_last_ckp_info __P((const DB_LOG_VRFY_INFO *,
1518 * PUBLIC: VRFY_CKP_INFO **));
1519 */
1520 int
__get_last_ckp_info(lvinfo,ckpinfopp)1521 __get_last_ckp_info (lvinfo, ckpinfopp)
1522 const DB_LOG_VRFY_INFO *lvinfo;
1523 VRFY_CKP_INFO **ckpinfopp;
1524 {
1525 int ret, tret;
1526 DBT key, data;
1527 VRFY_CKP_INFO *ckpinfo;
1528 DBC *csr;
1529
1530 csr = NULL;
1531 memset(&key, 0, sizeof(DBT));
1532 memset(&data, 0, sizeof(DBT));
1533
1534 BDBOP(__db_cursor(lvinfo->ckps, lvinfo->ip, NULL, &csr, 0));
1535 if ((ret = __dbc_get(csr, &key, &data, DB_LAST)) != 0)
1536 goto err;
1537
1538 if ((ret = __os_malloc(lvinfo->dbenv->env,
1539 sizeof(VRFY_CKP_INFO), &ckpinfo)) != 0)
1540 goto err;
1541 DB_ASSERT(lvinfo->dbenv->env, sizeof(VRFY_CKP_INFO) == data.size);
1542 memcpy(ckpinfo, data.data, sizeof(VRFY_CKP_INFO));
1543 *ckpinfopp = ckpinfo;
1544 err:
1545 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1546 ret = tret;
1547 if (ret != 0 && ret != DB_NOTFOUND)
1548 __db_err(lvinfo->dbenv->env, ret, "__get_last_ckp_info");
1549 return (ret);
1550 }
1551
1552 /*
1553 * PUBLIC: int __put_ckp_info __P((const DB_LOG_VRFY_INFO *,
1554 * PUBLIC: const VRFY_CKP_INFO *));
1555 */
__put_ckp_info(lvinfo,ckpinfo)1556 int __put_ckp_info (lvinfo, ckpinfo)
1557 const DB_LOG_VRFY_INFO *lvinfo;
1558 const VRFY_CKP_INFO *ckpinfo;
1559 {
1560 int ret;
1561 DBT key, data;
1562
1563 memset(&key, 0, sizeof(DBT));
1564 memset(&data, 0, sizeof(DBT));
1565 key.data = (void *)&ckpinfo->lsn;
1566 key.size = sizeof(DB_LSN);
1567 data.data = (void *)ckpinfo;
1568 data.size = sizeof(VRFY_CKP_INFO);
1569
1570 BDBOP2(lvinfo->dbenv, __db_put(lvinfo->ckps, lvinfo->ip,
1571 NULL, &key, &data, 0), "__put_ckp_info");
1572 return (0);
1573 }
1574
1575 /*
1576 * PUBLIC: int __get_timestamp_info __P((const DB_LOG_VRFY_INFO *,
1577 * PUBLIC: DB_LSN, VRFY_TIMESTAMP_INFO **));
1578 */
__get_timestamp_info(lvinfo,lsn,tsinfopp)1579 int __get_timestamp_info (lvinfo, lsn, tsinfopp)
1580 const DB_LOG_VRFY_INFO *lvinfo;
1581 DB_LSN lsn;
1582 VRFY_TIMESTAMP_INFO **tsinfopp;
1583 {
1584 int ret;
1585 DBT key, data;
1586 VRFY_TIMESTAMP_INFO *tsinfo;
1587
1588 memset(&key, 0, sizeof(DBT));
1589 memset(&data, 0, sizeof(DBT));
1590 key.data = &lsn;
1591 key.size = sizeof(DB_LSN);
1592 BDBOP3(lvinfo->dbenv, __db_get(lvinfo->lsntime, lvinfo->ip, NULL,
1593 &key, &data, 0), DB_NOTFOUND, "__get_timestamp_info");
1594
1595 if (ret == DB_NOTFOUND)
1596 goto err;
1597
1598 if ((ret = __os_malloc(lvinfo->dbenv->env,
1599 sizeof(VRFY_TIMESTAMP_INFO), &tsinfo)) != 0)
1600 goto err;
1601
1602 memcpy(tsinfo, data.data, sizeof(VRFY_TIMESTAMP_INFO));
1603 *tsinfopp = tsinfo;
1604 err:
1605 return (ret);
1606 }
1607
1608 /*
1609 * __get_latest_timestamp_info --
1610 * Get latest timestamp info before lsn.
1611 * PUBLIC: int __get_latest_timestamp_info __P((const DB_LOG_VRFY_INFO *,
1612 * PUBLIC: DB_LSN, VRFY_TIMESTAMP_INFO **));
1613 */
__get_latest_timestamp_info(lvinfo,lsn,tsinfopp)1614 int __get_latest_timestamp_info(lvinfo, lsn, tsinfopp)
1615 const DB_LOG_VRFY_INFO *lvinfo;
1616 DB_LSN lsn;
1617 VRFY_TIMESTAMP_INFO **tsinfopp;
1618 {
1619 int ret, tret;
1620 DBT key, data;
1621 VRFY_TIMESTAMP_INFO *tsinfo;
1622 DBC *csr;
1623
1624 csr = NULL;
1625 ret = tret = 0;
1626 memset(&key, 0, sizeof(DBT));
1627 memset(&data, 0, sizeof(DBT));
1628
1629 key.data = &lsn;
1630 key.size = sizeof(lsn);
1631 BDBOP(__db_cursor(lvinfo->lsntime, lvinfo->ip, NULL, &csr, 0));
1632
1633 BDBOP(__dbc_get(csr, &key, &data, DB_SET));
1634 BDBOP(__dbc_get(csr, &key, &data, DB_PREV));
1635
1636 if ((ret = __os_malloc(lvinfo->dbenv->env, sizeof(VRFY_TIMESTAMP_INFO),
1637 &tsinfo)) != 0)
1638 goto err;
1639
1640 memcpy(tsinfo, data.data, sizeof(VRFY_TIMESTAMP_INFO));
1641 *tsinfopp = tsinfo;
1642
1643 err:
1644 if (ret != 0 && ret != DB_NOTFOUND)
1645 __db_err(lvinfo->dbenv->env,
1646 ret, "__get_latest_timestamp_info");
1647 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1648 ret = tret;
1649 return (ret);
1650 }
1651
1652 /*
1653 * PUBLIC: int __put_timestamp_info __P((const DB_LOG_VRFY_INFO *,
1654 * PUBLIC: const VRFY_TIMESTAMP_INFO *));
1655 */
__put_timestamp_info(lvinfo,tsinfo)1656 int __put_timestamp_info (lvinfo, tsinfo)
1657 const DB_LOG_VRFY_INFO *lvinfo;
1658 const VRFY_TIMESTAMP_INFO *tsinfo;
1659 {
1660 int ret;
1661 DBT key, data;
1662
1663 memset(&key, 0, sizeof(DBT));
1664 memset(&data, 0, sizeof(DBT));
1665 key.data = (void *)&(tsinfo->lsn);
1666 key.size = sizeof(DB_LSN);
1667 data.data = (void *)tsinfo;
1668 data.size = sizeof(VRFY_TIMESTAMP_INFO);
1669 BDBOP2(lvinfo->dbenv, __db_put(lvinfo->lsntime, lvinfo->ip, NULL,
1670 &key, &data, 0), "__put_timestamp_info");
1671
1672 return (0);
1673 }
1674
1675 static int
__lv_txnrgns_lsn_cmp(db,d1,d2,locp)1676 __lv_txnrgns_lsn_cmp (db, d1, d2, locp)
1677 DB *db;
1678 const DBT *d1, *d2;
1679 size_t *locp;
1680 {
1681 struct __lv_txnrange r1, r2;
1682
1683 COMPQUIET(locp, NULL);
1684
1685 DB_ASSERT(db->env, d1->size == sizeof(r1));
1686 DB_ASSERT(db->env, d2->size == sizeof(r2));
1687 memcpy(&r1, d1->data, d1->size);
1688 memcpy(&r2, d2->data, d2->size);
1689
1690 COMPQUIET(db, NULL);
1691 return (LOG_COMPARE(&(r1.end), &(r2.end)));
1692 }
1693
1694 /*
1695 * __find_lsnrg_by_timerg --
1696 * Find the lsn closed interval [beginlsn, endlsn] so that the
1697 * corresponding timestamp interval fully contains interval [begin, end].
1698 * PUBLIC: int __find_lsnrg_by_timerg __P((DB_LOG_VRFY_INFO *,
1699 * PUBLIC: time_t, time_t, DB_LSN *, DB_LSN *));
1700 */
1701 int
__find_lsnrg_by_timerg(lvinfo,begin,end,startlsn,endlsn)1702 __find_lsnrg_by_timerg(lvinfo, begin, end, startlsn, endlsn)
1703 DB_LOG_VRFY_INFO *lvinfo;
1704 time_t begin, end;
1705 DB_LSN *startlsn, *endlsn;
1706 {
1707 int ret, tret;
1708 DBC *csr;
1709 struct __lv_timestamp_info *t1, *t2;
1710 DBT key, data;
1711
1712 ret = tret = 0;
1713 csr = NULL;
1714 memset(&key, 0, sizeof(DBT));
1715 memset(&data, 0, sizeof(DBT));
1716
1717 BDBOP(__db_cursor(lvinfo->timelsn, lvinfo->ip, NULL, &csr, 0));
1718
1719 /*
1720 * We want a lsn range that completely contains [begin, end], so
1721 * try move 1 record prev when getting the startlsn.
1722 */
1723 key.data = &begin;
1724 key.size = sizeof(begin);
1725 BDBOP(__dbc_get(csr, &key, &data, DB_SET_RANGE));
1726 if ((ret = __dbc_get(csr, &key, &data, DB_PREV)) != 0 &&
1727 ret != DB_NOTFOUND)
1728 goto err;
1729 if (ret == DB_NOTFOUND)/* begin is smaller than the smallest key. */
1730 startlsn->file = startlsn->offset = 0;/* beginning. */
1731 else {
1732 t1 = (struct __lv_timestamp_info *)data.data;
1733 *startlsn = t1->lsn;
1734 }
1735
1736 /*
1737 * Move to the last key/data pair of the duplicate set to get the
1738 * biggest lsn having end as timestamp.
1739 */
1740 key.data = &end;
1741 key.size = sizeof(end);
1742 if ((ret = __dbc_get(csr, &key, &data, DB_SET_RANGE)) != 0 &&
1743 ret != DB_NOTFOUND)
1744 goto err;
1745 if (ret == DB_NOTFOUND) {
1746 endlsn->file = endlsn->offset = (u_int32_t)-1;/* Biggest lsn. */
1747 ret = 0;
1748 goto err; /* We are done. */
1749 }
1750
1751 /*
1752 * Go to the biggest lsn of the dup set, if the key is the last one,
1753 * go to the last one.
1754 */
1755 if ((ret = __dbc_get(csr, &key, &data, DB_NEXT_NODUP)) != 0 &&
1756 ret != DB_NOTFOUND)
1757 goto err;
1758
1759 if (ret == DB_NOTFOUND)
1760 BDBOP(__dbc_get(csr, &key, &data, DB_LAST));
1761 else
1762 BDBOP(__dbc_get(csr, &key, &data, DB_PREV));
1763
1764 t2 = (struct __lv_timestamp_info *)data.data;
1765 *endlsn = t2->lsn;
1766 err:
1767 if (ret == DB_NOTFOUND)
1768 ret = 0;
1769 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1770 ret = tret;
1771 return (ret);
1772 }
1773
1774 /*
1775 * PUBLIC: int __add_txnrange __P((DB_LOG_VRFY_INFO *, u_int32_t,
1776 * PUBLIC: DB_LSN, int32_t, int));
1777 */
__add_txnrange(lvinfo,txnid,lsn,when,ishead)1778 int __add_txnrange (lvinfo, txnid, lsn, when, ishead)
1779 DB_LOG_VRFY_INFO *lvinfo;
1780 u_int32_t txnid;
1781 DB_LSN lsn;
1782 int32_t when;
1783 int ishead; /* Whether it's the 1st log of the txn. */
1784 {
1785 int ret, tret;
1786 DBC *csr;
1787 struct __lv_txnrange tr, *ptr;
1788 DBT key, data;
1789
1790 csr = NULL;
1791 ret = 0;
1792 ptr = NULL;
1793 memset(&key, 0, sizeof(DBT));
1794 memset(&data, 0, sizeof(DBT));
1795 memset(&tr, 0, sizeof(tr));
1796
1797 key.data = &txnid;
1798 key.size = sizeof(txnid);
1799 tr.txnid = txnid;
1800 BDBOP(__db_cursor(lvinfo->txnrngs, lvinfo->ip, NULL, &csr, 0));
1801 /*
1802 * Note that we will backward play the logs to gather such information.
1803 */
1804 if (!ishead) {
1805 tr.end = lsn;
1806 tr.when_commit = when;
1807 data.data = &tr;
1808 data.size = sizeof(tr);
1809 BDBOP(__dbc_put(csr, &key, &data, DB_KEYFIRST));
1810 } else {
1811 /*
1812 * Dup data sorted by lsn, and we are backward playing logs,
1813 * so the 1st record should be the one we want.
1814 */
1815 BDBOP(__dbc_get(csr, &key, &data, DB_SET));
1816 ptr = (struct __lv_txnrange *)data.data;
1817 DB_ASSERT(lvinfo->dbenv->env, IS_ZERO_LSN(ptr->begin));
1818 ptr->begin = lsn;
1819 BDBOP(__dbc_put(csr, &key, &data, DB_CURRENT));
1820 }
1821
1822 err:
1823 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1824 ret = tret;
1825 return (ret);
1826 }
1827
1828 /*
1829 * __get_aborttxn --
1830 * If lsn is the last log of an aborted txn T, T's txnid is
1831 * returned via the log verify handle.
1832 *
1833 * PUBLIC: int __get_aborttxn __P((DB_LOG_VRFY_INFO *, DB_LSN));
1834 */
1835 int
__get_aborttxn(lvinfo,lsn)1836 __get_aborttxn(lvinfo, lsn)
1837 DB_LOG_VRFY_INFO *lvinfo;
1838 DB_LSN lsn;
1839 {
1840 int ret, tret;
1841 u_int32_t txnid;
1842 DBC *csr;
1843 DBT key, data;
1844
1845 csr = NULL;
1846 txnid = 0;
1847 ret = tret = 0;
1848 memset(&key, 0, sizeof(DBT));
1849 memset(&data, 0, sizeof(DBT));
1850
1851 key.data = &lsn;
1852 key.size = sizeof(lsn);
1853 BDBOP(__db_cursor(lvinfo->txnaborts, lvinfo->ip, NULL, &csr, 0));
1854 BDBOP(__dbc_get(csr, &key, &data, DB_SET));
1855 memcpy(&txnid, data.data, data.size);
1856 /*
1857 * The lsn is the last op of an aborted txn, call __on_txnabort
1858 * before processing next log record.
1859 */
1860 lvinfo->aborted_txnid = txnid;
1861 lvinfo->aborted_txnlsn = lsn;
1862
1863 err:
1864 /* It's OK if can't find it. */
1865 if (ret == DB_NOTFOUND)
1866 ret = 0;
1867 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1868 ret = tret;
1869 return (ret);
1870 }
1871
1872 /*
1873 * __txn_started --
1874 * Whether txnid is started before lsn and ended after lsn.
1875 *
1876 * PUBLIC: int __txn_started __P((DB_LOG_VRFY_INFO *,
1877 * PUBLIC: DB_LSN, u_int32_t, int *));
1878 */
1879 int
__txn_started(lvinfo,lsn,txnid,res)1880 __txn_started(lvinfo, lsn, txnid, res)
1881 DB_LOG_VRFY_INFO *lvinfo;
1882 DB_LSN lsn;
1883 u_int32_t txnid;
1884 int *res;
1885 {
1886 int ret, tret;
1887 DBC *csr;
1888 DBT key, data;
1889 struct __lv_txnrange *ptr, tr;
1890
1891 ret = *res = 0;
1892 csr = NULL;
1893 memset(&tr, 0, sizeof(tr));
1894 memset(&key, 0, sizeof(DBT));
1895 memset(&data, 0, sizeof(DBT));
1896 key.data = &txnid;
1897 key.size = sizeof(txnid);
1898
1899 BDBOP(__db_cursor(lvinfo->txnrngs, lvinfo->ip, NULL, &csr, 0));
1900 BDBOP(__dbc_get(csr, &key, &data, DB_SET));
1901 for (;ret == 0; ret = __dbc_get(csr, &key, &data, DB_NEXT_DUP)) {
1902 ptr = (struct __lv_txnrange *)data.data;
1903 if (LOG_COMPARE(&lsn, &(ptr->begin)) > 0 &&
1904 LOG_COMPARE(&lsn, &(ptr->end)) <= 0) {
1905 *res = 1;
1906 break;
1907 }
1908 }
1909 err:
1910 if (ret == DB_NOTFOUND)
1911 ret = 0;/* It's OK if can't find it. */
1912 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
1913 ret = tret;
1914 return (ret);
1915 }
1916
1917 /*
1918 * PUBLIC: int __set_logvrfy_dbfuid __P((DB_LOG_VRFY_INFO *));
1919 */
1920 int
__set_logvrfy_dbfuid(lvinfo)1921 __set_logvrfy_dbfuid(lvinfo)
1922 DB_LOG_VRFY_INFO *lvinfo;
1923 {
1924 int ret;
1925 const char *p;
1926 DBT key, data;
1927 size_t buflen;
1928
1929 p = NULL;
1930 memset(&key, 0, sizeof(DBT));
1931 memset(&data, 0, sizeof(DBT));
1932
1933 /* So far we only support verifying a specific db file. */
1934 p = lvinfo->lv_config->dbfile;
1935 buflen = sizeof(char) * (strlen(p) + 1);
1936 key.data = (char *)p;
1937 key.size = (u_int32_t)buflen;
1938
1939 BDBOP2(lvinfo->dbenv, __db_get(lvinfo->fnameuid, lvinfo->ip, NULL,
1940 &key, &data, 0), "__set_logvrfy_dbfuid");
1941
1942 memcpy(lvinfo->target_dbid, data.data, DB_FILE_ID_LEN);
1943
1944 return (ret);
1945 }
1946
1947 /*
1948 * __add_page_to_txn --
1949 * Try adding a page to a txn, result brings back if really added(0/1)
1950 * or if there is an access violation(-1).
1951 * PUBLIC: int __add_page_to_txn __P((DB_LOG_VRFY_INFO *,
1952 * PUBLIC: int32_t, db_pgno_t, u_int32_t, u_int32_t *, int *));
1953 */
1954 int
__add_page_to_txn(lvinfo,dbregid,pgno,txnid,otxn,result)1955 __add_page_to_txn (lvinfo, dbregid, pgno, txnid, otxn, result)
1956 DB_LOG_VRFY_INFO *lvinfo;
1957 int32_t dbregid;
1958 db_pgno_t pgno;
1959 u_int32_t txnid, *otxn;
1960 int *result;
1961 {
1962 int ret;
1963 u_int8_t *buf;
1964 DBT key, data;
1965 size_t buflen;
1966 u_int32_t txnid2;
1967 VRFY_FILELIFE *pff;
1968
1969 if (txnid < TXN_MINIMUM) {
1970 *result = 0;
1971 return (0);
1972 }
1973 buf = NULL;
1974 ret = 0;
1975 txnid2 = 0;
1976 pff = NULL;
1977 buflen = sizeof(u_int8_t) * DB_FILE_ID_LEN + sizeof(db_pgno_t);
1978 BDBOP(__os_malloc(lvinfo->dbenv->env, buflen, &buf));
1979 memset(buf, 0, buflen);
1980 memset(&key, 0, sizeof(DBT));
1981 memset(&data, 0, sizeof(DBT));
1982
1983 /*
1984 * We use the file uid as key because a single db file can have
1985 * multiple dbregid at the same time, and we may neglect the fact
1986 * that the same db file is being updated by multiple txns if we use
1987 * dbregid as key.
1988 */
1989 key.data = &dbregid;
1990 key.size = sizeof(dbregid);
1991 if ((ret = __db_get(lvinfo->dbregids, lvinfo->ip, NULL,
1992 &key, &data, 0)) != 0) {
1993 if (ret == DB_NOTFOUND) {
1994 if (F_ISSET(lvinfo, DB_LOG_VERIFY_PARTIAL)) {
1995 ret = 0;
1996 goto out;
1997 } else
1998 F_SET(lvinfo, DB_LOG_VERIFY_INTERR);
1999 }
2000 goto err;
2001 }
2002 pff = (VRFY_FILELIFE *)data.data;
2003 memcpy(buf, pff->fileid, DB_FILE_ID_LEN);
2004 memcpy(buf + DB_FILE_ID_LEN, (u_int8_t *)&pgno, sizeof(pgno));
2005 memset(&key, 0, sizeof(DBT));
2006 memset(&data, 0, sizeof(DBT));
2007 key.data = buf;
2008 key.size = (u_int32_t)buflen;
2009 if ((ret = __db_get(lvinfo->pgtxn, lvinfo->ip, NULL,
2010 &key, &data, 0)) != 0) {
2011 if (ret == DB_NOTFOUND) {
2012 data.data = &txnid;
2013 data.size = sizeof(txnid);
2014 BDBOP(__db_put(lvinfo->pgtxn, lvinfo->ip, NULL, &key,
2015 &data, 0));
2016 *result = 1;
2017 ret = 0;/* This is not an error. */
2018 }
2019 goto err;
2020 }
2021 DB_ASSERT(lvinfo->dbenv->env, data.size == sizeof(txnid2));
2022 memcpy(&txnid2, data.data, data.size);
2023 if (txnid == txnid2)/* The same txn already has the page. */
2024 *result = 0;
2025 else {/* Txn txnid is updating pages still held by txnid2. */
2026 *result = -1;
2027 *otxn = txnid2;
2028 }
2029 out:
2030 /* result is set to -1 on violation, 0 if already has it, 1 if added. */
2031 err:
2032 if (buf != NULL)
2033 __os_free(lvinfo->dbenv->env, buf);
2034 return (ret);
2035 }
2036
2037 /*
2038 * PUBLIC: int __del_txn_pages __P((DB_LOG_VRFY_INFO *, u_int32_t));
2039 */
2040 int
__del_txn_pages(lvinfo,txnid)2041 __del_txn_pages(lvinfo, txnid)
2042 DB_LOG_VRFY_INFO *lvinfo;
2043 u_int32_t txnid;
2044 {
2045 int ret;
2046 DBT key;
2047
2048 ret = 0;
2049 memset(&key, 0, sizeof(DBT));
2050 key.data = &txnid;
2051 key.size = sizeof(txnid);
2052
2053 BDBOP(__db_del(lvinfo->txnpg, lvinfo->ip, NULL, &key, 0));
2054
2055 err:
2056 return (ret);
2057 }
2058
2059 /*
2060 * __is_ancestor_txn --
2061 * Tells via res if ptxnid is txnid's parent txn at the moment of lsn.
2062 *
2063 * PUBLIC: int __is_ancestor_txn __P((DB_LOG_VRFY_INFO *,
2064 * PUBLIC: u_int32_t, u_int32_t, DB_LSN, int *));
2065 */
2066 int
__is_ancestor_txn(lvinfo,ptxnid,txnid,lsn,res)2067 __is_ancestor_txn (lvinfo, ptxnid, txnid, lsn, res)
2068 DB_LOG_VRFY_INFO *lvinfo;
2069 u_int32_t ptxnid, txnid;
2070 DB_LSN lsn;
2071 int *res;
2072 {
2073 u_int32_t ptid;
2074 int ret, tret;
2075 DBC *csr;
2076 DB *pdb;
2077 DBT key, data;
2078 struct __lv_txnrange tr;
2079
2080 ret = 0;
2081 ptid = txnid;
2082 csr = NULL;
2083 pdb = lvinfo->txnrngs;
2084 memset(&key, 0, sizeof(DBT));
2085 memset(&data, 0, sizeof(DBT));
2086 *res = 0;
2087 BDBOP(__db_cursor(pdb, lvinfo->ip, NULL, &csr, 0));
2088
2089 /* See if ptxnid is an ancestor of txnid. */
2090 do {
2091 key.data = &ptid;
2092 key.size = sizeof(ptid);
2093 BDBOP(__dbc_get(csr, &key, &data, DB_SET));
2094 /* A txnid maybe reused, we want the range having lsn in it. */
2095 for (;ret == 0;
2096 ret = __dbc_get(csr, &key, &data, DB_NEXT_DUP)) {
2097 DB_ASSERT(pdb->env, sizeof(tr) == data.size);
2098 memcpy(&tr, data.data, data.size);
2099 if (tr.ptxnid > 0 &&
2100 LOG_COMPARE(&lsn, &(tr.begin)) >= 0 &&
2101 LOG_COMPARE(&lsn, &(tr.end)) <= 0)
2102 break;
2103 }
2104
2105 if (tr.ptxnid == ptxnid) {
2106 *res = 1;
2107 goto out;
2108 } else
2109 ptid = tr.ptxnid;
2110
2111 } while (ptid != 0);
2112 out:
2113
2114 err:
2115 if (ret == DB_NOTFOUND)
2116 ret = 0;
2117 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
2118 ret = tret;
2119 return (ret);
2120 }
2121
2122 /*
2123 * PUBLIC: int __return_txn_pages __P((DB_LOG_VRFY_INFO *,
2124 * PUBLIC: u_int32_t, u_int32_t));
2125 */
__return_txn_pages(lvh,ctxn,ptxn)2126 int __return_txn_pages(lvh, ctxn, ptxn)
2127 DB_LOG_VRFY_INFO *lvh;
2128 u_int32_t ctxn, ptxn;
2129 {
2130 int ret, tret;
2131 DBC *csr;
2132 DB *pdb, *sdb;
2133 DBT key, key2, data, data2;
2134 char buf[DB_FILE_ID_LEN + sizeof(db_pgno_t)];
2135
2136 ret = tret = 0;
2137 csr = NULL;
2138 sdb = lvh->txnpg;
2139 pdb = lvh->pgtxn;
2140 memset(&key, 0, sizeof(DBT));
2141 memset(&key2, 0, sizeof(DBT));
2142 memset(&data, 0, sizeof(DBT));
2143 memset(&data2, 0, sizeof(DBT));
2144
2145 BDBOP(__db_cursor(sdb, lvh->ip, NULL, &csr, 0));
2146 key.data = &ctxn;
2147 key.size = sizeof(ctxn);
2148 key2.data = &ptxn;
2149 key2.size = sizeof(ptxn);
2150 data2.data = buf;
2151 data2.ulen = DB_FILE_ID_LEN + sizeof(db_pgno_t);
2152 data2.flags = DB_DBT_USERMEM;
2153
2154 for (ret = __dbc_pget(csr, &key, &data2, &data, DB_SET); ret == 0;
2155 ret = __dbc_pget(csr, &key, &data2, &data, DB_NEXT_DUP))
2156 BDBOP(__db_put(pdb, lvh->ip, NULL, &data2, &key2, 0));
2157 if ((ret = __del_txn_pages(lvh, ctxn)) != 0 && ret != DB_NOTFOUND)
2158 goto err;
2159 err:
2160 if (csr != NULL && (tret = __dbc_close(csr)) != 0 && ret == 0)
2161 ret = tret;
2162 return (ret);
2163 }
2164
2165 #define ADD_ITEM(lvh, logtype) ((lvh)->logtype_names[(logtype)] = (#logtype))
2166 static void
__lv_setup_logtype_names(lvinfo)2167 __lv_setup_logtype_names(lvinfo)
2168 DB_LOG_VRFY_INFO *lvinfo;
2169 {
2170 ADD_ITEM(lvinfo, DB___bam_irep);
2171 ADD_ITEM(lvinfo, DB___bam_split_42);
2172 ADD_ITEM(lvinfo, DB___bam_split);
2173 ADD_ITEM(lvinfo, DB___bam_rsplit);
2174 ADD_ITEM(lvinfo, DB___bam_adj);
2175 ADD_ITEM(lvinfo, DB___bam_cadjust);
2176 ADD_ITEM(lvinfo, DB___bam_cdel);
2177 ADD_ITEM(lvinfo, DB___bam_repl);
2178 ADD_ITEM(lvinfo, DB___bam_root);
2179 ADD_ITEM(lvinfo, DB___bam_curadj);
2180 ADD_ITEM(lvinfo, DB___bam_rcuradj);
2181 ADD_ITEM(lvinfo, DB___bam_relink_43);
2182 ADD_ITEM(lvinfo, DB___bam_merge_44);
2183 ADD_ITEM(lvinfo, DB___crdel_metasub);
2184 ADD_ITEM(lvinfo, DB___crdel_inmem_create);
2185 ADD_ITEM(lvinfo, DB___crdel_inmem_rename);
2186 ADD_ITEM(lvinfo, DB___crdel_inmem_remove);
2187 ADD_ITEM(lvinfo, DB___dbreg_register);
2188 ADD_ITEM(lvinfo, DB___db_addrem);
2189 ADD_ITEM(lvinfo, DB___db_big);
2190 ADD_ITEM(lvinfo, DB___db_ovref);
2191 ADD_ITEM(lvinfo, DB___db_relink_42);
2192 ADD_ITEM(lvinfo, DB___db_debug);
2193 ADD_ITEM(lvinfo, DB___db_noop);
2194 ADD_ITEM(lvinfo, DB___db_pg_alloc_42);
2195 ADD_ITEM(lvinfo, DB___db_pg_alloc);
2196 ADD_ITEM(lvinfo, DB___db_pg_free_42);
2197 ADD_ITEM(lvinfo, DB___db_pg_free);
2198 ADD_ITEM(lvinfo, DB___db_cksum);
2199 ADD_ITEM(lvinfo, DB___db_pg_freedata_42);
2200 ADD_ITEM(lvinfo, DB___db_pg_freedata);
2201 ADD_ITEM(lvinfo, DB___db_pg_init);
2202 ADD_ITEM(lvinfo, DB___db_pg_sort_44);
2203 ADD_ITEM(lvinfo, DB___db_pg_trunc);
2204 ADD_ITEM(lvinfo, DB___db_realloc);
2205 ADD_ITEM(lvinfo, DB___db_relink);
2206 ADD_ITEM(lvinfo, DB___db_merge);
2207 ADD_ITEM(lvinfo, DB___db_pgno);
2208 #ifdef HAVE_HASH
2209 ADD_ITEM(lvinfo, DB___ham_insdel);
2210 ADD_ITEM(lvinfo, DB___ham_newpage);
2211 ADD_ITEM(lvinfo, DB___ham_splitdata);
2212 ADD_ITEM(lvinfo, DB___ham_replace);
2213 ADD_ITEM(lvinfo, DB___ham_copypage);
2214 ADD_ITEM(lvinfo, DB___ham_metagroup_42);
2215 ADD_ITEM(lvinfo, DB___ham_metagroup);
2216 ADD_ITEM(lvinfo, DB___ham_groupalloc_42);
2217 ADD_ITEM(lvinfo, DB___ham_groupalloc);
2218 ADD_ITEM(lvinfo, DB___ham_changeslot);
2219 ADD_ITEM(lvinfo, DB___ham_contract);
2220 ADD_ITEM(lvinfo, DB___ham_curadj);
2221 ADD_ITEM(lvinfo, DB___ham_chgpg);
2222 #endif
2223 #ifdef HAVE_QUEUE
2224 ADD_ITEM(lvinfo, DB___qam_incfirst);
2225 ADD_ITEM(lvinfo, DB___qam_mvptr);
2226 ADD_ITEM(lvinfo, DB___qam_del);
2227 ADD_ITEM(lvinfo, DB___qam_add);
2228 ADD_ITEM(lvinfo, DB___qam_delext);
2229 #endif
2230 ADD_ITEM(lvinfo, DB___txn_regop_42);
2231 ADD_ITEM(lvinfo, DB___txn_regop);
2232 ADD_ITEM(lvinfo, DB___txn_ckp_42);
2233 ADD_ITEM(lvinfo, DB___txn_ckp);
2234 ADD_ITEM(lvinfo, DB___txn_child);
2235 ADD_ITEM(lvinfo, DB___txn_xa_regop_42);
2236 ADD_ITEM(lvinfo, DB___txn_prepare);
2237 ADD_ITEM(lvinfo, DB___txn_recycle);
2238 ADD_ITEM(lvinfo, DB___fop_create_42);
2239 ADD_ITEM(lvinfo, DB___fop_create);
2240 ADD_ITEM(lvinfo, DB___fop_remove);
2241 ADD_ITEM(lvinfo, DB___fop_write_42);
2242 ADD_ITEM(lvinfo, DB___fop_write);
2243 ADD_ITEM(lvinfo, DB___fop_rename_42);
2244 ADD_ITEM(lvinfo, DB___fop_rename_noundo_46);
2245 ADD_ITEM(lvinfo, DB___fop_rename);
2246 ADD_ITEM(lvinfo, DB___fop_rename_noundo);
2247 ADD_ITEM(lvinfo, DB___fop_file_remove);
2248 }
2249