d3e5183588b76e64cf0b05f564720545a41e38b3
[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.25 (Sleepycat) 8/27/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_ENV *, 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         dblp->dbenv = dbenv;
74         dblp->lfd = -1;
75         ZERO_LSN(dblp->c_lsn);
76         dblp->c_fd = -1;
77         if (LF_ISSET(DB_THREAD)) {
78                 F_SET(dblp, DB_AM_THREAD);
79                 (void)__db_mutex_init(&dblp->mutex, -1);
80         }
81
82         /*
83          * The log region isn't fixed size because we store the registered
84          * file names there.  Make it fairly large so that we don't have to
85          * grow it.
86          */
87         len = 30 * 1024;
88
89         /* Map in the region. */
90         retry_cnt = newregion = 0;
91 retry:  if (LF_ISSET(DB_CREATE)) {
92                 ret = __db_rcreate(dbenv, DB_APP_LOG, path,
93                     DB_DEFAULT_LOG_FILE, mode, len, &fd, &dblp->maddr);
94                 if (ret == 0) {
95                         /* Put the LOG structure first in the region. */
96                         lp = dblp->maddr;
97
98                         /* Initialize the rest of the region as free space. */
99                         dblp->addr = (u_int8_t *)dblp->maddr + sizeof(LOG);
100                         __db_shalloc_init(dblp->addr, len - sizeof(LOG));
101
102                         /* Initialize the LOG structure. */
103                         lp->persist.lg_max = dbenv == NULL ? 0 : dbenv->lg_max;
104                         if (lp->persist.lg_max == 0)
105                                 lp->persist.lg_max = DEFAULT_MAX;
106                         lp->persist.magic = DB_LOGMAGIC;
107                         lp->persist.version = DB_LOGVERSION;
108                         lp->persist.mode = mode;
109                         SH_TAILQ_INIT(&lp->fq);
110
111                         /* Initialize LOG LSNs. */
112                         lp->lsn.file = 1;
113                         lp->lsn.offset = 0;
114
115                         newregion = 1;
116                 } else if (ret != EEXIST)
117                         return (ret);
118         }
119
120         /* If we didn't or couldn't create the region, try and join it. */
121         if (!newregion &&
122             (ret = __db_ropen(dbenv, DB_APP_LOG,
123             path, DB_DEFAULT_LOG_FILE, 0, &fd, &dblp->maddr)) != 0) {
124                 /*
125                  * If we fail because the file isn't available, wait a
126                  * second and try again.
127                  */
128                 if (ret == EAGAIN && ++retry_cnt < 3) {
129                         (void)__db_sleep(1, 0);
130                         goto retry;
131                 }
132                 return (ret);
133         }
134
135         /* Set up the common information. */
136         dblp->lp = dblp->maddr;
137         dblp->addr = (u_int8_t *)dblp->maddr + sizeof(LOG);
138         dblp->fd = fd;
139
140         /*
141          * If doing recovery, try and recover any previous log files
142          * before releasing the lock.
143          */
144         if (newregion) {
145                 if ((ret = __log_recover(dbenv, dblp)) != 0) {
146                         log_unlink(path, 1, dbenv);
147                         return (ret);
148                 }
149                 UNLOCK_LOGREGION(dblp);
150         }
151         *lpp = dblp;
152         return (0);
153 }
154
155 /*
156  * __log_recover --
157  *      Recover a log.
158  */
159 static int
160 __log_recover(dbenv, dblp)
161         DB_ENV *dbenv;
162         DB_LOG *dblp;
163 {
164         DBT dbt;
165         DB_LSN lsn;
166         LOG *lp;
167         u_int32_t chk;
168         int cnt, found_checkpoint, ret;
169
170         lp = dblp->lp;
171
172         /*
173          * Find a log file.  If none exist, we simply return, leaving
174          * everything initialized to a new log.
175          */
176         if ((ret = __log_find(dbenv, lp, &cnt)) != 0)
177                 return (ret);
178         if (cnt == 0)
179                 return (0);
180
181         /* We have a log file name, find the last one. */
182         while (cnt < MAXLFNAME)
183                 if (__log_valid(dbenv, lp, ++cnt) != 0) {
184                         --cnt;
185                         break;
186                 }
187
188         /*
189          * We have the last useful log file and we've loaded any persistent
190          * information.  Pretend that the log is larger than it can possibly
191          * be, and read this file, looking for a checkpoint and its end.
192          */
193         dblp->c_lsn.file = cnt;
194         dblp->c_lsn.offset = 0;
195         lsn = dblp->c_lsn;
196         lp->lsn.file = cnt + 1;
197         lp->lsn.offset = 0;
198
199         /* Set the cursor.  Shouldn't fail, leave error messages on. */
200         memset(&dbt, 0, sizeof(dbt));
201         if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
202                 return (ret);
203
204         /*
205          * Read to the end of the file, saving checkpoints.  This will fail
206          * at some point, so turn off error messages.
207          */
208         found_checkpoint = 0;
209         while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 1) == 0) {
210                 if (dbt.size < sizeof(u_int32_t))
211                         continue;
212                 memcpy(&chk, dbt.data, sizeof(u_int32_t));
213                 if (chk == DB_txn_ckp) {
214                         lp->c_lsn = lsn;
215                         found_checkpoint = 1;
216                 }
217         }
218
219         /*
220          * We know where the end of the log is.  Since that record is on disk,
221          * it's also the last-synced LSN.
222          */
223         lp->lsn = lsn;
224         lp->lsn.offset += dblp->c_len;
225         lp->s_lsn = lp->lsn;
226
227         /* Set up the current buffer information, too. */
228         lp->len = dblp->c_len;
229         lp->b_off = 0;
230         lp->w_off = lp->lsn.offset;
231
232         /*
233          * It's possible that we didn't find a checkpoint because there wasn't
234          * one in the last log file.  Start searching.
235          */
236         while (!found_checkpoint && cnt > 1) {
237                 dblp->c_lsn.file = --cnt;
238                 dblp->c_lsn.offset = 0;
239                 lsn = dblp->c_lsn;
240
241                 /* Set the cursor.  Shouldn't fail, leave error messages on. */
242                 if ((ret = __log_get(dblp, &lsn, &dbt, DB_SET, 0)) != 0)
243                         return (ret);
244
245                 /*
246                  * Read to the end of the file, saving checkpoints.  Shouldn't
247                  * fail, leave error messages on.
248                  */
249                 while (__log_get(dblp, &lsn, &dbt, DB_NEXT, 0) == 0) {
250                         if (dbt.size < sizeof(u_int32_t))
251                                 continue;
252                         memcpy(&chk, dbt.data, sizeof(u_int32_t));
253                         if (chk == DB_txn_ckp) {
254                                 lp->c_lsn = lsn;
255                                 found_checkpoint = 1;
256                         }
257                 }
258         }
259
260         /* If we never find a checkpoint, that's okay, just 0 it out. */
261         if (!found_checkpoint) {
262                 lp->c_lsn.file = 1;
263                 lp->c_lsn.offset = 0;
264         }
265
266         __db_err(dbenv,
267             "Recovering the log: last valid LSN: file: %lu offset %lu",
268             (u_long)lp->lsn.file, (u_long)lp->lsn.offset);
269
270         /* Reset the cursor.  */
271         ZERO_LSN(dblp->c_lsn);
272
273         return (0);
274 }
275
276 /*
277  * __log_find --
278  *      Try to find a log file.
279  *
280  * PUBLIC: int __log_find __P((DB_ENV *, LOG *, int *));
281  */
282 int
283 __log_find(dbenv, lp, valp)
284         DB_ENV *dbenv;
285         LOG *lp;
286         int *valp;
287 {
288         int cnt, fcnt, logval, ret;
289         const char *dir;
290         char **names, *p, *q;
291
292         /* Find the directory name. */
293         if ((ret = __log_name(dbenv, 1, &p)) != 0)
294                 return (ret);
295         if ((q = __db_rpath(p)) == NULL)
296                 dir = PATH_DOT;
297         else {
298                 *q = '\0';
299                 dir = p;
300         }
301
302         /* Get the list of file names. */
303         ret = __db_dir(dbenv, dir, &names, &fcnt);
304         FREES(p);
305         if (ret != 0)
306                 return (ret);
307
308         /*
309          * Search for a valid log file name, return a value of 0 on
310          * failure.
311          */
312         *valp = 0;
313         for (cnt = fcnt, logval = 0; --cnt >= 0;)
314                 if (strncmp(names[cnt], "log.", sizeof("log.") - 1) == 0) {
315                         logval = atoi(names[cnt] + 4);
316                         if (logval != 0 &&
317                             __log_valid(dbenv, lp, logval) == 0) {
318                                 *valp = logval;
319                                 break;
320                         }
321                 }
322
323         /* Discard the list. */
324         __db_dirf(dbenv, names, fcnt);
325
326         return (ret);
327 }
328
329 /*
330  * log_valid --
331  *      Validate a log file.
332  *
333  * PUBLIC: int __log_valid __P((DB_ENV *, LOG *, int));
334  */
335 int
336 __log_valid(dbenv, lp, cnt)
337         DB_ENV *dbenv;
338         LOG *lp;
339         int cnt;
340 {
341         LOGP persist;
342         ssize_t nw;
343         int fd, ret;
344         char *p;
345
346         if ((ret = __log_name(dbenv, cnt, &p)) != 0)
347                 return (ret);
348
349         fd = -1;
350         if ((ret = __db_fdopen(p,
351             DB_RDONLY | DB_SEQUENTIAL,
352             DB_RDONLY | DB_SEQUENTIAL, 0, &fd)) != 0 ||
353             (ret = __db_lseek(fd, 0, 0, sizeof(HDR), SEEK_SET)) != 0 ||
354             (ret = __db_read(fd, &persist, sizeof(LOGP), &nw)) != 0 ||
355             nw != sizeof(LOGP)) {
356                 if (ret == 0)
357                         ret = EIO;
358                 if (fd != -1) {
359                         (void)__db_close(fd);
360                         __db_err(dbenv,
361                             "Ignoring log file: %s: %s", p, strerror(ret));
362                 }
363                 goto err;
364         }
365         (void)__db_close(fd);
366
367         if (persist.magic != DB_LOGMAGIC) {
368                 __db_err(dbenv,
369                     "Ignoring log file: %s: magic number %lx, not %lx",
370                     p, (u_long)persist.magic, (u_long)DB_LOGMAGIC);
371                 ret = EINVAL;
372                 goto err;
373         }
374         if (persist.version < DB_LOGOLDVER || persist.version > DB_LOGVERSION) {
375                 __db_err(dbenv,
376                     "Ignoring log file: %s: unsupported log version %lu",
377                     p, (u_long)persist.version);
378                 ret = EINVAL;
379                 goto err;
380         }
381
382         if (lp != NULL) {
383                 lp->persist.lg_max = persist.lg_max;
384                 lp->persist.mode = persist.mode;
385         }
386         ret = 0;
387
388 err:    FREES(p);
389         return (ret);
390 }
391
392 /*
393  * log_close --
394  *      Close a log.
395  */
396 int
397 log_close(dblp)
398         DB_LOG *dblp;
399 {
400         int ret, t_ret;
401
402         ret = 0;
403
404         /* Close the region. */
405         if ((t_ret =
406             __db_rclose(dblp->dbenv, dblp->fd, dblp->maddr)) != 0 && ret == 0)
407                 ret = t_ret;
408
409         /* Close open files, release allocated memory. */
410         if (dblp->lfd != -1 && (t_ret = __db_close(dblp->lfd)) != 0 && ret == 0)
411                 ret = t_ret;
412         if (dblp->c_dbt.data != NULL)
413                 FREE(dblp->c_dbt.data, dblp->c_dbt.ulen);
414         if (dblp->c_fd != -1 &&
415             (t_ret = __db_close(dblp->c_fd)) != 0 && ret == 0)
416                 ret = t_ret;
417
418         /* Free the structure. */
419         if (dblp->dbentry != NULL)
420                 FREE(dblp->dbentry, (dblp->dbentry_cnt * sizeof(DB_ENTRY)));
421         FREE(dblp, sizeof(DB_LOG));
422
423         return (ret);
424 }
425
426 /*
427  * log_unlink --
428  *      Exit a log.
429  */
430 int
431 log_unlink(path, force, dbenv)
432         const char *path;
433         int force;
434         DB_ENV *dbenv;
435 {
436         return (__db_runlink(dbenv,
437             DB_APP_LOG, path, DB_DEFAULT_LOG_FILE, force));
438 }