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