~ubuntu-branches/ubuntu/maverick/evolution-data-server/maverick-proposed

« back to all changes in this revision

Viewing changes to libdb/log/log.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-05-17 17:02:06 UTC
  • mfrom: (1.1.79 upstream) (1.6.12 experimental)
  • Revision ID: james.westby@ubuntu.com-20100517170206-4ufr52vwrhh26yh0
Tags: 2.30.1-1ubuntu1
* Merge from debian experimental. Remaining change:
  (LP: #42199, #229669, #173703, #360344, #508494)
  + debian/control:
    - add Vcs-Bzr tag
    - don't use libgnome
    - Use Breaks instead of Conflicts against evolution 2.25 and earlier.
  + debian/evolution-data-server.install,
    debian/patches/45_libcamel_providers_version.patch:
    - use the upstream versioning, not a Debian-specific one 
  + debian/libedata-book1.2-dev.install, debian/libebackend-1.2-dev.install,
    debian/libcamel1.2-dev.install, debian/libedataserverui1.2-dev.install:
    - install html documentation
  + debian/rules:
    - don't build documentation it's shipped with the tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*-
2
 
 * See the file LICENSE for redistribution information.
3
 
 *
4
 
 * Copyright (c) 1996-2002
5
 
 *      Sleepycat Software.  All rights reserved.
6
 
 */
7
 
#include "db_config.h"
8
 
 
9
 
#ifndef lint
10
 
static const char revid[] = "$Id$";
11
 
#endif /* not lint */
12
 
 
13
 
#ifndef NO_SYSTEM_INCLUDES
14
 
#include <sys/types.h>
15
 
 
16
 
#include <ctype.h>
17
 
#include <stdlib.h>
18
 
#include <string.h>
19
 
#include <unistd.h>
20
 
#endif
21
 
 
22
 
#include "db_int.h"
23
 
#include "dbinc/crypto.h"
24
 
#include "dbinc/hmac.h"
25
 
#include "dbinc/log.h"
26
 
#include "dbinc/txn.h"
27
 
 
28
 
static int __log_init __P((DB_ENV *, DB_LOG *));
29
 
static int __log_recover __P((DB_LOG *));
30
 
static size_t __log_region_size __P((DB_ENV *));
31
 
static int __log_zero __P((DB_ENV *, DB_LSN *, DB_LSN *));
32
 
 
33
 
/*
34
 
 * __log_open --
35
 
 *      Internal version of log_open: only called from DB_ENV->open.
36
 
 *
37
 
 * PUBLIC: int __log_open __P((DB_ENV *));
38
 
 */
39
 
int
40
 
__log_open(dbenv)
41
 
        DB_ENV *dbenv;
42
 
{
43
 
        DB_LOG *dblp;
44
 
        LOG *lp;
45
 
        int ret;
46
 
 
47
 
        /* Create/initialize the DB_LOG structure. */
48
 
        if ((ret = __os_calloc(dbenv, 1, sizeof(DB_LOG), &dblp)) != 0)
49
 
                return (ret);
50
 
        dblp->dbenv = dbenv;
51
 
 
52
 
        /* Join/create the log region. */
53
 
        dblp->reginfo.type = REGION_TYPE_LOG;
54
 
        dblp->reginfo.id = INVALID_REGION_ID;
55
 
        dblp->reginfo.mode = dbenv->db_mode;
56
 
        dblp->reginfo.flags = REGION_JOIN_OK;
57
 
        if (F_ISSET(dbenv, DB_ENV_CREATE))
58
 
                F_SET(&dblp->reginfo, REGION_CREATE_OK);
59
 
        if ((ret = __db_r_attach(
60
 
            dbenv, &dblp->reginfo, __log_region_size(dbenv))) != 0)
61
 
                goto err;
62
 
 
63
 
        /* If we created the region, initialize it. */
64
 
        if (F_ISSET(&dblp->reginfo, REGION_CREATE))
65
 
                if ((ret = __log_init(dbenv, dblp)) != 0)
66
 
                        goto err;
67
 
 
68
 
        /* Set the local addresses. */
69
 
        lp = dblp->reginfo.primary =
70
 
            R_ADDR(&dblp->reginfo, dblp->reginfo.rp->primary);
71
 
 
72
 
        /*
73
 
         * If the region is threaded, then we have to lock both the handles
74
 
         * and the region, and we need to allocate a mutex for that purpose.
75
 
         */
76
 
        if (F_ISSET(dbenv, DB_ENV_THREAD) &&
77
 
            (ret = __db_mutex_setup(dbenv, &dblp->reginfo, &dblp->mutexp,
78
 
            MUTEX_ALLOC | MUTEX_NO_RLOCK)) != 0)
79
 
                goto err;
80
 
 
81
 
        /* Initialize the rest of the structure. */
82
 
        dblp->bufp = R_ADDR(&dblp->reginfo, lp->buffer_off);
83
 
 
84
 
        /*
85
 
         * Set the handle -- we may be about to run recovery, which allocates
86
 
         * log cursors.  Log cursors require logging be already configured,
87
 
         * and the handle being set is what demonstrates that.
88
 
         *
89
 
         * If we created the region, run recovery.  If that fails, make sure
90
 
         * we reset the log handle before cleaning up, otherwise we will try
91
 
         * and clean up again in the mainline DB_ENV initialization code.
92
 
         */
93
 
        dbenv->lg_handle = dblp;
94
 
 
95
 
        if (F_ISSET(&dblp->reginfo, REGION_CREATE)) {
96
 
                if ((ret = __log_recover(dblp)) != 0) {
97
 
                        dbenv->lg_handle = NULL;
98
 
                        goto err;
99
 
                }
100
 
 
101
 
                /*
102
 
                 * We first take the log file size from the environment, if
103
 
                 * specified.  If that wasn't set, recovery may have set it
104
 
                 * from the persistent information in a log file header.  If
105
 
                 * that didn't set it either, we default.
106
 
                 */
107
 
                if (lp->log_size == 0)
108
 
                        lp->log_size = lp->log_nsize = LG_MAX_DEFAULT;
109
 
        } else {
110
 
                /*
111
 
                 * A process joining the region may have reset the log file
112
 
                 * size, too.  If so, it only affects the next log file we
113
 
                 * create.
114
 
                 */
115
 
                 if (dbenv->lg_size != 0)
116
 
                        lp->log_nsize = dbenv->lg_size;
117
 
        }
118
 
 
119
 
        R_UNLOCK(dbenv, &dblp->reginfo);
120
 
        return (0);
121
 
 
122
 
err:    if (dblp->reginfo.addr != NULL) {
123
 
                if (F_ISSET(&dblp->reginfo, REGION_CREATE))
124
 
                        ret = __db_panic(dbenv, ret);
125
 
                R_UNLOCK(dbenv, &dblp->reginfo);
126
 
                (void)__db_r_detach(dbenv, &dblp->reginfo, 0);
127
 
        }
128
 
 
129
 
        if (dblp->mutexp != NULL)
130
 
                __db_mutex_free(dbenv, &dblp->reginfo, dblp->mutexp);
131
 
 
132
 
        __os_free(dbenv, dblp);
133
 
 
134
 
        return (ret);
135
 
}
136
 
 
137
 
/*
138
 
 * __log_init --
139
 
 *      Initialize a log region in shared memory.
140
 
 */
141
 
static int
142
 
__log_init(dbenv, dblp)
143
 
        DB_ENV *dbenv;
144
 
        DB_LOG *dblp;
145
 
{
146
 
        DB_MUTEX *flush_mutexp;
147
 
        LOG *region;
148
 
        int ret;
149
 
        void *p;
150
 
#ifdef  HAVE_MUTEX_SYSTEM_RESOURCES
151
 
        u_int8_t *addr;
152
 
#endif
153
 
 
154
 
        if ((ret = __db_shalloc(dblp->reginfo.addr,
155
 
            sizeof(*region), 0, &dblp->reginfo.primary)) != 0)
156
 
                goto mem_err;
157
 
        dblp->reginfo.rp->primary =
158
 
            R_OFFSET(&dblp->reginfo, dblp->reginfo.primary);
159
 
        region = dblp->reginfo.primary;
160
 
        memset(region, 0, sizeof(*region));
161
 
 
162
 
        region->fid_max = 0;
163
 
        SH_TAILQ_INIT(&region->fq);
164
 
        region->free_fid_stack = INVALID_ROFF;
165
 
        region->free_fids = region->free_fids_alloced = 0;
166
 
 
167
 
        /* Initialize LOG LSNs. */
168
 
        INIT_LSN(region->lsn);
169
 
        INIT_LSN(region->ready_lsn);
170
 
        INIT_LSN(region->t_lsn);
171
 
 
172
 
        /*
173
 
         * It's possible to be waiting for an LSN of [1][0], if a replication
174
 
         * client gets the first log record out of order.  An LSN of [0][0]
175
 
         * signifies that we're not waiting.
176
 
         */
177
 
        ZERO_LSN(region->waiting_lsn);
178
 
 
179
 
        /*
180
 
         * Log makes note of the fact that it ran into a checkpoint on
181
 
         * startup if it did so, as a recovery optimization.  A zero
182
 
         * LSN signifies that it hasn't found one [yet].
183
 
         */
184
 
        ZERO_LSN(region->cached_ckp_lsn);
185
 
 
186
 
#ifdef  HAVE_MUTEX_SYSTEM_RESOURCES
187
 
        /* Allocate room for the log maintenance info and initialize it. */
188
 
        if ((ret = __db_shalloc(dblp->reginfo.addr,
189
 
            sizeof(REGMAINT) + LG_MAINT_SIZE, 0, &addr)) != 0)
190
 
                goto mem_err;
191
 
        __db_maintinit(&dblp->reginfo, addr, LG_MAINT_SIZE);
192
 
        region->maint_off = R_OFFSET(&dblp->reginfo, addr);
193
 
#endif
194
 
 
195
 
        if ((ret = __db_mutex_setup(dbenv, &dblp->reginfo, &region->fq_mutex,
196
 
            MUTEX_NO_RLOCK)) != 0)
197
 
                return (ret);
198
 
 
199
 
        /*
200
 
         * We must create a place for the flush mutex separately; mutexes have
201
 
         * to be aligned to MUTEX_ALIGN, and the only way to guarantee that is
202
 
         * to make sure they're at the beginning of a shalloc'ed chunk.
203
 
         */
204
 
        if ((ret = __db_shalloc(dblp->reginfo.addr,
205
 
            sizeof(DB_MUTEX), MUTEX_ALIGN, &flush_mutexp)) != 0)
206
 
                goto mem_err;
207
 
        if ((ret = __db_mutex_setup(dbenv, &dblp->reginfo, flush_mutexp,
208
 
            MUTEX_NO_RLOCK)) != 0)
209
 
                return (ret);
210
 
        region->flush_mutex_off = R_OFFSET(&dblp->reginfo, flush_mutexp);
211
 
 
212
 
        /* Initialize the buffer. */
213
 
        if ((ret =
214
 
            __db_shalloc(dblp->reginfo.addr, dbenv->lg_bsize, 0, &p)) != 0) {
215
 
mem_err:        __db_err(dbenv, "Unable to allocate memory for the log buffer");
216
 
                return (ret);
217
 
        }
218
 
        region->buffer_size = dbenv->lg_bsize;
219
 
        region->buffer_off = R_OFFSET(&dblp->reginfo, p);
220
 
        region->log_size = region->log_nsize = dbenv->lg_size;
221
 
 
222
 
        /* Initialize the commit Queue. */
223
 
        SH_TAILQ_INIT(&region->free_commits);
224
 
        SH_TAILQ_INIT(&region->commits);
225
 
        region->ncommit = 0;
226
 
 
227
 
        /*
228
 
         * Fill in the log's persistent header.  Don't fill in the log file
229
 
         * sizes, as they may change at any time and so have to be filled in
230
 
         * as each log file is created.
231
 
         */
232
 
        region->persist.magic = DB_LOGMAGIC;
233
 
        region->persist.version = DB_LOGVERSION;
234
 
        region->persist.mode = (u_int32_t)dbenv->db_mode;
235
 
 
236
 
        return (0);
237
 
}
238
 
 
239
 
/*
240
 
 * __log_recover --
241
 
 *      Recover a log.
242
 
 */
243
 
static int
244
 
__log_recover(dblp)
245
 
        DB_LOG *dblp;
246
 
{
247
 
        DBT dbt;
248
 
        DB_ENV *dbenv;
249
 
        DB_LOGC *logc;
250
 
        DB_LSN lsn;
251
 
        LOG *lp;
252
 
        u_int32_t cnt, rectype;
253
 
        int ret;
254
 
        logfile_validity status;
255
 
 
256
 
        logc = NULL;
257
 
        dbenv = dblp->dbenv;
258
 
        lp = dblp->reginfo.primary;
259
 
 
260
 
        /*
261
 
         * Find a log file.  If none exist, we simply return, leaving
262
 
         * everything initialized to a new log.
263
 
         */
264
 
        if ((ret = __log_find(dblp, 0, &cnt, &status)) != 0)
265
 
                return (ret);
266
 
        if (cnt == 0)
267
 
                return (0);
268
 
 
269
 
        /*
270
 
         * If the last file is an old version, readable or no, start a new
271
 
         * file.  Don't bother finding the end of the last log file;
272
 
         * we assume that it's valid in its entirety, since the user
273
 
         * should have shut down cleanly or run recovery before upgrading.
274
 
         */
275
 
        if (status == DB_LV_OLD_READABLE || status == DB_LV_OLD_UNREADABLE) {
276
 
                lp->lsn.file = lp->s_lsn.file = cnt + 1;
277
 
                lp->lsn.offset = lp->s_lsn.offset = 0;
278
 
                goto skipsearch;
279
 
        }
280
 
        DB_ASSERT(status == DB_LV_NORMAL);
281
 
 
282
 
        /*
283
 
         * We have the last useful log file and we've loaded any persistent
284
 
         * information.  Set the end point of the log past the end of the last
285
 
         * file. Read the last file, looking for the last checkpoint and
286
 
         * the log's end.
287
 
         */
288
 
        lp->lsn.file = cnt + 1;
289
 
        lp->lsn.offset = 0;
290
 
        lsn.file = cnt;
291
 
        lsn.offset = 0;
292
 
 
293
 
        /*
294
 
         * Allocate a cursor and set it to the first record.  This shouldn't
295
 
         * fail, leave error messages on.
296
 
         */
297
 
        if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
298
 
                return (ret);
299
 
        F_SET(logc, DB_LOG_LOCKED);
300
 
        memset(&dbt, 0, sizeof(dbt));
301
 
        if ((ret = logc->get(logc, &lsn, &dbt, DB_SET)) != 0)
302
 
                goto err;
303
 
 
304
 
        /*
305
 
         * Read to the end of the file.  This may fail at some point, so
306
 
         * turn off error messages.
307
 
         */
308
 
        F_SET(logc, DB_LOG_SILENT_ERR);
309
 
        while (logc->get(logc, &lsn, &dbt, DB_NEXT) == 0) {
310
 
                if (dbt.size < sizeof(u_int32_t))
311
 
                        continue;
312
 
                memcpy(&rectype, dbt.data, sizeof(u_int32_t));
313
 
                if (rectype == DB___txn_ckp)
314
 
                        /*
315
 
                         * If we happen to run into a checkpoint, cache its
316
 
                         * LSN so that the transaction system doesn't have
317
 
                         * to walk this log file again looking for it.
318
 
                         */
319
 
                        lp->cached_ckp_lsn = lsn;
320
 
        }
321
 
        F_CLR(logc, DB_LOG_SILENT_ERR);
322
 
 
323
 
        /*
324
 
         * We now know where the end of the log is.  Set the first LSN that
325
 
         * we want to return to an application and the LSN of the last known
326
 
         * record on disk.
327
 
         */
328
 
        lp->lsn = lsn;
329
 
        lp->s_lsn = lsn;
330
 
        lp->lsn.offset += logc->c_len;
331
 
        lp->s_lsn.offset += logc->c_len;
332
 
 
333
 
        /* Set up the current buffer information, too. */
334
 
        lp->len = logc->c_len;
335
 
        lp->b_off = 0;
336
 
        lp->w_off = lp->lsn.offset;
337
 
 
338
 
skipsearch:
339
 
        if (FLD_ISSET(dbenv->verbose, DB_VERB_RECOVERY))
340
 
                __db_err(dbenv,
341
 
                    "Finding last valid log LSN: file: %lu offset %lu",
342
 
                    (u_long)lp->lsn.file, (u_long)lp->lsn.offset);
343
 
 
344
 
err:    if (logc != NULL)
345
 
                (void)logc->close(logc, 0);
346
 
 
347
 
        return (ret);
348
 
}
349
 
 
350
 
/*
351
 
 * __log_find --
352
 
 *      Try to find a log file.  If find_first is set, valp will contain
353
 
 * the number of the first readable log file, else it will contain the number
354
 
 * of the last log file (which may be too old to read).
355
 
 *
356
 
 * PUBLIC: int __log_find __P((DB_LOG *, int, u_int32_t *, logfile_validity *));
357
 
 */
358
 
int
359
 
__log_find(dblp, find_first, valp, statusp)
360
 
        DB_LOG *dblp;
361
 
        int find_first;
362
 
        u_int32_t *valp;
363
 
        logfile_validity *statusp;
364
 
{
365
 
        DB_ENV *dbenv;
366
 
        logfile_validity logval_status, status;
367
 
        u_int32_t clv, logval;
368
 
        int cnt, fcnt, ret;
369
 
        const char *dir;
370
 
        char *c, **names, *p, *q, savech;
371
 
 
372
 
        dbenv = dblp->dbenv;
373
 
        logval_status = status = DB_LV_NONEXISTENT;
374
 
 
375
 
        /* Return a value of 0 as the log file number on failure. */
376
 
        *valp = 0;
377
 
 
378
 
        /* Find the directory name. */
379
 
        if ((ret = __log_name(dblp, 1, &p, NULL, 0)) != 0)
380
 
                return (ret);
381
 
        if ((q = __db_rpath(p)) == NULL) {
382
 
                COMPQUIET(savech, 0);
383
 
                dir = PATH_DOT;
384
 
        } else {
385
 
                savech = *q;
386
 
                *q = '\0';
387
 
                dir = p;
388
 
        }
389
 
 
390
 
        /* Get the list of file names. */
391
 
        ret = __os_dirlist(dbenv, dir, &names, &fcnt);
392
 
 
393
 
        /*
394
 
         * !!!
395
 
         * We overwrote a byte in the string with a nul.  Restore the string
396
 
         * so that the diagnostic checks in the memory allocation code work
397
 
         * and any error messages display the right file name.
398
 
         */
399
 
        if (q != NULL)
400
 
                *q = savech;
401
 
 
402
 
        if (ret != 0) {
403
 
                __db_err(dbenv, "%s: %s", dir, db_strerror(ret));
404
 
                __os_free(dbenv, p);
405
 
                return (ret);
406
 
        }
407
 
 
408
 
        /* Search for a valid log file name. */
409
 
        for (cnt = fcnt, clv = logval = 0; --cnt >= 0;) {
410
 
                if (strncmp(names[cnt], LFPREFIX, sizeof(LFPREFIX) - 1) != 0)
411
 
                        continue;
412
 
 
413
 
                /*
414
 
                 * Names of the form log\.[0-9]* are reserved for DB.  Other
415
 
                 * names sharing LFPREFIX, such as "log.db", are legal.
416
 
                 */
417
 
                for (c = names[cnt] + sizeof(LFPREFIX) - 1; *c != '\0'; c++)
418
 
                        if (!isdigit((int)*c))
419
 
                                break;
420
 
                if (*c != '\0')
421
 
                        continue;
422
 
 
423
 
                /*
424
 
                 * Use atol, not atoi; if an "int" is 16-bits, the largest
425
 
                 * log file name won't fit.
426
 
                 */
427
 
                clv = atol(names[cnt] + (sizeof(LFPREFIX) - 1));
428
 
 
429
 
                /*
430
 
                 * If searching for the first log file, we want to return the
431
 
                 * oldest log file we can read, or, if no readable log files
432
 
                 * exist, the newest log file we can't read (the crossover
433
 
                 * point between the old and new versions of the log file).
434
 
                 *
435
 
                 * If we're searching for the last log file, we want to return
436
 
                 * the newest log file, period.
437
 
                 *
438
 
                 * Readable log files should never preceede unreadable log
439
 
                 * files, that would mean the admin seriously screwed up.
440
 
                 */
441
 
                if (find_first) {
442
 
                        if (logval != 0 &&
443
 
                            status != DB_LV_OLD_UNREADABLE && clv > logval)
444
 
                                continue;
445
 
                } else
446
 
                        if (logval != 0 && clv < logval)
447
 
                                continue;
448
 
 
449
 
                if ((ret = __log_valid(dblp, clv, 1, &status)) != 0) {
450
 
                        __db_err(dbenv, "Invalid log file: %s: %s",
451
 
                            names[cnt], db_strerror(ret));
452
 
                        goto err;
453
 
                }
454
 
                switch (status) {
455
 
                case DB_LV_NONEXISTENT:
456
 
                        /* __log_valid never returns DB_LV_NONEXISTENT. */
457
 
                        DB_ASSERT(0);
458
 
                        break;
459
 
                case DB_LV_INCOMPLETE:
460
 
                        /*
461
 
                         * The last log file may not have been initialized --
462
 
                         * it's possible to create a log file but not write
463
 
                         * anything to it.  If performing recovery (that is,
464
 
                         * if find_first isn't set), ignore the file, it's
465
 
                         * not interesting.  If we're searching for the first
466
 
                         * log record, return the file (assuming we don't find
467
 
                         * something better), as the "real" first log record
468
 
                         * is likely to be in the log buffer, and we want to
469
 
                         * set the file LSN for our return.
470
 
                         */
471
 
                        if (find_first)
472
 
                                goto found;
473
 
                        break;
474
 
                case DB_LV_OLD_UNREADABLE:
475
 
                        /*
476
 
                         * If we're searching for the first log file, then we
477
 
                         * only want this file if we don't yet have a file or
478
 
                         * already have an unreadable file and this one is
479
 
                         * newer than that one.  If we're searching for the
480
 
                         * last log file, we always want this file because we
481
 
                         * wouldn't be here if it wasn't newer than our current
482
 
                         * choice.
483
 
                         */
484
 
                        if (!find_first || logval == 0 ||
485
 
                            (status == DB_LV_OLD_UNREADABLE && clv > logval))
486
 
                                goto found;
487
 
                        break;
488
 
                case DB_LV_NORMAL:
489
 
                case DB_LV_OLD_READABLE:
490
 
found:                  logval = clv;
491
 
                        logval_status = status;
492
 
                        break;
493
 
                }
494
 
        }
495
 
 
496
 
        *valp = logval;
497
 
 
498
 
err:    __os_dirfree(dbenv, names, fcnt);
499
 
        __os_free(dbenv, p);
500
 
        *statusp = logval_status;
501
 
 
502
 
        return (ret);
503
 
}
504
 
 
505
 
/*
506
 
 * log_valid --
507
 
 *      Validate a log file.  Returns an error code in the event of
508
 
 *      a fatal flaw in a the specified log file;  returns success with
509
 
 *      a code indicating the currentness and completeness of the specified
510
 
 *      log file if it is not unexpectedly flawed (that is, if it's perfectly
511
 
 *      normal, if it's zero-length, or if it's an old version).
512
 
 *
513
 
 * PUBLIC: int __log_valid __P((DB_LOG *, u_int32_t, int, logfile_validity *));
514
 
 */
515
 
int
516
 
__log_valid(dblp, number, set_persist, statusp)
517
 
        DB_LOG *dblp;
518
 
        u_int32_t number;
519
 
        int set_persist;
520
 
        logfile_validity *statusp;
521
 
{
522
 
        DB_CIPHER *db_cipher;
523
 
        DB_ENV *dbenv;
524
 
        DB_FH fh;
525
 
        HDR *hdr;
526
 
        LOG *region;
527
 
        LOGP *persist;
528
 
        logfile_validity status;
529
 
        size_t hdrsize, nw, recsize;
530
 
        int is_hmac, need_free, ret;
531
 
        u_int8_t *tmp;
532
 
        char *fname;
533
 
 
534
 
        dbenv = dblp->dbenv;
535
 
        db_cipher = dbenv->crypto_handle;
536
 
        persist = NULL;
537
 
        status = DB_LV_NORMAL;
538
 
 
539
 
        /* Try to open the log file. */
540
 
        if ((ret = __log_name(dblp,
541
 
            number, &fname, &fh, DB_OSO_RDONLY | DB_OSO_SEQ)) != 0) {
542
 
                __os_free(dbenv, fname);
543
 
                return (ret);
544
 
        }
545
 
 
546
 
        need_free = 0;
547
 
        hdrsize = HDR_NORMAL_SZ;
548
 
        is_hmac = 0;
549
 
        recsize = sizeof(LOGP);
550
 
        if (CRYPTO_ON(dbenv)) {
551
 
                hdrsize = HDR_CRYPTO_SZ;
552
 
                recsize = sizeof(LOGP);
553
 
                recsize += db_cipher->adj_size(recsize);
554
 
                is_hmac = 1;
555
 
        }
556
 
        if ((ret = __os_calloc(dbenv, 1, recsize + hdrsize, &tmp)) != 0)
557
 
                return (ret);
558
 
        need_free = 1;
559
 
        hdr = (HDR *)tmp;
560
 
        persist = (LOGP *)(tmp + hdrsize);
561
 
        /* Try to read the header. */
562
 
        if ((ret = __os_read(dbenv, &fh, tmp, recsize + hdrsize, &nw)) != 0 ||
563
 
            nw != recsize + hdrsize) {
564
 
                if (ret == 0)
565
 
                        status = DB_LV_INCOMPLETE;
566
 
                else
567
 
                        /*
568
 
                         * The error was a fatal read error, not just an
569
 
                         * incompletely initialized log file.
570
 
                         */
571
 
                        __db_err(dbenv, "Ignoring log file: %s: %s",
572
 
                            fname, db_strerror(ret));
573
 
 
574
 
                (void)__os_closehandle(dbenv, &fh);
575
 
                goto err;
576
 
        }
577
 
        (void)__os_closehandle(dbenv, &fh);
578
 
 
579
 
        /*
580
 
         * Now we have to validate the persistent record.  We have
581
 
         * several scenarios we have to deal with:
582
 
         *
583
 
         * 1.  User has crypto turned on:
584
 
         *      - They're reading an old, unencrypted log file
585
 
         *        .  We will fail the record size match check below.
586
 
         *      - They're reading a current, unencrypted log file
587
 
         *        .  We will fail the record size match check below.
588
 
         *      - They're reading an old, encrypted log file [NOT YET]
589
 
         *        .  After decryption we'll fail the version check.  [NOT YET]
590
 
         *      - They're reading a current, encrypted log file
591
 
         *        .  We should proceed as usual.
592
 
         * 2.  User has crypto turned off:
593
 
         *      - They're reading an old, unencrypted log file
594
 
         *        .  We will fail the version check.
595
 
         *      - They're reading a current, unencrypted log file
596
 
         *        .  We should proceed as usual.
597
 
         *      - They're reading an old, encrypted log file [NOT YET]
598
 
         *        .  We'll fail the magic number check (it is encrypted).
599
 
         *      - They're reading a current, encrypted log file
600
 
         *        .  We'll fail the magic number check (it is encrypted).
601
 
         */
602
 
        if (CRYPTO_ON(dbenv)) {
603
 
                /*
604
 
                 * If we are trying to decrypt an unencrypted log
605
 
                 * we can only detect that by having an unreasonable
606
 
                 * data length for our persistent data.
607
 
                 */
608
 
                if ((hdr->len - hdrsize) != sizeof(LOGP)) {
609
 
                        __db_err(dbenv, "log record size mismatch");
610
 
                        goto err;
611
 
                }
612
 
                /* Check the checksum and decrypt. */
613
 
                if ((ret = __db_check_chksum(dbenv, db_cipher, &hdr->chksum[0],
614
 
                    (u_int8_t *)persist, hdr->len - hdrsize, is_hmac)) != 0) {
615
 
                        __db_err(dbenv, "log record checksum mismatch");
616
 
                        goto err;
617
 
                }
618
 
                if ((ret = db_cipher->decrypt(dbenv, db_cipher->data,
619
 
                    &hdr->iv[0], (u_int8_t *)persist, hdr->len - hdrsize)) != 0)
620
 
                        goto err;
621
 
        }
622
 
 
623
 
        /* Validate the header. */
624
 
        if (persist->magic != DB_LOGMAGIC) {
625
 
                __db_err(dbenv,
626
 
                    "Ignoring log file: %s: magic number %lx, not %lx",
627
 
                    fname, (u_long)persist->magic, (u_long)DB_LOGMAGIC);
628
 
                ret = EINVAL;
629
 
                goto err;
630
 
        }
631
 
 
632
 
        /*
633
 
         * Set our status code to indicate whether the log file
634
 
         * belongs to an unreadable or readable old version;  leave it
635
 
         * alone if and only if the log file version is the current one.
636
 
         */
637
 
        if (persist->version > DB_LOGVERSION) {
638
 
                /* This is a fatal error--the log file is newer than DB. */
639
 
                __db_err(dbenv,
640
 
                    "Ignoring log file: %s: unsupported log version %lu",
641
 
                    fname, (u_long)persist->version);
642
 
                ret = EINVAL;
643
 
                goto err;
644
 
        } else if (persist->version < DB_LOGOLDVER) {
645
 
                status = DB_LV_OLD_UNREADABLE;
646
 
                /*
647
 
                 * We don't want to set persistent info based on an
648
 
                 * unreadable region, so jump to "err".
649
 
                 */
650
 
                goto err;
651
 
        } else if (persist->version < DB_LOGVERSION)
652
 
                status = DB_LV_OLD_READABLE;
653
 
 
654
 
        /*
655
 
         * Only if we have a current log do we verify the checksum.
656
 
         * We could not check the checksum before checking the magic
657
 
         * and version because old log hdrs have the length and checksum
658
 
         * in a different location.
659
 
         */
660
 
        if (!CRYPTO_ON(dbenv) && ((ret = __db_check_chksum(dbenv,
661
 
            db_cipher, &hdr->chksum[0], (u_int8_t *)persist,
662
 
            hdr->len - hdrsize, is_hmac)) != 0)) {
663
 
                __db_err(dbenv, "log record checksum mismatch");
664
 
                goto err;
665
 
        }
666
 
 
667
 
        /*
668
 
         * If the log is readable so far and we're doing system initialization,
669
 
         * set the region's persistent information based on the headers.
670
 
         *
671
 
         * Always set the current log file size.  Only set the next log file's
672
 
         * size if the application hasn't set it already.
673
 
         *
674
 
         * XXX
675
 
         * Always use the persistent header's mode, regardless of what was set
676
 
         * in the current environment.  We've always done it this way, but it's
677
 
         * probably a bug -- I can't think of a way not-changing the mode would
678
 
         * be a problem, though.
679
 
         */
680
 
        if (set_persist) {
681
 
                region = dblp->reginfo.primary;
682
 
                region->log_size = persist->log_size;
683
 
                if (region->log_nsize == 0)
684
 
                        region->log_nsize = persist->log_size;
685
 
                region->persist.mode = persist->mode;
686
 
        }
687
 
 
688
 
err:    __os_free(dbenv, fname);
689
 
        if (need_free)
690
 
                __os_free(dbenv, tmp);
691
 
        *statusp = status;
692
 
        return (ret);
693
 
}
694
 
 
695
 
/*
696
 
 * __log_dbenv_refresh --
697
 
 *      Clean up after the log system on a close or failed open.  Called only
698
 
 * from __dbenv_refresh.  (Formerly called __log_close.)
699
 
 *
700
 
 * PUBLIC: int __log_dbenv_refresh __P((DB_ENV *));
701
 
 */
702
 
int
703
 
__log_dbenv_refresh(dbenv)
704
 
        DB_ENV *dbenv;
705
 
{
706
 
        DB_LOG *dblp;
707
 
        int ret, t_ret;
708
 
 
709
 
        dblp = dbenv->lg_handle;
710
 
 
711
 
        /* We may have opened files as part of XA; if so, close them. */
712
 
        F_SET(dblp, DBLOG_RECOVER);
713
 
        ret = __dbreg_close_files(dbenv);
714
 
 
715
 
        /* Discard the per-thread lock. */
716
 
        if (dblp->mutexp != NULL)
717
 
                __db_mutex_free(dbenv, &dblp->reginfo, dblp->mutexp);
718
 
 
719
 
        /* Detach from the region. */
720
 
        if ((t_ret =
721
 
            __db_r_detach(dbenv, &dblp->reginfo, 0)) != 0 && ret == 0)
722
 
                ret = t_ret;
723
 
 
724
 
        /* Close open files, release allocated memory. */
725
 
        if (F_ISSET(&dblp->lfh, DB_FH_VALID) &&
726
 
            (t_ret = __os_closehandle(dbenv, &dblp->lfh)) != 0 && ret == 0)
727
 
                ret = t_ret;
728
 
        if (dblp->dbentry != NULL)
729
 
                __os_free(dbenv, dblp->dbentry);
730
 
 
731
 
        __os_free(dbenv, dblp);
732
 
 
733
 
        dbenv->lg_handle = NULL;
734
 
        return (ret);
735
 
}
736
 
 
737
 
/*
738
 
 * __log_stat --
739
 
 *      Return log statistics.
740
 
 *
741
 
 * PUBLIC: int __log_stat __P((DB_ENV *, DB_LOG_STAT **, u_int32_t));
742
 
 */
743
 
int
744
 
__log_stat(dbenv, statp, flags)
745
 
        DB_ENV *dbenv;
746
 
        DB_LOG_STAT **statp;
747
 
        u_int32_t flags;
748
 
{
749
 
        DB_LOG *dblp;
750
 
        DB_LOG_STAT *stats;
751
 
        LOG *region;
752
 
        int ret;
753
 
 
754
 
        PANIC_CHECK(dbenv);
755
 
        ENV_REQUIRES_CONFIG(dbenv,
756
 
            dbenv->lg_handle, "DB_ENV->log_stat", DB_INIT_LOG);
757
 
 
758
 
        *statp = NULL;
759
 
        if ((ret = __db_fchk(dbenv,
760
 
            "DB_ENV->log_stat", flags, DB_STAT_CLEAR)) != 0)
761
 
                return (ret);
762
 
 
763
 
        dblp = dbenv->lg_handle;
764
 
        region = dblp->reginfo.primary;
765
 
 
766
 
        if ((ret = __os_umalloc(dbenv, sizeof(DB_LOG_STAT), &stats)) != 0)
767
 
                return (ret);
768
 
 
769
 
        /* Copy out the global statistics. */
770
 
        R_LOCK(dbenv, &dblp->reginfo);
771
 
        *stats = region->stat;
772
 
        if (LF_ISSET(DB_STAT_CLEAR))
773
 
                memset(&region->stat, 0, sizeof(region->stat));
774
 
 
775
 
        stats->st_magic = region->persist.magic;
776
 
        stats->st_version = region->persist.version;
777
 
        stats->st_mode = region->persist.mode;
778
 
        stats->st_lg_bsize = region->buffer_size;
779
 
        stats->st_lg_size = region->log_nsize;
780
 
 
781
 
        stats->st_region_wait = dblp->reginfo.rp->mutex.mutex_set_wait;
782
 
        stats->st_region_nowait = dblp->reginfo.rp->mutex.mutex_set_nowait;
783
 
        if (LF_ISSET(DB_STAT_CLEAR)) {
784
 
                dblp->reginfo.rp->mutex.mutex_set_wait = 0;
785
 
                dblp->reginfo.rp->mutex.mutex_set_nowait = 0;
786
 
        }
787
 
        stats->st_regsize = dblp->reginfo.rp->size;
788
 
 
789
 
        stats->st_cur_file = region->lsn.file;
790
 
        stats->st_cur_offset = region->lsn.offset;
791
 
        stats->st_disk_file = region->s_lsn.file;
792
 
        stats->st_disk_offset = region->s_lsn.offset;
793
 
 
794
 
        R_UNLOCK(dbenv, &dblp->reginfo);
795
 
 
796
 
        *statp = stats;
797
 
        return (0);
798
 
}
799
 
 
800
 
/*
801
 
 * __log_get_cached_ckp_lsn --
802
 
 *      Retrieve any last checkpoint LSN that we may have found on startup.
803
 
 *
804
 
 * PUBLIC: void __log_get_cached_ckp_lsn __P((DB_ENV *, DB_LSN *));
805
 
 */
806
 
void
807
 
__log_get_cached_ckp_lsn(dbenv, ckp_lsnp)
808
 
        DB_ENV *dbenv;
809
 
        DB_LSN *ckp_lsnp;
810
 
{
811
 
        DB_LOG *dblp;
812
 
        LOG *lp;
813
 
 
814
 
        dblp = (DB_LOG *)dbenv->lg_handle;
815
 
        lp = (LOG *)dblp->reginfo.primary;
816
 
 
817
 
        R_LOCK(dbenv, &dblp->reginfo);
818
 
        *ckp_lsnp = lp->cached_ckp_lsn;
819
 
        R_UNLOCK(dbenv, &dblp->reginfo);
820
 
}
821
 
 
822
 
/*
823
 
 * __log_region_size --
824
 
 *      Return the amount of space needed for the log region.
825
 
 *      Make the region large enough to hold txn_max transaction
826
 
 *      detail structures  plus some space to hold thread handles
827
 
 *      and the beginning of the shalloc region and anything we
828
 
 *      need for mutex system resource recording.
829
 
 */
830
 
static size_t
831
 
__log_region_size(dbenv)
832
 
        DB_ENV *dbenv;
833
 
{
834
 
        size_t s;
835
 
 
836
 
        s = dbenv->lg_regionmax + dbenv->lg_bsize;
837
 
#ifdef HAVE_MUTEX_SYSTEM_RESOURCES
838
 
        if (F_ISSET(dbenv, DB_ENV_THREAD))
839
 
                s += sizeof(REGMAINT) + LG_MAINT_SIZE;
840
 
#endif
841
 
        return (s);
842
 
}
843
 
 
844
 
/*
845
 
 * __log_region_destroy
846
 
 *      Destroy any region maintenance info.
847
 
 *
848
 
 * PUBLIC: void __log_region_destroy __P((DB_ENV *, REGINFO *));
849
 
 */
850
 
void
851
 
__log_region_destroy(dbenv, infop)
852
 
        DB_ENV *dbenv;
853
 
        REGINFO *infop;
854
 
{
855
 
        __db_shlocks_destroy(infop, (REGMAINT *)R_ADDR(infop,
856
 
            ((LOG *)R_ADDR(infop, infop->rp->primary))->maint_off));
857
 
 
858
 
        COMPQUIET(dbenv, NULL);
859
 
        COMPQUIET(infop, NULL);
860
 
}
861
 
 
862
 
/*
863
 
 * __log_vtruncate
864
 
 *      This is a virtual truncate.  We set up the log indicators to
865
 
 * make everyone believe that the given record is the last one in the
866
 
 * log.  Returns with the next valid LSN (i.e., the LSN of the next
867
 
 * record to be written). This is used in replication to discard records
868
 
 * in the log file that do not agree with the master.
869
 
 *
870
 
 * PUBLIC: int __log_vtruncate __P((DB_ENV *, DB_LSN *, DB_LSN *));
871
 
 */
872
 
int
873
 
__log_vtruncate(dbenv, lsn, ckplsn)
874
 
        DB_ENV *dbenv;
875
 
        DB_LSN *lsn, *ckplsn;
876
 
{
877
 
        DBT log_dbt;
878
 
        DB_FH fh;
879
 
        DB_LOG *dblp;
880
 
        DB_LOGC *logc;
881
 
        DB_LSN end_lsn;
882
 
        LOG *lp;
883
 
        u_int32_t bytes, c_len;
884
 
        int fn, ret, t_ret;
885
 
        char *fname;
886
 
 
887
 
        /* Need to find out the length of this soon-to-be-last record. */
888
 
        if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0)
889
 
                return (ret);
890
 
        memset(&log_dbt, 0, sizeof(log_dbt));
891
 
        ret = logc->get(logc, lsn, &log_dbt, DB_SET);
892
 
        c_len = logc->c_len;
893
 
        if ((t_ret = logc->close(logc, 0)) != 0 && ret == 0)
894
 
                ret = t_ret;
895
 
        if (ret != 0)
896
 
                return (ret);
897
 
 
898
 
        /* Now do the truncate. */
899
 
        dblp = (DB_LOG *)dbenv->lg_handle;
900
 
        lp = (LOG *)dblp->reginfo.primary;
901
 
 
902
 
        R_LOCK(dbenv, &dblp->reginfo);
903
 
        end_lsn = lp->lsn;
904
 
        lp->lsn = *lsn;
905
 
        lp->len = c_len;
906
 
        lp->lsn.offset += lp->len;
907
 
 
908
 
        /*
909
 
         * I am going to assume that the number of bytes written since
910
 
         * the last checkpoint doesn't exceed a 32-bit number.
911
 
         */
912
 
        DB_ASSERT(lp->lsn.file >= ckplsn->file);
913
 
        bytes = 0;
914
 
        if (ckplsn->file != lp->lsn.file) {
915
 
                bytes = lp->log_size - ckplsn->offset;
916
 
                if (lp->lsn.file > ckplsn->file + 1)
917
 
                        bytes += lp->log_size *
918
 
                            (lp->lsn.file - ckplsn->file - 1);
919
 
                bytes += lp->lsn.offset;
920
 
        } else
921
 
                bytes = lp->lsn.offset - ckplsn->offset;
922
 
 
923
 
        lp->stat.st_wc_mbytes += bytes / MEGABYTE;
924
 
        lp->stat.st_wc_bytes += bytes % MEGABYTE;
925
 
 
926
 
        /*
927
 
         * If the saved lsn is greater than our new end of log, reset it
928
 
         * to our current end of log.
929
 
         */
930
 
        if (log_compare(&lp->s_lsn, lsn) > 0)
931
 
                lp->s_lsn = lp->lsn;
932
 
 
933
 
        /*
934
 
         * If the new end of log is in the middle of the buffer,
935
 
         * don't change the w_off or f_lsn.  If the new end is
936
 
         * before the w_off then reset w_off and f_lsn to the new
937
 
         * end of log.
938
 
         */
939
 
        if (lp->w_off >= lp->lsn.offset) {
940
 
                lp->f_lsn = lp->lsn;
941
 
                lp->w_off = lp->lsn.offset;
942
 
                lp->b_off = 0;
943
 
        } else
944
 
                lp->b_off = lp->lsn.offset - lp->w_off;
945
 
 
946
 
        ZERO_LSN(lp->waiting_lsn);
947
 
        lp->ready_lsn = lp->lsn;
948
 
        lp->wait_recs = 0;
949
 
        lp->rcvd_recs = 0;
950
 
 
951
 
        /* Now throw away any extra log files that we have around. */
952
 
        for (fn = lp->lsn.file + 1;; fn++) {
953
 
                if (__log_name(dblp, fn, &fname, &fh, DB_OSO_RDONLY) != 0) {
954
 
                        __os_free(dbenv, fname);
955
 
                        break;
956
 
                }
957
 
                (void)__os_closehandle(dbenv, &fh);
958
 
                ret = __os_unlink(dbenv, fname);
959
 
                __os_free(dbenv, fname);
960
 
                if (ret != 0)
961
 
                        goto err;
962
 
        }
963
 
 
964
 
        /* Truncate the log to the new point. */
965
 
        if ((ret = __log_zero(dbenv, &lp->lsn, &end_lsn)) != 0)
966
 
                goto err;
967
 
 
968
 
err:    R_UNLOCK(dbenv, &dblp->reginfo);
969
 
        return (ret);
970
 
}
971
 
 
972
 
/*
973
 
 * __log_is_outdated --
974
 
 *      Used by the replication system to identify if a client's logs
975
 
 * are too old.  The log represented by dbenv is compared to the file
976
 
 * number passed in fnum.  If the log file fnum does not exist and is
977
 
 * lower-numbered than the current logs, the we return *outdatedp non
978
 
 * zero, else we return it 0.
979
 
 *
980
 
 * PUBLIC: int __log_is_outdated __P((DB_ENV *dbenv,
981
 
 * PUBLIC:     u_int32_t fnum, int *outdatedp));
982
 
 */
983
 
int
984
 
__log_is_outdated(dbenv, fnum, outdatedp)
985
 
        DB_ENV *dbenv;
986
 
        u_int32_t fnum;
987
 
        int *outdatedp;
988
 
{
989
 
        DB_LOG *dblp;
990
 
        LOG *lp;
991
 
        char *name;
992
 
        int ret;
993
 
        u_int32_t cfile;
994
 
 
995
 
        dblp = dbenv->lg_handle;
996
 
        *outdatedp = 0;
997
 
 
998
 
        if ((ret = __log_name(dblp, fnum, &name, NULL, 0)) != 0)
999
 
                return (ret);
1000
 
 
1001
 
        /* If the file exists, we're just fine. */
1002
 
        if (__os_exists(name, NULL) == 0)
1003
 
                goto out;
1004
 
 
1005
 
        /*
1006
 
         * It didn't exist, decide if the file number is too big or
1007
 
         * too little.  If it's too little, then we need to indicate
1008
 
         * that the LSN is outdated.
1009
 
         */
1010
 
        R_LOCK(dbenv, &dblp->reginfo);
1011
 
        lp = (LOG *)dblp->reginfo.primary;
1012
 
        cfile = lp->lsn.file;
1013
 
        R_UNLOCK(dbenv, &dblp->reginfo);
1014
 
 
1015
 
        if (cfile > fnum)
1016
 
                *outdatedp = 1;
1017
 
out:    __os_free(dbenv, name);
1018
 
        return (ret);
1019
 
}
1020
 
 
1021
 
/*
1022
 
 * __log_zero --
1023
 
 *      Zero out the tail of a log after a truncate.
1024
 
 */
1025
 
static int
1026
 
__log_zero(dbenv, from_lsn, to_lsn)
1027
 
        DB_ENV *dbenv;
1028
 
        DB_LSN *from_lsn, *to_lsn;
1029
 
{
1030
 
        char *lname;
1031
 
        DB_LOG *dblp;
1032
 
        LOG *lp;
1033
 
        int ret;
1034
 
        size_t nbytes, len, nw;
1035
 
        u_int8_t buf[4096];
1036
 
        u_int32_t mbytes, bytes;
1037
 
 
1038
 
        dblp = dbenv->lg_handle;
1039
 
        lp = (LOG *)dblp->reginfo.primary;
1040
 
        lname = NULL;
1041
 
 
1042
 
        if (dblp->lfname != lp->lsn.file) {
1043
 
                if (F_ISSET(&dblp->lfh, DB_FH_VALID))
1044
 
                        (void)__os_closehandle(dbenv, &dblp->lfh);
1045
 
                dblp->lfname = lp->lsn.file;
1046
 
        }
1047
 
 
1048
 
        if (from_lsn->file != to_lsn->file) {
1049
 
                /* We removed some log files; have to 0 to end of file. */
1050
 
                if (!F_ISSET(&dblp->lfh, DB_FH_VALID) && (ret =
1051
 
                    __log_name(dblp, dblp->lfname, &lname, &dblp->lfh, 0)) != 0)
1052
 
                        return (ret);
1053
 
                if ((ret = __os_ioinfo(dbenv,
1054
 
                    NULL, &dblp->lfh, &mbytes, &bytes, NULL)) != 0)
1055
 
                        goto err;
1056
 
                len = mbytes * MEGABYTE + bytes - from_lsn->offset;
1057
 
        } else if (to_lsn->offset <= from_lsn->offset)
1058
 
                return (0);
1059
 
        else
1060
 
                len = to_lsn->offset = from_lsn->offset;
1061
 
 
1062
 
        memset(buf, 0, sizeof(buf));
1063
 
 
1064
 
        /* Initialize the write position. */
1065
 
        if (!F_ISSET(&dblp->lfh, DB_FH_VALID) &&
1066
 
            (ret = __log_name(dblp, dblp->lfname, &lname, &dblp->lfh, 0)) != 0)
1067
 
                goto err;
1068
 
 
1069
 
        if ((ret = __os_seek(dbenv,
1070
 
            &dblp->lfh, 0, 0, from_lsn->offset, 0, DB_OS_SEEK_SET)) != 0)
1071
 
                return (ret);
1072
 
 
1073
 
        while (len > 0) {
1074
 
                nbytes = len > sizeof(buf) ? sizeof(buf) : len;
1075
 
                if ((ret =
1076
 
                    __os_write(dbenv, &dblp->lfh, buf, nbytes, &nw)) != 0)
1077
 
                        return (ret);
1078
 
                len -= nbytes;
1079
 
        }
1080
 
err:    if (lname != NULL)
1081
 
                __os_free(dbenv, lname);
1082
 
 
1083
 
        return (0);
1084
 
}