db

log_get.c

Go to the documentation of this file.
00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1996, 1997, 1998, 1999, 2000
00005  * Sleepycat Software. All rights reserved.
00006  */
00007 #include "config.h"
00008 
00009 #ifndef lint
00010 static const char revid[] = "$Id: log__get_8c-source.html,v 1.1 2008年06月08日 10:20:23 sebdiaz Exp $";
00011 #endif /* not lint */
00012 
00013 #ifndef NO_SYSTEM_INCLUDES
00014 #include <sys/types.h>
00015 
00016 #include <errno.h>
00017 #include <string.h>
00018 #include <unistd.h>
00019 #endif
00020 
00021 #ifdef HAVE_RPC
00022 #include "db_server.h"
00023 #endif
00024 
00025 #include "db_int.h"
00026 #include "db_page.h"
00027 #include "log.h"
00028 #include "hash.h"
00029 
00030 #ifdef HAVE_RPC
00031 #include "gen_client_ext.h"
00032 #include "rpc_client_ext.h"
00033 #endif
00034 
00035 /*
00036  * CDB_log_get --
00037  * Get a log record.
00038  */
00039 int
00040 CDB_log_get(dbenv, alsn, dbt, flags)
00041 DB_ENV *dbenv;
00042 DB_LSN *alsn;
00043 DBT *dbt;
00044 u_int32_t flags;
00045 {
00046 DB_LOG *dblp;
00047 DB_LSN saved_lsn;
00048 int ret;
00049 
00050 #ifdef HAVE_RPC
00051 if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
00052 return (__dbcl_log_get(dbenv, alsn, dbt, flags));
00053 #endif
00054 
00055 PANIC_CHECK(dbenv);
00056 ENV_REQUIRES_CONFIG(dbenv, dbenv->lg_handle, DB_INIT_LOG);
00057 
00058 /* Validate arguments. */
00059 if (flags != DB_CHECKPOINT && flags != DB_CURRENT &&
00060 flags != DB_FIRST && flags != DB_LAST &&
00061 flags != DB_NEXT && flags != DB_PREV && flags != DB_SET)
00062 return (CDB___db_ferr(dbenv, "CDB_log_get", 1));
00063 
00064 if (F_ISSET(dbenv, DB_ENV_THREAD)) {
00065 if (flags == DB_NEXT || flags == DB_PREV || flags == DB_CURRENT)
00066 return (CDB___db_ferr(dbenv, "CDB_log_get", 1));
00067 if (!F_ISSET(dbt,
00068 DB_DBT_MALLOC | DB_DBT_REALLOC | DB_DBT_USERMEM))
00069 return (CDB___db_ferr(dbenv, "threaded data", 1));
00070 }
00071 
00072 dblp = dbenv->lg_handle;
00073 R_LOCK(dbenv, &dblp->reginfo);
00074 
00075 /*
00076  * If we get one of the log's header records, repeat the operation.
00077  * This assumes that applications don't ever request the log header
00078  * records by LSN, but that seems reasonable to me.
00079  */
00080 saved_lsn = *alsn;
00081 if ((ret = CDB___log_get(dblp,
00082 alsn, dbt, flags, 0)) == 0 && alsn->offset == 0) {
00083 switch (flags) {
00084 case DB_FIRST:
00085 flags = DB_NEXT;
00086 break;
00087 case DB_LAST:
00088 flags = DB_PREV;
00089 break;
00090 }
00091 if (F_ISSET(dbt, DB_DBT_MALLOC)) {
00092 CDB___os_free(dbt->data, dbt->size);
00093 dbt->data = NULL;
00094 }
00095 ret = CDB___log_get(dblp, alsn, dbt, flags, 0);
00096 }
00097 if (ret != 0)
00098 *alsn = saved_lsn;
00099 
00100 R_UNLOCK(dbenv, &dblp->reginfo);
00101 
00102 return (ret);
00103 }
00104 
00105 /*
00106  * CDB___log_get --
00107  * Get a log record; internal version.
00108  *
00109  * PUBLIC: int CDB___log_get __P((DB_LOG *, DB_LSN *, DBT *, u_int32_t, int));
00110  */
00111 int
00112 CDB___log_get(dblp, alsn, dbt, flags, silent)
00113 DB_LOG *dblp;
00114 DB_LSN *alsn;
00115 DBT *dbt;
00116 u_int32_t flags;
00117 int silent;
00118 {
00119 DB_ENV *dbenv;
00120 DB_LSN nlsn;
00121 HDR hdr;
00122 LOG *lp;
00123 size_t len;
00124 size_t nr;
00125 int cnt, ret;
00126 char *np, *tbuf;
00127 const char *fail;
00128 void *shortp, *readp;
00129 u_int32_t offset;
00130 u_int8_t *p;
00131 
00132 lp = dblp->reginfo.primary;
00133 fail = np = tbuf = NULL;
00134 dbenv = dblp->dbenv;
00135 
00136 nlsn = dblp->c_lsn;
00137 switch (flags) {
00138 case DB_CHECKPOINT:
00139 nlsn = lp->chkpt_lsn;
00140 if (IS_ZERO_LSN(nlsn)) {
00141 /* No db_err. The caller may expect this. */
00142 ret = ENOENT;
00143 goto err2;
00144 }
00145 break;
00146 case DB_NEXT: /* Next log record. */
00147 if (!IS_ZERO_LSN(nlsn)) {
00148 /* Increment the cursor by the cursor record size. */
00149 nlsn.offset += dblp->c_len;
00150 break;
00151 }
00152 /* FALLTHROUGH */
00153 case DB_FIRST: /* Find the first log record. */
00154 /* Find the first log file. */
00155 if ((ret = CDB___log_find(dblp, 1, &cnt)) != 0)
00156 goto err2;
00157 
00158 /*
00159  * We may have only entered records in the buffer, and not
00160  * yet written a log file. If no log files were found and
00161  * there's anything in the buffer, it belongs to file 1.
00162  */
00163 if (cnt == 0)
00164 cnt = 1;
00165 
00166 nlsn.file = cnt;
00167 nlsn.offset = 0;
00168 break;
00169 case DB_CURRENT: /* Current log record. */
00170 break;
00171 case DB_PREV: /* Previous log record. */
00172 if (!IS_ZERO_LSN(nlsn)) {
00173 /* If at start-of-file, move to the previous file. */
00174 if (nlsn.offset == 0) {
00175 if (nlsn.file == 1 ||
00176 CDB___log_valid(dblp, nlsn.file - 1, 0) != 0)
00177 return (DB_NOTFOUND);
00178 
00179 --nlsn.file;
00180 nlsn.offset = dblp->c_off;
00181 } else
00182 nlsn.offset = dblp->c_off;
00183 break;
00184 }
00185 /* FALLTHROUGH */
00186 case DB_LAST: /* Last log record. */
00187 nlsn.file = lp->lsn.file;
00188 nlsn.offset = lp->lsn.offset - lp->len;
00189 break;
00190 case DB_SET: /* Set log record. */
00191 nlsn = *alsn;
00192 break;
00193 }
00194 
00195 if (0) { /* Move to the next file. */
00196 next_file: ++nlsn.file;
00197 nlsn.offset = 0;
00198 }
00199 
00200 /* Return 1 if the request is past the end of the log. */
00201 if (nlsn.file > lp->lsn.file ||
00202 (nlsn.file == lp->lsn.file && nlsn.offset >= lp->lsn.offset))
00203 return (DB_NOTFOUND);
00204 
00205 /* If we've switched files, discard the current file handle. */
00206 if (dblp->c_lsn.file != nlsn.file &&
00207 F_ISSET(&dblp->c_fh, DB_FH_VALID)) {
00208 (void)CDB___os_closehandle(&dblp->c_fh);
00209 }
00210 
00211 /* If the entire record is in the in-memory buffer, copy it out. */
00212 if (nlsn.file == lp->lsn.file && nlsn.offset >= lp->w_off) {
00213 /* Copy the header. */
00214 p = dblp->bufp + (nlsn.offset - lp->w_off);
00215 memcpy(&hdr, p, sizeof(HDR));
00216 
00217 /* Copy the record. */
00218 len = hdr.len - sizeof(HDR);
00219 if ((ret = CDB___db_retcopy(NULL, dbt, p + sizeof(HDR),
00220 len, &dblp->c_dbt.data, &dblp->c_dbt.ulen)) != 0)
00221 goto alloc_err;
00222 goto cksum;
00223 }
00224 
00225 shortp = NULL;
00226 
00227 /* Acquire a file descriptor. */
00228 if (!F_ISSET(&dblp->c_fh, DB_FH_VALID)) {
00229 if ((ret = CDB___log_name(dblp, nlsn.file,
00230 &np, &dblp->c_fh, DB_OSO_RDONLY | DB_OSO_SEQ)) != 0) {
00231 fail = np;
00232 goto err1;
00233 }
00234 CDB___os_freestr(np);
00235 np = NULL;
00236 }
00237 
00238 /* See if we've already read this */
00239 if (nlsn.file == dblp->r_file && nlsn.offset > dblp->r_off
00240 && nlsn.offset + sizeof(HDR) < dblp->r_off + dblp->r_size)
00241 goto got_header;
00242 
00243 /*
00244  * Seek to the header offset and read the header. Because the file
00245  * may be pre-allocated, we have to make sure that we're not reading
00246  * past the information in the start of the in-memory buffer.
00247  */
00248 
00249 readp = &hdr;
00250 offset = nlsn.offset;
00251 if (nlsn.file == lp->lsn.file && offset + sizeof(HDR) > lp->w_off)
00252 nr = lp->w_off - offset;
00253 else if (dblp->readbufp == NULL)
00254 nr = sizeof(HDR);
00255 else {
00256 nr = lp->buffer_size;
00257 readp = dblp->readbufp;
00258 dblp->r_file = nlsn.file;
00259 /* Going backwards. Put the current in the middle. */
00260 if (flags == DB_PREV || flags == DB_LAST) {
00261 if (offset <= lp->buffer_size/2)
00262 offset = 0;
00263 else
00264 offset = offset - lp->buffer_size/2;
00265 }
00266 if (nlsn.file == lp->lsn.file && offset + nr > lp->lsn.offset)
00267 nr = lp->lsn.offset - offset;
00268 dblp->r_off = offset;
00269 }
00270 
00271 if ((ret = CDB___os_seek(dblp->dbenv,
00272 &dblp->c_fh, 0, 0, offset, 0, DB_OS_SEEK_SET)) != 0) {
00273 fail = "seek";
00274 goto err1;
00275 }
00276 if ((ret = CDB___os_read(dblp->dbenv, &dblp->c_fh, readp, nr, &nr)) != 0) {
00277 fail = "read";
00278 goto err1;
00279 }
00280 if (nr < sizeof(HDR)) {
00281 /* If read returns EOF, try the next file. */
00282 if (nr == 0) {
00283 if (flags != DB_NEXT || nlsn.file == lp->lsn.file)
00284 goto corrupt;
00285 goto next_file;
00286 }
00287 
00288 if (dblp->readbufp != NULL)
00289 memcpy((u_int8_t *) &hdr, readp, nr);
00290 
00291 /*
00292  * If read returns a short count the rest of the record has
00293  * to be in the in-memory buffer.
00294  */
00295 if (lp->b_off < sizeof(HDR) - nr)
00296 goto corrupt;
00297 
00298 /* Get the rest of the header from the in-memory buffer. */
00299 memcpy((u_int8_t *)&hdr + nr, dblp->bufp, sizeof(HDR) - nr);
00300 
00301 if (hdr.len == 0)
00302 goto next_file;
00303 
00304 shortp = dblp->bufp + (sizeof(HDR) - nr);
00305 }
00306 
00307 else if (dblp->readbufp != NULL) {
00308 dblp->r_size = nr;
00309 got_header: memcpy((u_int8_t *)&hdr,
00310 dblp->readbufp + (nlsn.offset - dblp->r_off), sizeof(HDR));
00311 }
00312 
00313 /*
00314  * Check for buffers of 0's, that's what we usually see during recovery,
00315  * although it's certainly not something on which we can depend. Check
00316  * for impossibly large records. The malloc should fail later, but we
00317  * have customers that run mallocs that handle allocation failure as a
00318  * fatal error.
00319  */
00320 if (hdr.len == 0)
00321 goto next_file;
00322 if (hdr.len <= sizeof(HDR) || hdr.len > lp->persist.lg_max)
00323 goto corrupt;
00324 len = hdr.len - sizeof(HDR);
00325 
00326 /* If we've already moved to the in-memory buffer, fill from there. */
00327 if (shortp != NULL) {
00328 if (lp->b_off < ((u_int8_t *)shortp - dblp->bufp) + len)
00329 goto corrupt;
00330 if ((ret = CDB___db_retcopy(NULL, dbt, shortp, len,
00331 &dblp->c_dbt.data, &dblp->c_dbt.ulen)) != 0)
00332 goto alloc_err;
00333 goto cksum;
00334 }
00335 
00336 if (dblp->readbufp != NULL) {
00337 if (nlsn.offset + hdr.len < dblp->r_off + dblp->r_size) {
00338 if ((ret = CDB___db_retcopy(NULL, dbt, dblp->readbufp +
00339 (nlsn.offset - dblp->r_off) + sizeof(HDR),
00340 len, &dblp->c_dbt.data, &dblp->c_dbt.ulen)) != 0)
00341 goto alloc_err;
00342 goto cksum;
00343 } else if ((ret = CDB___os_seek(dblp->dbenv, &dblp->c_fh, 0,
00344 0, nlsn.offset + sizeof(HDR), 0, DB_OS_SEEK_SET)) != 0) {
00345 fail = "seek";
00346 goto err1;
00347 }
00348 }
00349 
00350 /*
00351  * Allocate temporary memory to hold the record.
00352  *
00353  * XXX
00354  * We're calling malloc(3) with a region locked. This isn't
00355  * a good idea.
00356  */
00357 if ((ret = CDB___os_malloc(dbenv, len, NULL, &tbuf)) != 0)
00358 goto err1;
00359 
00360 /*
00361  * Read the record into the buffer. If read returns a short count,
00362  * there was an error or the rest of the record is in the in-memory
00363  * buffer. Note, the information may be garbage if we're in recovery,
00364  * so don't read past the end of the buffer's memory.
00365  *
00366  * Because the file may be pre-allocated, we have to make sure that
00367  * we're not reading past the information in the start of the in-memory
00368  * buffer.
00369  */
00370 if (nlsn.file == lp->lsn.file &&
00371 nlsn.offset + sizeof(HDR) + len > lp->w_off)
00372 nr = lp->w_off - (nlsn.offset + sizeof(HDR));
00373 else
00374 nr = len;
00375 if ((ret = CDB___os_read(dblp->dbenv, &dblp->c_fh, tbuf, nr, &nr)) != 0) {
00376 fail = "read";
00377 goto err1;
00378 }
00379 if (len - nr > lp->buffer_size)
00380 goto corrupt;
00381 if (nr != len) {
00382 if (lp->b_off < len - nr)
00383 goto corrupt;
00384 
00385 /* Get the rest of the record from the in-memory buffer. */
00386 memcpy((u_int8_t *)tbuf + nr, dblp->bufp, len - nr);
00387 }
00388 
00389 /* Copy the record into the user's DBT. */
00390 if ((ret = CDB___db_retcopy(NULL, dbt, tbuf, len,
00391 &dblp->c_dbt.data, &dblp->c_dbt.ulen)) != 0)
00392 goto alloc_err;
00393 CDB___os_free(tbuf, 0);
00394 tbuf = NULL;
00395 
00396 cksum: if (hdr.cksum != CDB___ham_func4(dbt->data, dbt->size)) {
00397 if (!silent)
00398 CDB___db_err(dbenv, "CDB_log_get: checksum mismatch");
00399 goto corrupt;
00400 }
00401 
00402 /* Update the cursor and the return lsn. */
00403 dblp->c_off = hdr.prev;
00404 dblp->c_len = hdr.len;
00405 dblp->c_lsn = *alsn = nlsn;
00406 
00407 return (0);
00408 
00409 corrupt:/*
00410  * This is the catchall -- for some reason we didn't find enough
00411  * information or it wasn't reasonable information, and it wasn't
00412  * because a system call failed.
00413  */
00414 ret = EIO;
00415 fail = "read";
00416 
00417 err1: if (!silent) {
00418 if (fail == NULL)
00419 CDB___db_err(dbenv, "CDB_log_get: %s", CDB_db_strerror(ret));
00420 else
00421 CDB___db_err(dbenv,
00422 "CDB_log_get: %s: %s", fail, CDB_db_strerror(ret));
00423 }
00424 
00425 if (0) {
00426 alloc_err: if (!silent)
00427 CDB___db_err(dbenv,
00428 "Allocation failed: %lu", (u_long)len);
00429 }
00430 
00431 err2: if (np != NULL)
00432 CDB___os_freestr(np);
00433 if (tbuf != NULL)
00434 CDB___os_free(tbuf, 0);
00435 return (ret);
00436 }

Generated on Sun Jun 8 10:56:38 2008 for GNUmifluz by doxygen 1.5.5

AltStyle によって変換されたページ (->オリジナル) /