]> git.ipfire.org Git - thirdparty/glibc.git/blob - db2/log/log.c
Update.
[thirdparty/glibc.git] / db2 / log / log.c
1 /*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
6 */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)log.c 10.39 (Sleepycat) 1/17/98";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15 #include <sys/stat.h>
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #endif
23
24 #include "db_int.h"
25 #include "shqueue.h"
26 #include "db_shash.h"
27 #include "log.h"
28 #include "db_dispatch.h"
29 #include "txn_auto.h"
30 #include "common_ext.h"
31
32 static int __log_recover __P((DB_LOG *));
33
34 /*
35 * log_open --
36 * Initialize and/or join a log.
37 */
38 int
39 log_open(path, flags, mode, dbenv, lpp)
40 const char *path;
41 int flags;
42 int mode;
43 DB_ENV *dbenv;
44 DB_LOG **lpp;
45 {
46 DB_LOG *dblp;
47 LOG *lp;
48 size_t len;
49 int fd, newregion, ret, retry_cnt;
50
51 /* Validate arguments. */
52 #ifdef HAVE_SPINLOCKS
53 #define OKFLAGS (DB_CREATE | DB_THREAD)
54 #else
55 #define OKFLAGS (DB_CREATE)
56 #endif
57 if ((ret = __db_fchk(dbenv, "log_open", flags, OKFLAGS)) != 0)
58 return (ret);
59
60 /*
61 * We store 4-byte offsets into the file, so the maximum file
62 * size can't be larger than that.
63 */
64 if (dbenv != NULL && dbenv->lg_max > UINT32_T_MAX) {
65 __db_err(dbenv, "log_open: maximum file size too large");
66 return (EINVAL);
67 }
68
69 /* Create and initialize the DB_LOG structure. */
70 if ((dblp = (DB_LOG *)__db_calloc(1, sizeof(DB_LOG))) == NULL)
71 return (ENOMEM);
72
73 if (path != NULL && (dblp->dir = __db_strdup(path)) == NULL) {
74 __db_free(dblp);
75 return (ENOMEM);
76 }
77
78 dblp->dbenv = dbenv;
79 dblp->lfd = -1;
80 ZERO_LSN(dblp->c_lsn);
81 dblp->c_fd = -1;
82
83 /*
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
86 * grow it.
87 */
88 len = 30 * 1024;
89
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);
95 if (ret == 0) {
96 /* Put the LOG structure first in the region. */
97 lp = dblp->maddr;
98
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));
102
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);
111
112 /* Initialize LOG LSNs. */
113 lp->lsn.file = 1;
114 lp->lsn.offset = 0;
115
116 newregion = 1;
117 } else if (ret != EEXIST)
118 goto err;
119 }
120
121 /* If we didn't or couldn't create the region, try and join it. */
122 if (!newregion &&
123 (ret = __db_ropen(dbenv, DB_APP_LOG,
124 path, DB_DEFAULT_LOG_FILE, 0, &fd, &dblp->maddr)) != 0) {
125 /*
126 * If we fail because the file isn't available, wait a
127 * second and try again.
128 */
129 if (ret == EAGAIN && ++retry_cnt < 3) {
130 (void)__db_sleep(1, 0);
131 goto retry;
132 }
133 goto err;
134 }
135
136 /* Set up the common information. */
137 dblp->lp = dblp->maddr;
138 dblp->addr = (u_int8_t *)dblp->maddr + sizeof(LOG);
139 dblp->fd = fd;
140
141 /* Initialize thread information. */
142 if (LF_ISSET(DB_THREAD)) {
143 F_SET(dblp, DB_AM_THREAD);
144
145 if (!newregion)
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);
150 if (!newregion)
151 UNLOCK_LOGREGION(dblp);
152 if (ret != 0) {
153 (void)log_close(dblp);
154 if (newregion)
155 (void)log_unlink(path, 1, dbenv);
156 return (ret);
157 }
158 }
159
160 /*
161 * If doing recovery, try and recover any previous log files
162 * before releasing the lock.
163 */
164 if (newregion) {
165 ret = __log_recover(dblp);
166 UNLOCK_LOGREGION(dblp);
167
168 if (ret != 0) {
169 (void)log_close(dblp);
170 (void)log_unlink(path, 1, dbenv);
171 return (ret);
172 }
173 }
174 *lpp = dblp;
175 return (0);
176
177 err: /*
178 * We never get here with an allocated thread-mutex, so we do
179 * not have to worry about freeing it.
180 */
181 FREE(dblp, sizeof(DB_LOG));
182 return (ret);
183
184 }
185
186 /*
187 * __log_recover --
188 * Recover a log.
189 */
190 static int
191 __log_recover(dblp)
192 DB_LOG *dblp;
193 {
194 DBT dbt;
195 DB_LSN lsn;
196 LOG *lp;
197 u_int32_t chk;
198 int cnt, found_checkpoint, ret;
199
200 lp = dblp->lp;
201
202 /*
203 * Find a log file. If none exist, we simply return, leaving
204 * everything initialized to a new log.
205 */
206 if ((ret = __log_find(dblp, 0, &cnt)) != 0)
207 return (ret);
208 if (cnt == 0)
209 return (0);
210
211 /*
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
215 * the log's end.
216 */
217 lp->lsn.file = cnt + 1;
218 lp->lsn.offset = 0;
219 lsn.file = cnt;
220 lsn.offset = 0;
221
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)
225 return (ret);
226
227 /*
228 * Read to the end of the file, saving checkpoints. This will fail
229 * at some point, so turn off error messages.
230 */
231 found_checkpoint = 0;
232 while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) {
233 if (dbt.size < sizeof(u_int32_t))
234 continue;
235 memcpy(&chk, dbt.data, sizeof(u_int32_t));
236 if (chk == DB_txn_ckp) {
237 lp->c_lsn = lsn;
238 found_checkpoint = 1;
239 }
240 }
241
242 /*
243 * We know where the end of the log is. Since that record is on disk,
244 * it's also the last-synced LSN.
245 */
246 lp->lsn = lsn;
247 lp->lsn.offset += dblp->c_len;
248 lp->s_lsn = lp->lsn;
249
250 /* Set up the current buffer information, too. */
251 lp->len = dblp->c_len;
252 lp->b_off = 0;
253 lp->w_off = lp->lsn.offset;
254
255 /*
256 * It's possible that we didn't find a checkpoint because there wasn't
257 * one in the last log file. Start searching.
258 */
259 while (!found_checkpoint && cnt > 1) {
260 lsn.file = --cnt;
261 lsn.offset = 0;
262
263 /* Set the cursor. Shouldn't fail, leave error messages on. */
264 if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
265 return (ret);
266
267 /*
268 * Read to the end of the file, saving checkpoints. Shouldn't
269 * fail, leave error messages on.
270 */
271 while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 0) == 0) {
272 if (dbt.size < sizeof(u_int32_t))
273 continue;
274 memcpy(&chk, dbt.data, sizeof(u_int32_t));
275 if (chk == DB_txn_ckp) {
276 lp->c_lsn = lsn;
277 found_checkpoint = 1;
278 }
279 }
280 }
281
282 /* If we never find a checkpoint, that's okay, just 0 it out. */
283 if (!found_checkpoint)
284 ZERO_LSN(lp->c_lsn);
285
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);
289
290 return (0);
291 }
292
293 /*
294 * __log_find --
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
297 * the last log file.
298 *
299 * PUBLIC: int __log_find __P((DB_LOG *, int, int *));
300 */
301 int
302 __log_find(dblp, find_first, valp)
303 DB_LOG *dblp;
304 int find_first, *valp;
305 {
306 int cnt, fcnt, logval, ret;
307 const char *dir;
308 char **names, *p, *q;
309
310 *valp = 0;
311
312 /* Find the directory name. */
313 if ((ret = __log_name(dblp, 1, &p)) != 0)
314 return (ret);
315 if ((q = __db_rpath(p)) == NULL)
316 dir = PATH_DOT;
317 else {
318 *q = '\0';
319 dir = p;
320 }
321
322 /* Get the list of file names. */
323 ret = __db_dirlist(dir, &names, &fcnt);
324 FREES(p);
325 if (ret != 0) {
326 __db_err(dblp->dbenv, "%s: %s", dir, strerror(ret));
327 return (ret);
328 }
329
330 /*
331 * Search for a valid log file name, return a value of 0 on
332 * failure.
333 */
334 for (cnt = fcnt, logval = 0; --cnt >= 0;)
335 if (strncmp(names[cnt], "log.", sizeof("log.") - 1) == 0) {
336 logval = atoi(names[cnt] + 4);
337 if (logval != 0 &&
338 __log_valid(dblp, dblp->lp, logval) == 0)
339 break;
340 }
341
342 /* Discard the list. */
343 __db_dirfree(names, fcnt);
344
345 /* We have a valid log file, find either the first or last one. */
346 if (find_first) {
347 for (; logval > 0; --logval)
348 if (__log_valid(dblp, dblp->lp, logval - 1) != 0)
349 break;
350 } else
351 for (; logval < MAXLFNAME; ++logval)
352 if (__log_valid(dblp, dblp->lp, logval + 1) != 0)
353 break;
354 *valp = logval;
355
356 return (0);
357 }
358
359 /*
360 * log_valid --
361 * Validate a log file.
362 *
363 * PUBLIC: int __log_valid __P((DB_LOG *, LOG *, int));
364 */
365 int
366 __log_valid(dblp, lp, cnt)
367 DB_LOG *dblp;
368 LOG *lp;
369 int cnt;
370 {
371 LOGP persist;
372 ssize_t nw;
373 int fd, ret;
374 char *p;
375
376 if ((ret = __log_name(dblp, cnt, &p)) != 0)
377 return (ret);
378
379 fd = -1;
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)) {
386 if (ret == 0)
387 ret = EIO;
388 if (fd != -1) {
389 (void)__db_close(fd);
390 __db_err(dblp->dbenv,
391 "Ignoring log file: %s: %s", p, strerror(ret));
392 }
393 goto err;
394 }
395 (void)__db_close(fd);
396
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);
401 ret = EINVAL;
402 goto err;
403 }
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);
408 ret = EINVAL;
409 goto err;
410 }
411
412 if (lp != NULL) {
413 lp->persist.lg_max = persist.lg_max;
414 lp->persist.mode = persist.mode;
415 }
416 ret = 0;
417
418 err: FREES(p);
419 return (ret);
420 }
421
422 /*
423 * log_close --
424 * Close a log.
425 */
426 int
427 log_close(dblp)
428 DB_LOG *dblp;
429 {
430 int ret, t_ret;
431
432 ret = 0;
433
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);
439 }
440
441 /* Close the region. */
442 if ((t_ret =
443 __db_rclose(dblp->dbenv, dblp->fd, dblp->maddr)) != 0 && ret == 0)
444 ret = t_ret;
445
446 /* Close open files, release allocated memory. */
447 if (dblp->lfd != -1 && (t_ret = __db_close(dblp->lfd)) != 0 && ret == 0)
448 ret = t_ret;
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)
453 ret = t_ret;
454 if (dblp->dbentry != NULL)
455 FREE(dblp->dbentry, (dblp->dbentry_cnt * sizeof(DB_ENTRY)));
456 if (dblp->dir != NULL)
457 FREES(dblp->dir);
458
459 /* Free the structure. */
460 FREE(dblp, sizeof(DB_LOG));
461
462 return (ret);
463 }
464
465 /*
466 * log_unlink --
467 * Exit a log.
468 */
469 int
470 log_unlink(path, force, dbenv)
471 const char *path;
472 int force;
473 DB_ENV *dbenv;
474 {
475 return (__db_runlink(dbenv,
476 DB_APP_LOG, path, DB_DEFAULT_LOG_FILE, force));
477 }
478
479 /*
480 * log_stat --
481 * Return LOG statistics.
482 */
483 int
484 log_stat(dblp, gspp, db_malloc)
485 DB_LOG *dblp;
486 DB_LOG_STAT **gspp;
487 void *(*db_malloc) __P((size_t));
488 {
489 LOG *lp;
490
491 *gspp = NULL;
492 lp = dblp->lp;
493
494 if ((*gspp = db_malloc == NULL ?
495 (DB_LOG_STAT *)__db_malloc(sizeof(**gspp)) :
496 (DB_LOG_STAT *)db_malloc(sizeof(**gspp))) == NULL)
497 return (ENOMEM);
498
499 /* Copy out the global statistics. */
500 LOCK_LOGREGION(dblp);
501 **gspp = lp->stat;
502
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;
507
508 (*gspp)->st_region_nowait = lp->rlayout.lock.mutex_set_nowait;
509 (*gspp)->st_region_wait = lp->rlayout.lock.mutex_set_wait;
510
511 (*gspp)->st_cur_file = lp->lsn.file;
512 (*gspp)->st_cur_offset = lp->lsn.offset;
513
514 UNLOCK_LOGREGION(dblp);
515
516 return (0);
517 }