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