Version information for db-2 library.
[kopensolaris-gnu/glibc.git] / db2 / log / log_get.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *      Sleepycat Software.  All rights reserved.
6  */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)log_get.c     10.32 (Sleepycat) 5/6/98";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15
16 #include <errno.h>
17 #include <string.h>
18 #include <unistd.h>
19 #endif
20
21 #include "db_int.h"
22 #include "shqueue.h"
23 #include "db_page.h"
24 #include "log.h"
25 #include "hash.h"
26 #include "common_ext.h"
27
28 /*
29  * log_get --
30  *      Get a log record.
31  */
32 int
33 log_get(dblp, alsn, dbt, flags)
34         DB_LOG *dblp;
35         DB_LSN *alsn;
36         DBT *dbt;
37         u_int32_t flags;
38 {
39         int ret;
40
41         /* Validate arguments. */
42 #define OKFLAGS (DB_CHECKPOINT | \
43     DB_CURRENT | DB_FIRST | DB_LAST | DB_NEXT | DB_PREV | DB_SET)
44         if ((ret = __db_fchk(dblp->dbenv, "log_get", flags, OKFLAGS)) != 0)
45                 return (ret);
46         switch (flags) {
47         case DB_CHECKPOINT:
48         case DB_CURRENT:
49         case DB_FIRST:
50         case DB_LAST:
51         case DB_NEXT:
52         case DB_PREV:
53         case DB_SET:
54                 break;
55         default:
56                 return (__db_ferr(dblp->dbenv, "log_get", 1));
57         }
58
59         if (F_ISSET(dblp, DB_AM_THREAD)) {
60                 if (LF_ISSET(DB_NEXT | DB_PREV | DB_CURRENT))
61                         return (__db_ferr(dblp->dbenv, "log_get", 1));
62                 if (!F_ISSET(dbt, DB_DBT_USERMEM | DB_DBT_MALLOC))
63                         return (__db_ferr(dblp->dbenv, "threaded data", 1));
64         }
65
66         LOCK_LOGREGION(dblp);
67
68         /*
69          * If we get one of the log's header records, repeat the operation.
70          * This assumes that applications don't ever request the log header
71          * records by LSN, but that seems reasonable to me.
72          */
73         ret = __log_get(dblp, alsn, dbt, flags, 0);
74         if (ret == 0 && alsn->offset == 0) {
75                 switch (flags) {
76                 case DB_FIRST:
77                         flags = DB_NEXT;
78                         break;
79                 case DB_LAST:
80                         flags = DB_PREV;
81                         break;
82                 }
83                 ret = __log_get(dblp, alsn, dbt, flags, 0);
84         }
85
86         UNLOCK_LOGREGION(dblp);
87
88         return (ret);
89 }
90
91 /*
92  * __log_get --
93  *      Get a log record; internal version.
94  *
95  * PUBLIC: int __log_get __P((DB_LOG *, DB_LSN *, DBT *, u_int32_t, int));
96  */
97 int
98 __log_get(dblp, alsn, dbt, flags, silent)
99         DB_LOG *dblp;
100         DB_LSN *alsn;
101         DBT *dbt;
102         u_int32_t flags;
103         int silent;
104 {
105         DB_LSN nlsn;
106         HDR hdr;
107         LOG *lp;
108         size_t len;
109         ssize_t nr;
110         int cnt, ret;
111         char *np, *tbuf;
112         const char *fail;
113         void *p, *shortp;
114
115         lp = dblp->lp;
116         fail = np = tbuf = NULL;
117
118         nlsn = dblp->c_lsn;
119         switch (flags) {
120         case DB_CHECKPOINT:
121                 nlsn = lp->chkpt_lsn;
122                 if (IS_ZERO_LSN(nlsn)) {
123                         __db_err(dblp->dbenv,
124         "log_get: unable to find checkpoint record: no checkpoint set.");
125                         ret = ENOENT;
126                         goto err2;
127                 }
128                 break;
129         case DB_NEXT:                           /* Next log record. */
130                 if (!IS_ZERO_LSN(nlsn)) {
131                         /* Increment the cursor by the cursor record size. */
132                         nlsn.offset += dblp->c_len;
133                         break;
134                 }
135                 /* FALLTHROUGH */
136         case DB_FIRST:                          /* Find the first log record. */
137                 /* Find the first log file. */
138                 if ((ret = __log_find(dblp, 1, &cnt)) != 0)
139                         goto err2;
140
141                 /*
142                  * We may have only entered records in the buffer, and not
143                  * yet written a log file.  If no log files were found and
144                  * there's anything in the buffer, it belongs to file 1.
145                  */
146                 if (cnt == 0)
147                         cnt = 1;
148
149                 nlsn.file = cnt;
150                 nlsn.offset = 0;
151                 break;
152         case DB_CURRENT:                        /* Current log record. */
153                 break;
154         case DB_PREV:                           /* Previous log record. */
155                 if (!IS_ZERO_LSN(nlsn)) {
156                         /* If at start-of-file, move to the previous file. */
157                         if (nlsn.offset == 0) {
158                                 if (nlsn.file == 1 ||
159                                     __log_valid(dblp, NULL, nlsn.file - 1) != 0)
160                                         return (DB_NOTFOUND);
161
162                                 --nlsn.file;
163                                 nlsn.offset = dblp->c_off;
164                         } else
165                                 nlsn.offset = dblp->c_off;
166                         break;
167                 }
168                 /* FALLTHROUGH */
169         case DB_LAST:                           /* Last log record. */
170                 nlsn.file = lp->lsn.file;
171                 nlsn.offset = lp->lsn.offset - lp->len;
172                 break;
173         case DB_SET:                            /* Set log record. */
174                 nlsn = *alsn;
175                 break;
176         }
177
178 retry:
179         /* Return 1 if the request is past end-of-file. */
180         if (nlsn.file > lp->lsn.file ||
181             (nlsn.file == lp->lsn.file && nlsn.offset >= lp->lsn.offset))
182                 return (DB_NOTFOUND);
183
184         /* If we've switched files, discard the current fd. */
185         if (dblp->c_lsn.file != nlsn.file && dblp->c_fd != -1) {
186                 (void)__db_close(dblp->c_fd);
187                 dblp->c_fd = -1;
188         }
189
190         /* If the entire record is in the in-memory buffer, copy it out. */
191         if (nlsn.file == lp->lsn.file && nlsn.offset >= lp->w_off) {
192                 /* Copy the header. */
193                 p = lp->buf + (nlsn.offset - lp->w_off);
194                 memcpy(&hdr, p, sizeof(HDR));
195
196                 /* Copy the record. */
197                 len = hdr.len - sizeof(HDR);
198                 if ((ret = __db_retcopy(dbt, (u_int8_t *)p + sizeof(HDR),
199                     len, &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
200                         goto err1;
201                 goto cksum;
202         }
203
204         /* Acquire a file descriptor. */
205         if (dblp->c_fd == -1) {
206                 if ((ret = __log_name(dblp, nlsn.file, &np)) != 0)
207                         goto err1;
208                 if ((ret = __db_open(np, DB_RDONLY | DB_SEQUENTIAL,
209                     DB_RDONLY | DB_SEQUENTIAL, 0, &dblp->c_fd)) != 0) {
210                         fail = np;
211                         goto err1;
212                 }
213                 __db_free(np);
214                 np = NULL;
215         }
216
217         /* Seek to the header offset and read the header. */
218         if ((ret =
219             __db_seek(dblp->c_fd, 0, 0, nlsn.offset, 0, SEEK_SET)) != 0) {
220                 fail = "seek";
221                 goto err1;
222         }
223         if ((ret = __db_read(dblp->c_fd, &hdr, sizeof(HDR), &nr)) != 0) {
224                 fail = "read";
225                 goto err1;
226         }
227         if (nr == sizeof(HDR))
228                 shortp = NULL;
229         else {
230                 /* If read returns EOF, try the next file. */
231                 if (nr == 0) {
232                         if (flags != DB_NEXT || nlsn.file == lp->lsn.file)
233                                 goto corrupt;
234
235                         /* Move to the next file. */
236                         ++nlsn.file;
237                         nlsn.offset = 0;
238                         goto retry;
239                 }
240
241                 /*
242                  * If read returns a short count the rest of the record has
243                  * to be in the in-memory buffer.
244                  */
245                 if (lp->b_off < sizeof(HDR) - nr)
246                         goto corrupt;
247
248                 /* Get the rest of the header from the in-memory buffer. */
249                 memcpy((u_int8_t *)&hdr + nr, lp->buf, sizeof(HDR) - nr);
250                 shortp = lp->buf + (sizeof(HDR) - nr);
251         }
252
253         /*
254          * Check for buffers of 0's, that's what we usually see during
255          * recovery, although it's certainly not something on which we
256          * can depend.
257          */
258         if (hdr.len <= sizeof(HDR))
259                 goto corrupt;
260         len = hdr.len - sizeof(HDR);
261
262         /* If we've already moved to the in-memory buffer, fill from there. */
263         if (shortp != NULL) {
264                 if (lp->b_off < ((u_int8_t *)shortp - lp->buf) + len)
265                         goto corrupt;
266                 if ((ret = __db_retcopy(dbt, shortp, len,
267                     &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
268                         goto err1;
269                 goto cksum;
270         }
271
272         /*
273          * Allocate temporary memory to hold the record.
274          *
275          * XXX
276          * We're calling malloc(3) with a region locked.  This isn't
277          * a good idea.
278          */
279         if ((tbuf = (char *)__db_malloc(len)) == NULL) {
280                 ret = ENOMEM;
281                 goto err1;
282         }
283
284         /*
285          * Read the record into the buffer.  If read returns a short count,
286          * there was an error or the rest of the record is in the in-memory
287          * buffer.  Note, the information may be garbage if we're in recovery,
288          * so don't read past the end of the buffer's memory.
289          */
290         if ((ret = __db_read(dblp->c_fd, tbuf, len, &nr)) != 0) {
291                 fail = "read";
292                 goto err1;
293         }
294         if (len - nr > sizeof(lp->buf))
295                 goto corrupt;
296         if (nr != (ssize_t)len) {
297                 if (lp->b_off < len - nr)
298                         goto corrupt;
299
300                 /* Get the rest of the record from the in-memory buffer. */
301                 memcpy((u_int8_t *)tbuf + nr, lp->buf, len - nr);
302         }
303
304         /* Copy the record into the user's DBT. */
305         if ((ret = __db_retcopy(dbt, tbuf, len,
306             &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
307                 goto err1;
308         __db_free(tbuf);
309         tbuf = NULL;
310
311 cksum:  if (hdr.cksum != __ham_func4(dbt->data, dbt->size)) {
312                 if (!silent)
313                         __db_err(dblp->dbenv, "log_get: checksum mismatch");
314                 goto corrupt;
315         }
316
317         /* Update the cursor and the return lsn. */
318         dblp->c_off = hdr.prev;
319         dblp->c_len = hdr.len;
320         dblp->c_lsn = *alsn = nlsn;
321
322         return (0);
323
324 corrupt:/*
325          * This is the catchall -- for some reason we didn't find enough
326          * information or it wasn't reasonable information, and it wasn't
327          * because a system call failed.
328          */
329         ret = EIO;
330         fail = "read";
331
332 err1:   if (!silent)
333                 if (fail == NULL)
334                         __db_err(dblp->dbenv, "log_get: %s", strerror(ret));
335                 else
336                         __db_err(dblp->dbenv,
337                             "log_get: %s: %s", fail, strerror(ret));
338 err2:   if (np != NULL)
339                 __db_free(np);
340         if (tbuf != NULL)
341                 __db_free(tbuf);
342         return (ret);
343 }