Update.
[glibc.git] / db2 / log / log_get.c
blobed35d57f820c8f15399ff18e5c809b0226a9c3a3
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
6 */
7 #include "config.h"
9 #ifndef lint
10 static const char sccsid[] = "@(#)log_get.c 10.21 (Sleepycat) 10/25/97";
11 #endif /* not lint */
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #endif
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"
31 * log_get --
32 * Get a log record.
34 int
35 log_get(dblp, alsn, dbt, flags)
36 DB_LOG *dblp;
37 DB_LSN *alsn;
38 DBT *dbt;
39 int flags;
41 LOG *lp;
42 int ret;
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));
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));
72 lp = dblp->lp;
74 LOCK_LOGREGION(dblp);
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.
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;
91 ret = __log_get(dblp, alsn, dbt, flags, 0);
94 UNLOCK_LOGREGION(dblp);
96 return (ret);
100 * __log_get --
101 * Get a log record; internal version.
103 * PUBLIC: int __log_get __P((DB_LOG *, DB_LSN *, DBT *, int, int));
106 __log_get(dblp, alsn, dbt, flags, silent)
107 DB_LOG *dblp;
108 DB_LSN *alsn;
109 DBT *dbt;
110 int flags, silent;
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;
122 lp = dblp->lp;
123 fail = np = tbuf = NULL;
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;
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;
142 /* FALLTHROUGH */
143 case DB_FIRST: /* Find the first log record. */
145 * Find any log file. Note, we may have only entered records
146 * in the buffer, and not yet written a log file.
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;
154 /* If there's anything in the buffer, it belongs to file 1. */
155 if (cnt == 0)
156 cnt = 1;
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;
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 __log_valid(dblp, NULL, nlsn.file - 1) != 0)
175 return (DB_NOTFOUND);
177 --nlsn.file;
178 nlsn.offset = dblp->c_off;
179 } else
180 nlsn.offset = dblp->c_off;
181 break;
183 /* FALLTHROUGH */
184 case DB_LAST: /* Last log record. */
185 nlsn.file = lp->lsn.file;
186 nlsn.offset = lp->lsn.offset - lp->len;
187 break;
188 case DB_SET: /* Set log record. */
189 nlsn = *alsn;
190 break;
193 retry:
194 /* Return 1 if the request is past end-of-file. */
195 if (nlsn.file > lp->lsn.file ||
196 (nlsn.file == lp->lsn.file && nlsn.offset >= lp->lsn.offset))
197 return (DB_NOTFOUND);
199 /* If we've switched files, discard the current fd. */
200 if (dblp->c_lsn.file != nlsn.file && dblp->c_fd != -1) {
201 (void)__db_close(dblp->c_fd);
202 dblp->c_fd = -1;
205 /* If the entire record is in the in-memory buffer, copy it out. */
206 if (nlsn.file == lp->lsn.file && nlsn.offset >= lp->w_off) {
207 /* Copy the header. */
208 p = lp->buf + (nlsn.offset - lp->w_off);
209 memcpy(&hdr, p, sizeof(HDR));
211 /* Copy the record. */
212 len = hdr.len - sizeof(HDR);
213 if ((ret = __db_retcopy(dbt, (u_int8_t *)p + sizeof(HDR),
214 len, &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
215 goto err1;
216 goto cksum;
219 /* Acquire a file descriptor. */
220 if (dblp->c_fd == -1) {
221 if ((ret = __log_name(dblp, nlsn.file, &np)) != 0)
222 goto err1;
223 if ((ret = __db_open(np, DB_RDONLY | DB_SEQUENTIAL,
224 DB_RDONLY | DB_SEQUENTIAL, 0, &dblp->c_fd)) != 0) {
225 fail = np;
226 goto err1;
228 __db_free(np);
229 np = NULL;
232 /* Seek to the header offset and read the header. */
233 if ((ret = __db_seek(dblp->c_fd, 0, 0, nlsn.offset, SEEK_SET)) != 0) {
234 fail = "seek";
235 goto err1;
237 if ((ret = __db_read(dblp->c_fd, &hdr, sizeof(HDR), &nr)) != 0) {
238 fail = "read";
239 goto err1;
241 if (nr == sizeof(HDR))
242 shortp = NULL;
243 else {
244 /* If read returns EOF, try the next file. */
245 if (nr == 0) {
246 if (flags != DB_NEXT || nlsn.file == lp->lsn.file)
247 goto corrupt;
249 /* Move to the next file. */
250 ++nlsn.file;
251 nlsn.offset = 0;
252 goto retry;
256 * If read returns a short count the rest of the record has
257 * to be in the in-memory buffer.
259 if (lp->b_off < sizeof(HDR) - nr)
260 goto corrupt;
262 /* Get the rest of the header from the in-memory buffer. */
263 memcpy((u_int8_t *)&hdr + nr, lp->buf, sizeof(HDR) - nr);
264 shortp = lp->buf + (sizeof(HDR) - nr);
268 * Check for buffers of 0's, that's what we usually see during
269 * recovery, although it's certainly not something on which we
270 * can depend.
272 if (hdr.len <= sizeof(HDR))
273 goto corrupt;
274 len = hdr.len - sizeof(HDR);
276 /* If we've already moved to the in-memory buffer, fill from there. */
277 if (shortp != NULL) {
278 if (lp->b_off < ((u_int8_t *)shortp - lp->buf) + len)
279 goto corrupt;
280 if ((ret = __db_retcopy(dbt, shortp, len,
281 &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
282 goto err1;
283 goto cksum;
286 /* Allocate temporary memory to hold the record. */
287 if ((tbuf = (char *)__db_malloc(len)) == NULL) {
288 ret = ENOMEM;
289 goto err1;
293 * Read the record into the buffer. If read returns a short count,
294 * there was an error or the rest of the record is in the in-memory
295 * buffer. Note, the information may be garbage if we're in recovery,
296 * so don't read past the end of the buffer's memory.
298 if ((ret = __db_read(dblp->c_fd, tbuf, len, &nr)) != 0) {
299 fail = "read";
300 goto err1;
302 if (len - nr > sizeof(lp->buf))
303 goto corrupt;
304 if (nr != (ssize_t)len) {
305 if (lp->b_off < len - nr)
306 goto corrupt;
308 /* Get the rest of the record from the in-memory buffer. */
309 memcpy((u_int8_t *)tbuf + nr, lp->buf, len - nr);
312 /* Copy the record into the user's DBT. */
313 if ((ret = __db_retcopy(dbt, tbuf, len,
314 &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
315 goto err1;
316 __db_free(tbuf);
317 tbuf = NULL;
319 cksum: if (hdr.cksum != __ham_func4(dbt->data, dbt->size)) {
320 if (!silent)
321 __db_err(dblp->dbenv, "log_get: checksum mismatch");
322 goto corrupt;
325 /* Update the cursor and the return lsn. */
326 dblp->c_off = hdr.prev;
327 dblp->c_len = hdr.len;
328 dblp->c_lsn = *alsn = nlsn;
330 return (0);
332 corrupt:/*
333 * This is the catchall -- for some reason we didn't find enough
334 * information or it wasn't reasonable information, and it wasn't
335 * because a system call failed.
337 ret = EIO;
338 fail = "read";
340 err1: if (!silent)
341 if (fail == NULL)
342 __db_err(dblp->dbenv, "log_get: %s", strerror(ret));
343 else
344 __db_err(dblp->dbenv,
345 "log_get: %s: %s", fail, strerror(ret));
346 err2: if (np != NULL)
347 __db_free(np);
348 if (tbuf != NULL)
349 __db_free(tbuf);
350 return (ret);