Patches for DB version 2.6.4

  1. The memory pool fails to correct for 0-based page numbers when calculating the end of the database file. The most likely symptom is that the db_dump utility will fail, and no data corruption can occur.
  2. Apply the following patch to the db-2.6.4 mp/mp_fopen.c file.  
    *** mp/mp_fopen.c.orig	Wed Dec 31 19:00:00 1969
    --- mp/mp_fopen.c	Mon Dec 21 11:12:23 1998
    ***************
    *** 177,182 ****
    --- 177,186 ----
      
      		last_pgno = mbytes * (MEGABYTE / pagesize);
      		last_pgno += bytes / pagesize;
    + 
    + 		/* Correction: page numbers are zero-based, not 1-based. */
    + 		if (last_pgno != 0)
    + 			--last_pgno;
      
      		/*
      		 * Get the file id if we weren't given one.  Generated file id's
    

  3. The DB->put call, with the DB_NOOVERWRITE flag set, where the key already exists in the tree, will overwrite the caller's data DBT with the value already stored in the tree.
  4. Apply the following patch to the db-2.6.4 db/db_am.c file.  
    *** db/db_am.c.orig	Wed Dec 31 19:00:00 1969
    --- db/db_am.c	Sat Jan  2 15:46:42 1999
    ***************
    *** 347,352 ****
    --- 347,353 ----
      	u_int32_t flags;
      {
      	DBC *dbc;
    + 	DBT tdata;
      	int ret, t_ret;
      
      	DB_PANIC_CHECK(dbp);
    ***************
    *** 360,369 ****
      
      	DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags);
      
    ! 	if (flags == DB_NOOVERWRITE &&
    ! 	    (ret = dbc->c_get(dbc, key, data, DB_SET | DB_RMW)) == 0)
    ! 		ret = DB_KEYEXIST;
    ! 	else
      		ret = dbc->c_put(dbc, key, data, DB_KEYLAST);
      
      	if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
    --- 361,380 ----
      
      	DEBUG_LWRITE(dbc, txn, "__db_put", key, data, flags);
      
    ! 	if (flags == DB_NOOVERWRITE) {
    ! 		/*
    ! 		 * Set DB_DBT_USERMEM, this might be a threaded application and
    ! 		 * the flags checking will catch us.  We don't want the actual
    ! 		 * data, so request a partial of length 0.
    ! 		 */
    ! 		memset(&tdata, 0, sizeof(tdata));
    ! 		F_SET(&tdata, DB_DBT_USERMEM | DB_DBT_PARTIAL);
    ! 		if ((ret = dbc->c_get(dbc, key, &tdata, DB_SET | DB_RMW)) == 0)
    ! 			ret = DB_KEYEXIST;
    ! 		else
    ! 			ret = 0;
    ! 	}
    ! 	if (ret == 0)
      		ret = dbc->c_put(dbc, key, data, DB_KEYLAST);
      
      	if ((t_ret = __db_c_close(dbc)) != 0 && ret == 0)
    

  5. In the Berkeley DB Concurrent Access Methods Product (i.e., the DB_INIT_CDB flag to the db_appinit interface), it was possible for cursor close to not correctly release all acquired locks in the B+tree access method.
  6. Apply the following patch to the db-2.6.4 db/db_am.c file.  
    *** db/db_am.c.orig	Sun Jan  3 13:12:26 1999
    --- db/db_am.c	Tue Jan  5 09:39:30 1999
    ***************
    *** 173,183 ****
      
      	ret = 0;
      
    ! 	/* Release the lock. */
    ! 	if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) {
    ! 		ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock);
    ! 		dbc->mylock = LOCK_INVALID;
    ! 	}
      
      	/* Remove the cursor from the active queue. */
      	DB_THREAD_LOCK(dbp);
    --- 173,183 ----
      
      	ret = 0;
      
    ! 	/*
    ! 	 * We cannot release the lock until after we've called the
    ! 	 * access method specific routine, since btrees may have pending
    ! 	 * deletes.
    ! 	 */
      
      	/* Remove the cursor from the active queue. */
      	DB_THREAD_LOCK(dbp);
    ***************
    *** 188,193 ****
    --- 188,199 ----
      	if ((t_ret = dbc->c_am_close(dbc)) != 0 && ret == 0)
      		t_ret = ret;
      
    + 	/* Release the lock. */
    + 	if (F_ISSET(dbc->dbp, DB_AM_CDB) && dbc->mylock != LOCK_INVALID) {
    + 		ret = lock_put(dbc->dbp->dbenv->lk_info, dbc->mylock);
    + 		dbc->mylock = LOCK_INVALID;
    + 	}
    + 
      	/* Clean up the cursor. */
      	dbc->flags = 0;
      
    

  7. In Btree databases with large numbers of duplicates, it was possible to incorrectly discard a page lock, leading to spurious error messages from the underlying shared memory buffer cache.
  8. Apply the following patch to the db-2.6.4 btree/bt_cursor.c file.  
    *** btree/bt_cursor.c.orig	Wed Dec 16 20:20:18 1998
    --- btree/bt_cursor.c	Wed Jan  6 16:02:36 1999
    ***************
    *** 641,655 ****
      		}
      
      		/*
    ! 		 * Move to the next item.  If __bam_c_next returns DB_NOTFOUND
    ! 		 * and we're doing an insert, reset the cursor back to the last
    ! 		 * item and set the referenced memory location so callers know
    ! 		 * to insert after the item, instead of before it.  If not doing
    ! 		 * an insert, return DB_NOTFOUND.
      		 */
    ! 		if ((ret = __bam_c_next(dbc, cp, 1)) != 0) {
    ! 			if (ret != DB_NOTFOUND || iflagp == NULL)
    ! 				return (ret);
      			goto use_last;
      		}
      
    --- 641,655 ----
      		}
      
      		/*
    ! 		 * Move to the next item.  If we reach the end of the page and
    ! 		 * we're doing an insert, set the cursor to the last item and
    ! 		 * set the referenced memory location so callers know to insert
    ! 		 * after the item, instead of before it.  If not inserting, we
    ! 		 * return DB_NOTFOUND.
      		 */
    ! 		if ((cp->indx += P_INDX) >= NUM_ENT(cp->page)) {
    ! 			if (iflagp == NULL)
    ! 				return (DB_NOTFOUND);
      			goto use_last;
      		}
    
    

  9. Auto-configuration was incorrect when building threaded versions of the Berkeley DB library on FreeBSD and Linux.
  10. Apply the following patch to the db-2.6.4 dist/configure file.  
    *** dist/configure.orig	Mon Jan 11 09:11:38 1999
    --- dist/configure	Mon Jan 11 09:10:51 1999
    ***************
    *** 3055,3063 ****
      fi
      
      case "$host_os" in
    ! freebsd*) CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS"
    ! 	  LIBS="-lc_r";;
    ! irix*)    CPPFLAGS="-D_SGI_MP_SOURCE $CPPFLAGS";;
      osf*)	  CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
      solaris*) CPPFLAGS="-D_REENTRANT $CPPFLAGS"
      	  LIBS="-lthread $LIBS";;
    --- 3055,3063 ----
      fi
      
      case "$host_os" in
    ! freebsd*) CPPFLAGS="-D_THREAD_SAFE -pthread $CPPFLAGS";;
    ! irix*)	  CPPFLAGS="-D_SGI_MP_SOURCE $CPPFLAGS";;
    ! linux*)	  CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
      osf*)	  CPPFLAGS="-D_REENTRANT $CPPFLAGS";;
      solaris*) CPPFLAGS="-D_REENTRANT $CPPFLAGS"
      	  LIBS="-lthread $LIBS";;
    

  11. There was a bug in returning partial records from an on-page duplicate in hash databases.
  12. Apply the following patch to the db-2.6.4 hash/hash.c file.  
    *** hash/hash.c.orig	Fri Dec 11 15:00:35 1998
    --- hash/hash.c	Tue Jan 12 22:18:49 1999
    ***************
    *** 1027,1040 ****
      			ndx = hcp->dndx;
      		} else {
      			/*
    ! 			 * Copy the DBT in case we are retrieving into
    ! 			 * user memory and we need the parameters for
    ! 			 * it.
      			 */
      			memcpy(&tmp_val, val, sizeof(*val));
    ! 			F_SET(&tmp_val, DB_DBT_PARTIAL);
    ! 			tmp_val.dlen = hcp->dup_len;
    ! 			tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
      			myval = &tmp_val;
      		}
      
    --- 1027,1060 ----
      			ndx = hcp->dndx;
      		} else {
      			/*
    ! 			 * Copy the DBT in case we are retrieving into user
    ! 			 * memory and we need the parameters for it.  If the
    ! 			 * user requested a partial, then we need to adjust
    ! 			 * the user's parameters to get the partial of the
    ! 			 * duplicate which is itself a partial.
      			 */
      			memcpy(&tmp_val, val, sizeof(*val));
    ! 			if (F_ISSET(&tmp_val, DB_DBT_PARTIAL)) {
    ! 				/*
    ! 				 * Take the user's length unless it would go
    ! 				 * beyond the end of the duplicate.
    ! 				 */
    ! 				if (tmp_val.doff + hcp->dup_off > hcp->dup_len)
    ! 					tmp_val.dlen = 0;
    ! 				else if (tmp_val.dlen + tmp_val.doff >
    ! 				    hcp->dup_len)
    ! 					tmp_val.dlen =
    ! 					    hcp->dup_len - tmp_val.doff;
    ! 				
    ! 				/*
    ! 				 * Calculate the new offset.
    ! 				 */
    ! 				tmp_val.doff += hcp->dup_off;
    ! 			} else {
    ! 				F_SET(&tmp_val, DB_DBT_PARTIAL);
    ! 				tmp_val.dlen = hcp->dup_len;
    ! 				tmp_val.doff = hcp->dup_off + sizeof(db_indx_t);
    ! 			}
      			myval = &tmp_val;
      		}
      
    

  13. It was possible to overwrite the checkpoint LSN during checkpoint.
  14. Apply the following patch to the db-2.6.4 txn/txn.c file.  
    *** txn/txn.c.orig	Sun Oct 25 21:14:49 1998
    --- txn/txn.c	Fri Jan 15 10:34:24 1999
    ***************
    *** 674,680 ****
      	u_int32_t kbytes, minutes;
      {
      	DB_LOG *dblp;
    ! 	DB_LSN ckp_lsn, last_ckp;
      	TXN_DETAIL *txnp;
      	time_t last_ckp_time, now;
      	u_int32_t kbytes_written;
    --- 674,680 ----
      	u_int32_t kbytes, minutes;
      {
      	DB_LOG *dblp;
    ! 	DB_LSN ckp_lsn, sync_lsn, last_ckp;
      	TXN_DETAIL *txnp;
      	time_t last_ckp_time, now;
      	u_int32_t kbytes_written;
    ***************
    *** 750,757 ****
      	mgr->region->pending_ckp = ckp_lsn;
      	UNLOCK_TXNREGION(mgr);
      
      	if (mgr->dbenv->mp_info != NULL &&
    ! 	    (ret = memp_sync(mgr->dbenv->mp_info, &ckp_lsn)) != 0) {
      		/*
      		 * ret == DB_INCOMPLETE means that there are still buffers to
      		 * flush, the checkpoint is not complete.  Wait and try again.
    --- 750,762 ----
      	mgr->region->pending_ckp = ckp_lsn;
      	UNLOCK_TXNREGION(mgr);
      
    + 	/*
    + 	 * memp_sync may change the lsn you pass it, so don't pass it
    + 	 * the actual ckp_lsn, pass it a temp instead.
    + 	 */
    + 	sync_lsn = ckp_lsn;
      	if (mgr->dbenv->mp_info != NULL &&
    ! 	    (ret = memp_sync(mgr->dbenv->mp_info, &sync_lsn)) != 0) {
      		/*
      		 * ret == DB_INCOMPLETE means that there are still buffers to
      		 * flush, the checkpoint is not complete.  Wait and try again.
    

  15. It was possible to run out of memory by using ever-increasing log file numbers.
  16. Apply the following patch to the db-2.6.4 log/log_put.c file.  
    Apply the following patch to the db-2.6.4 log/log_register.c file.  
    *** log/log_put.c.orig	Tue Nov  3 10:02:06 1998
    --- log/log_put.c	Fri Jan 15 10:35:41 1999
    ***************
    *** 157,162 ****
    --- 157,164 ----
      
      		for (fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
      		    fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
    + 			if (fnp->ref == 0)	/* Entry not in use. */
    + 				continue;
      			memset(&t, 0, sizeof(t));
      			t.data = R_ADDR(dblp, fnp->name_off);
      			t.size = strlen(t.data) + 1;
    *** log/log_register.c.orig	Sun Sep 27 10:40:32 1998
    --- log/log_register.c	Fri Jan 15 10:35:52 1999
    ***************
    *** 36,52 ****
      {
      	DBT fid_dbt, r_name;
      	DB_LSN r_unused;
    ! 	FNAME *fnp;
      	size_t len;
    ! 	u_int32_t fid;
      	int inserted, ret;
      	char *fullname;
      	void *namep;
      
    - 	fid = 0;
      	inserted = 0;
      	fullname = NULL;
    ! 	fnp = namep = NULL;
      
      	LOG_PANIC_CHECK(dblp);
      
    --- 36,51 ----
      {
      	DBT fid_dbt, r_name;
      	DB_LSN r_unused;
    ! 	FNAME *fnp, *reuse_fnp;
      	size_t len;
    ! 	u_int32_t maxid;
      	int inserted, ret;
      	char *fullname;
      	void *namep;
      
      	inserted = 0;
      	fullname = NULL;
    ! 	fnp = namep = reuse_fnp = NULL;
      
      	LOG_PANIC_CHECK(dblp);
      
    ***************
    *** 65,88 ****
      
      	/*
      	 * See if we've already got this file in the log, finding the
    ! 	 * next-to-lowest file id currently in use as we do it.
      	 */
    ! 	for (fid = 1, fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
      	    fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
    ! 		if (fid <= fnp->id)
    ! 			fid = fnp->id + 1;
      		if (!memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN)) {
      			++fnp->ref;
    - 			fid = fnp->id;
      			goto found;
      		}
      	}
      
    ! 	/* Allocate a new file name structure. */
    ! 	if ((ret = __db_shalloc(dblp->addr, sizeof(FNAME), 0, &fnp)) != 0)
      		goto err;
      	fnp->ref = 1;
    - 	fnp->id = fid;
      	fnp->s_type = type;
      	memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN);
      
    --- 64,98 ----
      
      	/*
      	 * See if we've already got this file in the log, finding the
    ! 	 * (maximum+1) in-use file id and some available file id (if we
    ! 	 * find an available fid, we'll use it, else we'll have to allocate
    ! 	 * one after the maximum that we found).
      	 */
    ! 	for (maxid = 0, fnp = SH_TAILQ_FIRST(&dblp->lp->fq, __fname);
      	    fnp != NULL; fnp = SH_TAILQ_NEXT(fnp, q, __fname)) {
    ! 		if (fnp->ref == 0 && reuse_fnp == NULL) {
    ! 			/* Entry is not in use. */
    ! 			reuse_fnp = fnp;
    ! 			continue;
    ! 		}
      		if (!memcmp(dbp->fileid, fnp->ufid, DB_FILE_ID_LEN)) {
      			++fnp->ref;
      			goto found;
      		}
    + 		if (maxid <= fnp->id)
    + 			maxid = fnp->id + 1;
      	}
      
    ! 	/* Fill in fnp structure. */
    ! 
    ! 	if (reuse_fnp != NULL)		/* Reuse existing one. */
    ! 		fnp = reuse_fnp;
    ! 	else if ((ret = __db_shalloc(dblp->addr, sizeof(FNAME), 0, &fnp)) != 0)
      		goto err;
    + 	else				/* Allocate a new one. */
    + 		fnp->id = maxid;
    + 
      	fnp->ref = 1;
      	fnp->s_type = type;
      	memcpy(fnp->ufid, dbp->fileid, DB_FILE_ID_LEN);
      
    ***************
    *** 92,98 ****
      	fnp->name_off = R_OFFSET(dblp, namep);
      	memcpy(namep, name, len);
      
    ! 	SH_TAILQ_INSERT_HEAD(&dblp->lp->fq, fnp, q, __fname);
      	inserted = 1;
      
      found:	/* Log the registry. */
    --- 102,110 ----
      	fnp->name_off = R_OFFSET(dblp, namep);
      	memcpy(namep, name, len);
      
    ! 	/* Only do the insert if we allocated a new fnp. */
    ! 	if (reuse_fnp == NULL)
    ! 		SH_TAILQ_INSERT_HEAD(&dblp->lp->fq, fnp, q, __fname);
      	inserted = 1;
      
      found:	/* Log the registry. */
    ***************
    *** 103,111 ****
      		fid_dbt.data = dbp->fileid;
      		fid_dbt.size = DB_FILE_ID_LEN;
      		if ((ret = __log_register_log(dblp, NULL, &r_unused,
    ! 		    0, LOG_OPEN, &r_name, &fid_dbt, fid, type)) != 0)
      			goto err;
    ! 		if ((ret = __log_add_logid(dblp, dbp, fid)) != 0)
      			goto err;
      	}
      
    --- 115,123 ----
      		fid_dbt.data = dbp->fileid;
      		fid_dbt.size = DB_FILE_ID_LEN;
      		if ((ret = __log_register_log(dblp, NULL, &r_unused,
    ! 		    0, LOG_OPEN, &r_name, &fid_dbt, fnp->id, type)) != 0)
      			goto err;
    ! 		if ((ret = __log_add_logid(dblp, dbp, fnp->id)) != 0)
      			goto err;
      	}
      
    ***************
    *** 122,134 ****
      			__db_shalloc_free(dblp->addr, fnp);
      	}
      
      	UNLOCK_LOGREGION(dblp);
      
      	if (fullname != NULL)
      		__os_freestr(fullname);
      
    - 	if (idp != NULL)
    - 		*idp = fid;
      	return (ret);
      }
      
    --- 134,146 ----
      			__db_shalloc_free(dblp->addr, fnp);
      	}
      
    + 	if (idp != NULL)
    + 		*idp = fnp->id;
      	UNLOCK_LOGREGION(dblp);
      
      	if (fullname != NULL)
      		__os_freestr(fullname);
      
      	return (ret);
      }
      
    ***************
    *** 177,191 ****
      
      	/*
      	 * If more than 1 reference, just decrement the reference and return.
    ! 	 * Otherwise, free the unique file information, name and structure.
      	 */
    ! 	if (fnp->ref > 1)
    ! 		--fnp->ref;
    ! 	else {
      		__db_shalloc_free(dblp->addr, R_ADDR(dblp, fnp->name_off));
    - 		SH_TAILQ_REMOVE(&dblp->lp->fq, fnp, q, __fname);
    - 		__db_shalloc_free(dblp->addr, fnp);
    - 	}
      
      	/*
      	 * Remove from the process local table.  If this operation is taking
    --- 189,199 ----
      
      	/*
      	 * If more than 1 reference, just decrement the reference and return.
    ! 	 * Otherwise, free the name.
      	 */
    ! 	--fnp->ref;
    ! 	if (fnp->ref == 0)
      		__db_shalloc_free(dblp->addr, R_ADDR(dblp, fnp->name_off));
      
      	/*
      	 * Remove from the process local table.  If this operation is taking
    

  17. It was possible for retrieval of specific duplicate records to cause a core dump.
  18. Apply the following patch to the db-2.6.4 btree/bt_cursor.c file.  
    *** btree/bt_cursor.c.orig	Wed Jan  6 21:38:45 1999
    --- btree/bt_cursor.c	Fri Jan 15 09:17:27 1999
    ***************
    *** 58,63 ****
    --- 58,72 ----
      }
      
      /* If the cursor references a deleted record. */
    + #undef	IS_CUR_DELETED
    + #define	IS_CUR_DELETED(cp)						\
    + 	(((cp)->dpgno == PGNO_INVALID &&				\
    + 	B_DISSET(GET_BKEYDATA((cp)->page,				\
    + 	(cp)->indx + O_INDX)->type)) ||					\
    + 	((cp)->dpgno != PGNO_INVALID &&					\
    + 	B_DISSET(GET_BKEYDATA((cp)->page, (cp)->dindx)->type)))
    + 
    + /* If the cursor and index combination references a deleted record. */
      #undef	IS_DELETED
      #define	IS_DELETED(cp, indx)						\
      	(((cp)->dpgno == PGNO_INVALID &&				\
    ***************
    *** 486,491 ****
    --- 495,506 ----
      		/* Search for a matching entry. */
      		if ((ret = __bam_dsearch(dbc, cp, data, NULL)) != 0)
      			goto err;
    + 
    + 		/* Ignore deleted entries. */
    + 		if (IS_CUR_DELETED(cp)) {
    + 			ret = DB_NOTFOUND;
    + 			goto err;
    + 		}
      		break;
      	case DB_SET_RANGE:
      		if ((ret = __bam_c_search(dbc, cp, key, flags, &exact)) != 0)
    ***************
    *** 1070,1076 ****
      		return (ret);
      
      	/* If on an empty page or a deleted record, move to the next one. */
    ! 	if (NUM_ENT(cp->page) == 0 || IS_DELETED(cp, cp->indx))
      		if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
      			return (ret);
      
    --- 1085,1091 ----
      		return (ret);
      
      	/* If on an empty page or a deleted record, move to the next one. */
    ! 	if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
      		if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
      			return (ret);
      
    ***************
    *** 1118,1124 ****
      		return (ret);
      
      	/* If on an empty page or a deleted record, move to the next one. */
    ! 	if (NUM_ENT(cp->page) == 0 || IS_DELETED(cp, cp->indx))
      		if ((ret = __bam_c_prev(dbc, cp)) != 0)
      			return (ret);
      
    --- 1133,1139 ----
      		return (ret);
      
      	/* If on an empty page or a deleted record, move to the next one. */
    ! 	if (NUM_ENT(cp->page) == 0 || IS_CUR_DELETED(cp))
      		if ((ret = __bam_c_prev(dbc, cp)) != 0)
      			return (ret);
      
    ***************
    *** 1556,1562 ****
      		return (0);
      
      	/* If it's a deleted record, go to the next valid record. */
    ! 	while (IS_DELETED(cp, cp->dindx))
      		if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
      			return (ret);
      	return (0);
    --- 1571,1577 ----
      		return (0);
      
      	/* If it's a deleted record, go to the next valid record. */
    ! 	if (IS_CUR_DELETED(cp))
      		if ((ret = __bam_c_next(dbc, cp, 0)) != 0)
      			return (ret);
      	return (0);
    

  19. Locks waiting on an aborted lock were not correctly promoted at the time of the abort.
  20. Apply the following patch to the db-2.6.4 lock/lock.c file.  
    *** lock/lock.c.orig	Wed Dec 16 14:52:45 1998
    --- lock/lock.c	Fri Jan 22 15:10:34 1999
    ***************
    *** 511,516 ****
    --- 511,522 ----
      
      		LOCK_LOCKREGION(lt);
      		if (newl->status != DB_LSTAT_PENDING) {
    + 			/*
    + 			 * If this lock errored due to a deadlock, then
    + 			 * we have waiters that require promotion.
    + 			 */
    + 			if (newl->status == DB_LSTAT_ABORTED)
    + 				(void)__lock_promote(lt, sh_obj);
      			/* Return to free list. */
      			__lock_checklocker(lt, newl, 0);
      			SH_TAILQ_INSERT_HEAD(&lrp->free_locks, newl, links,