📄 rbtdb.c
字号:
/* * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2003 Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. *//* $Id: rbtdb.c,v 1.168.2.11.2.16 2004/05/23 11:07:23 marka Exp $ *//* * Principal Author: Bob Halley */#include <config.h>#include <isc/event.h>#include <isc/mem.h>#include <isc/print.h>#include <isc/mutex.h>#include <isc/random.h>#include <isc/refcount.h>#include <isc/rwlock.h>#include <isc/string.h>#include <isc/task.h>#include <isc/util.h>#include <dns/db.h>#include <dns/dbiterator.h>#include <dns/events.h>#include <dns/fixedname.h>#include <dns/log.h>#include <dns/masterdump.h>#include <dns/rbt.h>#include <dns/rdata.h>#include <dns/rdataset.h>#include <dns/rdatasetiter.h>#include <dns/rdataslab.h>#include <dns/result.h>#include <dns/zonekey.h>#ifdef DNS_RBTDB_VERSION64#include "rbtdb64.h"#else#include "rbtdb.h"#endif#ifdef DNS_RBTDB_VERSION64#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '8')#else#define RBTDB_MAGIC ISC_MAGIC('R', 'B', 'D', '4')#endif/* * Note that "impmagic" is not the first four bytes of the struct, so * ISC_MAGIC_VALID cannot be used. */#define VALID_RBTDB(rbtdb) ((rbtdb) != NULL && \ (rbtdb)->common.impmagic == RBTDB_MAGIC)#ifdef DNS_RBTDB_VERSION64typedef isc_uint64_t rbtdb_serial_t;/* * Make casting easier in symbolic debuggers by using different names * for the 64 bit version. */#define dns_rbtdb_t dns_rbtdb64_t#define rdatasetheader_t rdatasetheader64_t#define rbtdb_version_t rbtdb_version64_t#elsetypedef isc_uint32_t rbtdb_serial_t;#endiftypedef isc_uint32_t rbtdb_rdatatype_t;#define RBTDB_RDATATYPE_BASE(type) ((dns_rdatatype_t)((type) & 0xFFFF))#define RBTDB_RDATATYPE_EXT(type) ((dns_rdatatype_t)((type) >> 16))#define RBTDB_RDATATYPE_VALUE(b, e) (((e) << 16) | (b))#define RBTDB_RDATATYPE_SIGNSEC \ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_nsec)#define RBTDB_RDATATYPE_SIGNS \ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_ns)#define RBTDB_RDATATYPE_SIGCNAME \ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_cname)#define RBTDB_RDATATYPE_SIGDNAME \ RBTDB_RDATATYPE_VALUE(dns_rdatatype_rrsig, dns_rdatatype_dname)#define RBTDB_RDATATYPE_NCACHEANY \ RBTDB_RDATATYPE_VALUE(0, dns_rdatatype_any)struct noqname { dns_name_t name; void * nsec; void * nsecsig;};typedef struct rdatasetheader { /* * Locked by the owning node's lock. */ rbtdb_serial_t serial; dns_ttl_t ttl; rbtdb_rdatatype_t type; isc_uint16_t attributes; dns_trust_t trust; struct noqname *noqname; /* * We don't use the LIST macros, because the LIST structure has * both head and tail pointers, and is doubly linked. */ struct rdatasetheader *next; /* * If this is the top header for an rdataset, 'next' points * to the top header for the next rdataset (i.e., the next type). * Otherwise, it points up to the header whose down pointer points * at this header. */ struct rdatasetheader *down; /* * Points to the header for the next older version of * this rdataset. */ isc_uint32_t count; /* * Monotonously increased every time this rdataset is bound so that * it is used as the base of the starting point in DNS responses * when the "cyclic" rrset-order is required. Since the ordering * should not be so crucial, no lock is set for the counter for * performance reasons. */} rdatasetheader_t;#define RDATASET_ATTR_NONEXISTENT 0x0001#define RDATASET_ATTR_STALE 0x0002#define RDATASET_ATTR_IGNORE 0x0004#define RDATASET_ATTR_RETAIN 0x0008#define RDATASET_ATTR_NXDOMAIN 0x0010/* * XXX * When the cache will pre-expire data (due to memory low or other * situations) before the rdataset's TTL has expired, it MUST * respect the RETAIN bit and not expire the data until its TTL is * expired. */#undef IGNORE /* WIN32 winbase.h defines this. */#define EXISTS(header) \ (((header)->attributes & RDATASET_ATTR_NONEXISTENT) == 0)#define NONEXISTENT(header) \ (((header)->attributes & RDATASET_ATTR_NONEXISTENT) != 0)#define IGNORE(header) \ (((header)->attributes & RDATASET_ATTR_IGNORE) != 0)#define RETAIN(header) \ (((header)->attributes & RDATASET_ATTR_RETAIN) != 0)#define NXDOMAIN(header) \ (((header)->attributes & RDATASET_ATTR_NXDOMAIN) != 0)#define DEFAULT_NODE_LOCK_COUNT 7 /* Should be prime. */typedef struct { isc_mutex_t lock; /* Locked by lock. */ unsigned int references; isc_boolean_t exiting;} rbtdb_nodelock_t;typedef struct rbtdb_changed { dns_rbtnode_t * node; isc_boolean_t dirty; ISC_LINK(struct rbtdb_changed) link;} rbtdb_changed_t;typedef ISC_LIST(rbtdb_changed_t) rbtdb_changedlist_t;typedef struct rbtdb_version { /* Not locked */ rbtdb_serial_t serial; /* Locked by database lock. */ isc_boolean_t writer; unsigned int references; isc_boolean_t commit_ok; rbtdb_changedlist_t changed_list; ISC_LINK(struct rbtdb_version) link;} rbtdb_version_t;typedef ISC_LIST(rbtdb_version_t) rbtdb_versionlist_t;typedef struct { /* Unlocked. */ dns_db_t common; isc_mutex_t lock; isc_rwlock_t tree_lock; unsigned int node_lock_count; rbtdb_nodelock_t * node_locks; dns_rbtnode_t * origin_node; /* Locked by lock. */ unsigned int active; isc_refcount_t references; unsigned int attributes; rbtdb_serial_t current_serial; rbtdb_serial_t least_serial; rbtdb_serial_t next_serial; rbtdb_version_t * current_version; rbtdb_version_t * future_version; rbtdb_versionlist_t open_versions; isc_boolean_t overmem; isc_task_t * task; /* Locked by tree_lock. */ dns_rbt_t * tree; isc_boolean_t secure;} dns_rbtdb_t;#define RBTDB_ATTR_LOADED 0x01#define RBTDB_ATTR_LOADING 0x02/* * Search Context */typedef struct { dns_rbtdb_t * rbtdb; rbtdb_version_t * rbtversion; rbtdb_serial_t serial; unsigned int options; dns_rbtnodechain_t chain; isc_boolean_t copy_name; isc_boolean_t need_cleanup; isc_boolean_t wild; dns_rbtnode_t * zonecut; rdatasetheader_t * zonecut_rdataset; rdatasetheader_t * zonecut_sigrdataset; dns_fixedname_t zonecut_name; isc_stdtime_t now;} rbtdb_search_t;/* * Load Context */typedef struct { dns_rbtdb_t * rbtdb; isc_stdtime_t now;} rbtdb_load_t;static void rdataset_disassociate(dns_rdataset_t *rdataset);static isc_result_t rdataset_first(dns_rdataset_t *rdataset);static isc_result_t rdataset_next(dns_rdataset_t *rdataset);static void rdataset_current(dns_rdataset_t *rdataset, dns_rdata_t *rdata);static void rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target);static unsigned int rdataset_count(dns_rdataset_t *rdataset);static isc_result_t rdataset_getnoqname(dns_rdataset_t *rdataset, dns_name_t *name, dns_rdataset_t *nsec, dns_rdataset_t *nsecsig);static dns_rdatasetmethods_t rdataset_methods = { rdataset_disassociate, rdataset_first, rdataset_next, rdataset_current, rdataset_clone, rdataset_count, NULL, rdataset_getnoqname};static void rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp);static isc_result_t rdatasetiter_first(dns_rdatasetiter_t *iterator);static isc_result_t rdatasetiter_next(dns_rdatasetiter_t *iterator);static void rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset);static dns_rdatasetitermethods_t rdatasetiter_methods = { rdatasetiter_destroy, rdatasetiter_first, rdatasetiter_next, rdatasetiter_current};typedef struct rbtdb_rdatasetiter { dns_rdatasetiter_t common; rdatasetheader_t * current;} rbtdb_rdatasetiter_t;static void dbiterator_destroy(dns_dbiterator_t **iteratorp);static isc_result_t dbiterator_first(dns_dbiterator_t *iterator);static isc_result_t dbiterator_last(dns_dbiterator_t *iterator);static isc_result_t dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name);static isc_result_t dbiterator_prev(dns_dbiterator_t *iterator);static isc_result_t dbiterator_next(dns_dbiterator_t *iterator);static isc_result_t dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep, dns_name_t *name);static isc_result_t dbiterator_pause(dns_dbiterator_t *iterator);static isc_result_t dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name);static dns_dbiteratormethods_t dbiterator_methods = { dbiterator_destroy, dbiterator_first, dbiterator_last, dbiterator_seek, dbiterator_prev, dbiterator_next, dbiterator_current, dbiterator_pause, dbiterator_origin};#define DELETION_BATCH_MAX 64/* * If 'paused' is ISC_TRUE, then the tree lock is not being held. */typedef struct rbtdb_dbiterator { dns_dbiterator_t common; isc_boolean_t paused; isc_boolean_t new_origin; isc_rwlocktype_t tree_locked; isc_result_t result; dns_fixedname_t name; dns_fixedname_t origin; dns_rbtnodechain_t chain; dns_rbtnode_t *node; dns_rbtnode_t *deletions[DELETION_BATCH_MAX]; int delete;} rbtdb_dbiterator_t;#define IS_STUB(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_STUB) != 0)#define IS_CACHE(rbtdb) (((rbtdb)->common.attributes & DNS_DBATTR_CACHE) != 0)static void free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event);/* * Locking * * If a routine is going to lock more than one lock in this module, then * the locking must be done in the following order: * * Tree Lock * * Node Lock (Only one from the set may be locked at one time by * any caller) * * Database Lock * * Failure to follow this hierarchy can result in deadlock. *//* * Deleting Nodes * * Currently there is no deletion of nodes from the database, except when * the database is being destroyed. * * If node deletion is added in the future, then for zone databases the node * for the origin of the zone MUST NOT be deleted. *//* * DB Routines */static voidattach(dns_db_t *source, dns_db_t **targetp) { dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)source; REQUIRE(VALID_RBTDB(rbtdb)); isc_refcount_increment(&rbtdb->references, NULL); *targetp = source;}static voidfree_rbtdb_callback(isc_task_t *task, isc_event_t *event) { dns_rbtdb_t *rbtdb = event->ev_arg; UNUSED(task); free_rbtdb(rbtdb, ISC_TRUE, event);}static voidfree_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) { unsigned int i; isc_ondestroy_t ondest; isc_result_t result; char buf[DNS_NAME_FORMATSIZE]; REQUIRE(EMPTY(rbtdb->open_versions)); REQUIRE(rbtdb->future_version == NULL); if (rbtdb->current_version != NULL) isc_mem_put(rbtdb->common.mctx, rbtdb->current_version, sizeof(rbtdb_version_t)); again: if (rbtdb->tree != NULL) { result = dns_rbt_destroy2(&rbtdb->tree, (rbtdb->task != NULL) ? 5 : 0); if (result == ISC_R_QUOTA) { INSIST(rbtdb->task != NULL); if (event == NULL) event = isc_event_allocate(rbtdb->common.mctx, NULL, DNS_EVENT_FREESTORAGE, free_rbtdb_callback, rbtdb, sizeof(isc_event_t)); if (event == NULL) goto again; isc_task_send(rbtdb->task, &event); return; } INSIST(result == ISC_R_SUCCESS && rbtdb->tree == NULL); } if (event != NULL) isc_event_free(&event); if (log) { if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_format(&rbtdb->common.origin, buf, sizeof(buf)); else strcpy(buf, "<UNKNOWN>"); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "done free_rbtdb(%s)", buf); } if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_free(&rbtdb->common.origin, rbtdb->common.mctx); for (i = 0; i < rbtdb->node_lock_count; i++) DESTROYLOCK(&rbtdb->node_locks[i].lock); isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks, rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t)); isc_rwlock_destroy(&rbtdb->tree_lock); isc_refcount_destroy(&rbtdb->references); if (rbtdb->task != NULL) isc_task_detach(&rbtdb->task); DESTROYLOCK(&rbtdb->lock); rbtdb->common.magic = 0; rbtdb->common.impmagic = 0; ondest = rbtdb->common.ondest; isc_mem_putanddetach(&rbtdb->common.mctx, rbtdb, sizeof(*rbtdb)); isc_ondestroy_notify(&ondest, rbtdb);}static inline voidmaybe_free_rbtdb(dns_rbtdb_t *rbtdb) { isc_boolean_t want_free = ISC_FALSE; unsigned int i; unsigned int inactive = 0; /* XXX check for open versions here */ /* * Even though there are no external direct references, there still * may be nodes in use. */ for (i = 0; i < rbtdb->node_lock_count; i++) { LOCK(&rbtdb->node_locks[i].lock); rbtdb->node_locks[i].exiting = ISC_TRUE; if (rbtdb->node_locks[i].references == 0) inactive++; UNLOCK(&rbtdb->node_locks[i].lock); } if (inactive != 0) { LOCK(&rbtdb->lock); rbtdb->active -= inactive; if (rbtdb->active == 0) want_free = ISC_TRUE; UNLOCK(&rbtdb->lock); if (want_free) { char buf[DNS_NAME_FORMATSIZE]; if (dns_name_dynamic(&rbtdb->common.origin)) dns_name_format(&rbtdb->common.origin, buf, sizeof(buf)); else strcpy(buf, "<UNKNOWN>"); isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_CACHE, ISC_LOG_DEBUG(1), "calling free_rbtdb(%s)", buf); free_rbtdb(rbtdb, ISC_TRUE, NULL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -