Update to db 2.3.10.
[kopensolaris-gnu/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.27 (Sleepycat) 9/23/97";
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 *)calloc(1, sizeof(DB_LOG))) == NULL)
71                 return (ENOMEM);
72
73         if (path != NULL && (dblp->dir = strdup(path)) == NULL) {
74                 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, &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, &cnt)) != 0)
207                 return (ret);
208         if (cnt == 0)
209                 return (0);
210
211         /* We have a log file name, find the last one. */
212         while (cnt < MAXLFNAME)
213                 if (__log_valid(dblp, lp, ++cnt) != 0) {
214                         --cnt;
215                         break;
216                 }
217
218         /*
219          * We have the last useful log file and we've loaded any persistent
220          * information.  Pretend that the log is larger than it can possibly
221          * be, and read this file, looking for a checkpoint and its end.
222          */
223         dblp->c_lsn.file = cnt;
224         dblp->c_lsn.offset = 0;
225         lsn = dblp->c_lsn;
226         lp->lsn.file = cnt + 1;
227         lp->lsn.offset = 0;
228
229         /* Set the cursor.  Shouldn't fail, leave error messages on. */
230         memset(&dbt, 0, sizeof(dbt));
231         if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
232                 return (ret);
233
234         /*
235          * Read to the end of the file, saving checkpoints.  This will fail
236          * at some point, so turn off error messages.
237          */
238         found_checkpoint = 0;
239         while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) {
240                 if (dbt.size < sizeof(u_int32_t))
241                         continue;
242                 memcpy(&chk, dbt.data, sizeof(u_int32_t));
243                 if (chk == DB_txn_ckp) {
244                         lp->c_lsn = lsn;
245                         found_checkpoint = 1;
246                 }
247         }
248
249         /*
250          * We know where the end of the log is.  Since that record is on disk,
251          * it's also the last-synced LSN.
252          */
253         lp->lsn = lsn;
254         lp->lsn.offset += dblp->c_len;
255         lp->s_lsn = lp->lsn;
256
257         /* Set up the current buffer information, too. */
258         lp->len = dblp->c_len;
259         lp->b_off = 0;
260         lp->w_off = lp->lsn.offset;
261
262         /*
263          * It's possible that we didn't find a checkpoint because there wasn't
264          * one in the last log file.  Start searching.
265          */
266         while (!found_checkpoint && cnt > 1) {
267                 dblp->c_lsn.file = --cnt;
268                 dblp->c_lsn.offset = 0;
269                 lsn = dblp->c_lsn;
270
271                 /* Set the cursor.  Shouldn't fail, leave error messages on. */
272                 if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
273                         return (ret);
274
275                 /*
276                  * Read to the end of the file, saving checkpoints.  Shouldn't
277                  * fail, leave error messages on.
278                  */
279                 while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 0) == 0) {
280                         if (dbt.size < sizeof(u_int32_t))
281                                 continue;
282                         memcpy(&chk, dbt.data, sizeof(u_int32_t));
283                         if (chk == DB_txn_ckp) {
284                                 lp->c_lsn = lsn;
285                                 found_checkpoint = 1;
286                         }
287                 }
288         }
289
290         /* If we never find a checkpoint, that's okay, just 0 it out. */
291         if (!found_checkpoint) {
292                 lp->c_lsn.file = 1;
293                 lp->c_lsn.offset = 0;
294         }
295
296         __db_err(dblp->dbenv,
297             "Recovering the log: last valid LSN: file: %lu offset %lu",
298             (u_long)lp->lsn.file, (u_long)lp->lsn.offset);
299
300         /* Reset the cursor.  */
301         ZERO_LSN(dblp->c_lsn);
302
303         return (0);
304 }
305
306 /*
307  * __log_find --
308  *      Try to find a log file.
309  *
310  * PUBLIC: int __log_find __P((DB_LOG *, int *));
311  */
312 int
313 __log_find(dblp, valp)
314         DB_LOG *dblp;
315         int *valp;
316 {
317         int cnt, fcnt, logval, ret;
318         const char *dir;
319         char **names, *p, *q;
320
321         /* Find the directory name. */
322         if ((ret = __log_name(dblp, 1, &p)) != 0)
323                 return (ret);
324         if ((q = __db_rpath(p)) == NULL)
325                 dir = PATH_DOT;
326         else {
327                 *q = '\0';
328                 dir = p;
329         }
330
331         /* Get the list of file names. */
332         ret = __db_dir(dblp->dbenv, dir, &names, &fcnt);
333         FREES(p);
334         if (ret != 0)
335                 return (ret);
336
337         /*
338          * Search for a valid log file name, return a value of 0 on
339          * failure.
340          */
341         *valp = 0;
342         for (cnt = fcnt, logval = 0; --cnt >= 0;)
343                 if (strncmp(names[cnt], "log.", sizeof("log.") - 1) == 0) {
344                         logval = atoi(names[cnt] + 4);
345                         if (logval != 0 &&
346                             __log_valid(dblp, dblp->lp, logval) == 0) {
347                                 *valp = logval;
348                                 break;
349                         }
350                 }
351
352         /* Discard the list. */
353         __db_dirf(dblp->dbenv, names, fcnt);
354
355         return (ret);
356 }
357
358 /*
359  * log_valid --
360  *      Validate a log file.
361  *
362  * PUBLIC: int __log_valid __P((DB_LOG *, LOG *, int));
363  */
364 int
365 __log_valid(dblp, lp, cnt)
366         DB_LOG *dblp;
367         LOG *lp;
368         int cnt;
369 {
370         LOGP persist;
371         ssize_t nw;
372         int fd, ret;
373         char *p;
374
375         if ((ret = __log_name(dblp, cnt, &p)) != 0)
376                 return (ret);
377
378         fd = -1;
379         if ((ret = __db_fdopen(p,
380             DB_RDONLY | DB_SEQUENTIAL,
381             DB_RDONLY | DB_SEQUENTIAL, 0, &fd)) != 0 ||
382             (ret = __db_lseek(fd, 0, 0, sizeof(HDR), SEEK_SET)) != 0 ||
383             (ret = __db_read(fd, &persist, sizeof(LOGP), &nw)) != 0 ||
384             nw != sizeof(LOGP)) {
385                 if (ret == 0)
386                         ret = EIO;
387                 if (fd != -1) {
388                         (void)__db_close(fd);
389                         __db_err(dblp->dbenv,
390                             "Ignoring log file: %s: %s", p, strerror(ret));
391                 }
392                 goto err;
393         }
394         (void)__db_close(fd);
395
396         if (persist.magic != DB_LOGMAGIC) {
397                 __db_err(dblp->dbenv,
398                     "Ignoring log file: %s: magic number %lx, not %lx",
399                     p, (u_long)persist.magic, (u_long)DB_LOGMAGIC);
400                 ret = EINVAL;
401                 goto err;
402         }
403         if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) {
404                 __db_err(dblp->dbenv,
405                     "Ignoring log file: %s: unsupported log version %lu",
406                     p, (u_long)persist.version);
407                 ret = EINVAL;
408                 goto err;
409         }
410
411         if (lp != NULL) {
412                 lp->persist.lg_max = persist.lg_max;
413                 lp->persist.mode = persist.mode;
414         }
415         ret = 0;
416
417 err:    FREES(p);
418         return (ret);
419 }
420
421 /*
422  * log_close --
423  *      Close a log.
424  */
425 int
426 log_close(dblp)
427         DB_LOG *dblp;
428 {
429         int ret, t_ret;
430
431         ret = 0;
432
433         /* Discard the per-thread pointer. */
434         if (dblp->mutexp != NULL) {
435                 LOCK_LOGREGION(dblp);
436                 __db_shalloc_free(dblp->addr, dblp->mutexp);
437                 UNLOCK_LOGREGION(dblp);
438         }
439
440         /* Close the region. */
441         if ((t_ret =
442             __db_rclose(dblp->dbenv, dblp->fd, dblp->maddr)) != 0 && ret == 0)
443                 ret = t_ret;
444
445         /* Close open files, release allocated memory. */
446         if (dblp->lfd != -1 && (t_ret = __db_close(dblp->lfd)) != 0 && ret == 0)
447                 ret = t_ret;
448         if (dblp->c_dbt.data != NULL)
449                 FREE(dblp->c_dbt.data, dblp->c_dbt.ulen);
450         if (dblp->c_fd != -1 &&
451             (t_ret = __db_close(dblp->c_fd)) != 0 && ret == 0)
452                 ret = t_ret;
453         if (dblp->dbentry != NULL)
454                 FREE(dblp->dbentry, (dblp->dbentry_cnt * sizeof(DB_ENTRY)));
455         if (dblp->dir != NULL)
456                 FREES(dblp->dir);
457
458         /* Free the structure. */
459         FREE(dblp, sizeof(DB_LOG));
460
461         return (ret);
462 }
463
464 /*
465  * log_unlink --
466  *      Exit a log.
467  */
468 int
469 log_unlink(path, force, dbenv)
470         const char *path;
471         int force;
472         DB_ENV *dbenv;
473 {
474         return (__db_runlink(dbenv,
475             DB_APP_LOG, path, DB_DEFAULT_LOG_FILE, force));
476 }