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 }