2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid[] = "@(#)log_get.c 10.32 (Sleepycat) 5/6/98";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
26 #include "common_ext.h"
33 log_get(dblp, alsn, dbt, flags)
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)
56 return (__db_ferr(dblp->dbenv, "log_get", 1));
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));
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.
73 ret = __log_get(dblp, alsn, dbt, flags, 0);
74 if (ret == 0 && alsn->offset == 0) {
83 ret = __log_get(dblp, alsn, dbt, flags, 0);
86 UNLOCK_LOGREGION(dblp);
93 * Get a log record; internal version.
95 * PUBLIC: int __log_get __P((DB_LOG *, DB_LSN *, DBT *, u_int32_t, int));
98 __log_get(dblp, alsn, dbt, flags, silent)
116 fail = np = tbuf = NULL;
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.");
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;
136 case DB_FIRST: /* Find the first log record. */
137 /* Find the first log file. */
138 if ((ret = __log_find(dblp, 1, &cnt)) != 0)
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.
152 case DB_CURRENT: /* Current log record. */
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);
163 nlsn.offset = dblp->c_off;
165 nlsn.offset = dblp->c_off;
169 case DB_LAST: /* Last log record. */
170 nlsn.file = lp->lsn.file;
171 nlsn.offset = lp->lsn.offset - lp->len;
173 case DB_SET: /* Set log record. */
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);
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);
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));
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)
204 /* Acquire a file descriptor. */
205 if (dblp->c_fd == -1) {
206 if ((ret = __log_name(dblp, nlsn.file, &np)) != 0)
208 if ((ret = __db_open(np, DB_RDONLY | DB_SEQUENTIAL,
209 DB_RDONLY | DB_SEQUENTIAL, 0, &dblp->c_fd)) != 0) {
217 /* Seek to the header offset and read the header. */
219 __db_seek(dblp->c_fd, 0, 0, nlsn.offset, 0, SEEK_SET)) != 0) {
223 if ((ret = __db_read(dblp->c_fd, &hdr, sizeof(HDR), &nr)) != 0) {
227 if (nr == sizeof(HDR))
230 /* If read returns EOF, try the next file. */
232 if (flags != DB_NEXT || nlsn.file == lp->lsn.file)
235 /* Move to the next file. */
242 * If read returns a short count the rest of the record has
243 * to be in the in-memory buffer.
245 if (lp->b_off < sizeof(HDR) - nr)
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);
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
258 if (hdr.len <= sizeof(HDR))
260 len = hdr.len - sizeof(HDR);
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)
266 if ((ret = __db_retcopy(dbt, shortp, len,
267 &dblp->c_dbt.data, &dblp->c_dbt.ulen, NULL)) != 0)
273 * Allocate temporary memory to hold the record.
276 * We're calling malloc(3) with a region locked. This isn't
279 if ((tbuf = (char *)__db_malloc(len)) == NULL) {
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.
290 if ((ret = __db_read(dblp->c_fd, tbuf, len, &nr)) != 0) {
294 if (len - nr > sizeof(lp->buf))
296 if (nr != (ssize_t)len) {
297 if (lp->b_off < len - nr)
300 /* Get the rest of the record from the in-memory buffer. */
301 memcpy((u_int8_t *)tbuf + nr, lp->buf, len - nr);
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)
311 cksum: if (hdr.cksum != __ham_func4(dbt->data, dbt->size)) {
313 __db_err(dblp->dbenv, "log_get: checksum mismatch");
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;
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.
334 __db_err(dblp->dbenv, "log_get: %s", strerror(ret));
336 __db_err(dblp->dbenv,
337 "log_get: %s: %s", fail, strerror(ret));
338 err2: if (np != NULL)