]>
git.ipfire.org Git - thirdparty/glibc.git/blob - db2/log/log.c
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid
[] = "@(#)log.c 10.39 (Sleepycat) 1/17/98";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
28 #include "db_dispatch.h"
30 #include "common_ext.h"
32 static int __log_recover
__P((DB_LOG
*));
36 * Initialize and/or join a log.
39 log_open(path
, flags
, mode
, dbenv
, lpp
)
49 int fd
, newregion
, ret
, retry_cnt
;
51 /* Validate arguments. */
53 #define OKFLAGS (DB_CREATE | DB_THREAD)
55 #define OKFLAGS (DB_CREATE)
57 if ((ret
= __db_fchk(dbenv
, "log_open", flags
, OKFLAGS
)) != 0)
61 * We store 4-byte offsets into the file, so the maximum file
62 * size can't be larger than that.
64 if (dbenv
!= NULL
&& dbenv
->lg_max
> UINT32_T_MAX
) {
65 __db_err(dbenv
, "log_open: maximum file size too large");
69 /* Create and initialize the DB_LOG structure. */
70 if ((dblp
= (DB_LOG
*)__db_calloc(1, sizeof(DB_LOG
))) == NULL
)
73 if (path
!= NULL
&& (dblp
->dir
= __db_strdup(path
)) == NULL
) {
80 ZERO_LSN(dblp
->c_lsn
);
84 * The log region isn't fixed size because we store the registered
85 * file names there. Make it fairly large so that we don't have to
90 /* Map in the region. */
91 retry_cnt
= newregion
= 0;
92 retry
: if (LF_ISSET(DB_CREATE
)) {
93 ret
= __db_rcreate(dbenv
, DB_APP_LOG
, path
,
94 DB_DEFAULT_LOG_FILE
, mode
, len
, 0, &fd
, &dblp
->maddr
);
96 /* Put the LOG structure first in the region. */
99 /* Initialize the rest of the region as free space. */
100 dblp
->addr
= (u_int8_t
*)dblp
->maddr
+ sizeof(LOG
);
101 __db_shalloc_init(dblp
->addr
, len
- sizeof(LOG
));
103 /* Initialize the LOG structure. */
104 lp
->persist
.lg_max
= dbenv
== NULL
? 0 : dbenv
->lg_max
;
105 if (lp
->persist
.lg_max
== 0)
106 lp
->persist
.lg_max
= DEFAULT_MAX
;
107 lp
->persist
.magic
= DB_LOGMAGIC
;
108 lp
->persist
.version
= DB_LOGVERSION
;
109 lp
->persist
.mode
= mode
;
110 SH_TAILQ_INIT(&lp
->fq
);
112 /* Initialize LOG LSNs. */
117 } else if (ret
!= EEXIST
)
121 /* If we didn't or couldn't create the region, try and join it. */
123 (ret
= __db_ropen(dbenv
, DB_APP_LOG
,
124 path
, DB_DEFAULT_LOG_FILE
, 0, &fd
, &dblp
->maddr
)) != 0) {
126 * If we fail because the file isn't available, wait a
127 * second and try again.
129 if (ret
== EAGAIN
&& ++retry_cnt
< 3) {
130 (void)__db_sleep(1, 0);
136 /* Set up the common information. */
137 dblp
->lp
= dblp
->maddr
;
138 dblp
->addr
= (u_int8_t
*)dblp
->maddr
+ sizeof(LOG
);
141 /* Initialize thread information. */
142 if (LF_ISSET(DB_THREAD
)) {
143 F_SET(dblp
, DB_AM_THREAD
);
146 LOCK_LOGREGION(dblp
);
147 if ((ret
= __db_shalloc(dblp
->addr
,
148 sizeof(db_mutex_t
), MUTEX_ALIGNMENT
, &dblp
->mutexp
)) == 0)
149 (void)__db_mutex_init(dblp
->mutexp
, -1);
151 UNLOCK_LOGREGION(dblp
);
153 (void)log_close(dblp
);
155 (void)log_unlink(path
, 1, dbenv
);
161 * If doing recovery, try and recover any previous log files
162 * before releasing the lock.
165 ret
= __log_recover(dblp
);
166 UNLOCK_LOGREGION(dblp
);
169 (void)log_close(dblp
);
170 (void)log_unlink(path
, 1, dbenv
);
178 * We never get here with an allocated thread-mutex, so we do
179 * not have to worry about freeing it.
181 FREE(dblp
, sizeof(DB_LOG
));
198 int cnt
, found_checkpoint
, ret
;
203 * Find a log file. If none exist, we simply return, leaving
204 * everything initialized to a new log.
206 if ((ret
= __log_find(dblp
, 0, &cnt
)) != 0)
212 * We have the last useful log file and we've loaded any persistent
213 * information. Pretend that the log is larger than it can possibly
214 * be, and read the last file, looking for the last checkpoint and
217 lp
->lsn
.file
= cnt
+ 1;
222 /* Set the cursor. Shouldn't fail, leave error messages on. */
223 memset(&dbt
, 0, sizeof(dbt
));
224 if ((ret
= __log_get(dblp
, &lsn
, &dbt
, DB_SET
, 0)) != 0)
228 * Read to the end of the file, saving checkpoints. This will fail
229 * at some point, so turn off error messages.
231 found_checkpoint
= 0;
232 while (__log_get(dblp
, &lsn
, &dbt
, DB_NEXT
, 1) == 0) {
233 if (dbt
.size
< sizeof(u_int32_t
))
235 memcpy(&chk
, dbt
.data
, sizeof(u_int32_t
));
236 if (chk
== DB_txn_ckp
) {
238 found_checkpoint
= 1;
243 * We know where the end of the log is. Since that record is on disk,
244 * it's also the last-synced LSN.
247 lp
->lsn
.offset
+= dblp
->c_len
;
250 /* Set up the current buffer information, too. */
251 lp
->len
= dblp
->c_len
;
253 lp
->w_off
= lp
->lsn
.offset
;
256 * It's possible that we didn't find a checkpoint because there wasn't
257 * one in the last log file. Start searching.
259 while (!found_checkpoint
&& cnt
> 1) {
263 /* Set the cursor. Shouldn't fail, leave error messages on. */
264 if ((ret
= __log_get(dblp
, &lsn
, &dbt
, DB_SET
, 0)) != 0)
268 * Read to the end of the file, saving checkpoints. Shouldn't
269 * fail, leave error messages on.
271 while (__log_get(dblp
, &lsn
, &dbt
, DB_NEXT
, 0) == 0) {
272 if (dbt
.size
< sizeof(u_int32_t
))
274 memcpy(&chk
, dbt
.data
, sizeof(u_int32_t
));
275 if (chk
== DB_txn_ckp
) {
277 found_checkpoint
= 1;
282 /* If we never find a checkpoint, that's okay, just 0 it out. */
283 if (!found_checkpoint
)
286 __db_err(dblp
->dbenv
,
287 "Recovering the log: last valid LSN: file: %lu offset %lu",
288 (u_long
)lp
->lsn
.file
, (u_long
)lp
->lsn
.offset
);
295 * Try to find a log file. If find_first is set, valp will contain
296 * the number of the first log file, else it will contain the number of
299 * PUBLIC: int __log_find __P((DB_LOG *, int, int *));
302 __log_find(dblp
, find_first
, valp
)
304 int find_first
, *valp
;
306 int cnt
, fcnt
, logval
, ret
;
308 char **names
, *p
, *q
;
312 /* Find the directory name. */
313 if ((ret
= __log_name(dblp
, 1, &p
)) != 0)
315 if ((q
= __db_rpath(p
)) == NULL
)
322 /* Get the list of file names. */
323 ret
= __db_dirlist(dir
, &names
, &fcnt
);
326 __db_err(dblp
->dbenv
, "%s: %s", dir
, strerror(ret
));
331 * Search for a valid log file name, return a value of 0 on
334 for (cnt
= fcnt
, logval
= 0; --cnt
>= 0;)
335 if (strncmp(names
[cnt
], "log.", sizeof("log.") - 1) == 0) {
336 logval
= atoi(names
[cnt
] + 4);
338 __log_valid(dblp
, dblp
->lp
, logval
) == 0)
342 /* Discard the list. */
343 __db_dirfree(names
, fcnt
);
345 /* We have a valid log file, find either the first or last one. */
347 for (; logval
> 0; --logval
)
348 if (__log_valid(dblp
, dblp
->lp
, logval
- 1) != 0)
351 for (; logval
< MAXLFNAME
; ++logval
)
352 if (__log_valid(dblp
, dblp
->lp
, logval
+ 1) != 0)
361 * Validate a log file.
363 * PUBLIC: int __log_valid __P((DB_LOG *, LOG *, int));
366 __log_valid(dblp
, lp
, cnt
)
376 if ((ret
= __log_name(dblp
, cnt
, &p
)) != 0)
380 if ((ret
= __db_open(p
,
381 DB_RDONLY
| DB_SEQUENTIAL
,
382 DB_RDONLY
| DB_SEQUENTIAL
, 0, &fd
)) != 0 ||
383 (ret
= __db_seek(fd
, 0, 0, sizeof(HDR
), SEEK_SET
)) != 0 ||
384 (ret
= __db_read(fd
, &persist
, sizeof(LOGP
), &nw
)) != 0 ||
385 nw
!= sizeof(LOGP
)) {
389 (void)__db_close(fd
);
390 __db_err(dblp
->dbenv
,
391 "Ignoring log file: %s: %s", p
, strerror(ret
));
395 (void)__db_close(fd
);
397 if (persist
.magic
!= DB_LOGMAGIC
) {
398 __db_err(dblp
->dbenv
,
399 "Ignoring log file: %s: magic number %lx, not %lx",
400 p
, (u_long
)persist
.magic
, (u_long
)DB_LOGMAGIC
);
404 if (persist
.version
< DB_LOGOLDVER
|| persist
.version
> DB_LOGVERSION
) {
405 __db_err(dblp
->dbenv
,
406 "Ignoring log file: %s: unsupported log version %lu",
407 p
, (u_long
)persist
.version
);
413 lp
->persist
.lg_max
= persist
.lg_max
;
414 lp
->persist
.mode
= persist
.mode
;
434 /* Discard the per-thread pointer. */
435 if (dblp
->mutexp
!= NULL
) {
436 LOCK_LOGREGION(dblp
);
437 __db_shalloc_free(dblp
->addr
, dblp
->mutexp
);
438 UNLOCK_LOGREGION(dblp
);
441 /* Close the region. */
443 __db_rclose(dblp
->dbenv
, dblp
->fd
, dblp
->maddr
)) != 0 && ret
== 0)
446 /* Close open files, release allocated memory. */
447 if (dblp
->lfd
!= -1 && (t_ret
= __db_close(dblp
->lfd
)) != 0 && ret
== 0)
449 if (dblp
->c_dbt
.data
!= NULL
)
450 FREE(dblp
->c_dbt
.data
, dblp
->c_dbt
.ulen
);
451 if (dblp
->c_fd
!= -1 &&
452 (t_ret
= __db_close(dblp
->c_fd
)) != 0 && ret
== 0)
454 if (dblp
->dbentry
!= NULL
)
455 FREE(dblp
->dbentry
, (dblp
->dbentry_cnt
* sizeof(DB_ENTRY
)));
456 if (dblp
->dir
!= NULL
)
459 /* Free the structure. */
460 FREE(dblp
, sizeof(DB_LOG
));
470 log_unlink(path
, force
, dbenv
)
475 return (__db_runlink(dbenv
,
476 DB_APP_LOG
, path
, DB_DEFAULT_LOG_FILE
, force
));
481 * Return LOG statistics.
484 log_stat(dblp
, gspp
, db_malloc
)
487 void *(*db_malloc
) __P((size_t));
494 if ((*gspp
= db_malloc
== NULL
?
495 (DB_LOG_STAT
*)__db_malloc(sizeof(**gspp
)) :
496 (DB_LOG_STAT
*)db_malloc(sizeof(**gspp
))) == NULL
)
499 /* Copy out the global statistics. */
500 LOCK_LOGREGION(dblp
);
503 (*gspp
)->st_magic
= lp
->persist
.magic
;
504 (*gspp
)->st_version
= lp
->persist
.version
;
505 (*gspp
)->st_mode
= lp
->persist
.mode
;
506 (*gspp
)->st_lg_max
= lp
->persist
.lg_max
;
508 (*gspp
)->st_region_nowait
= lp
->rlayout
.lock
.mutex_set_nowait
;
509 (*gspp
)->st_region_wait
= lp
->rlayout
.lock
.mutex_set_wait
;
511 (*gspp
)->st_cur_file
= lp
->lsn
.file
;
512 (*gspp
)->st_cur_offset
= lp
->lsn
.offset
;
514 UNLOCK_LOGREGION(dblp
);