new namecache implementation

Rafal Szczesniak mimir at diament.ists.pwr.wroc.pl
Sun Oct 20 22:39:01 GMT 2002


After a little while of silence, I have a new proposition of
namecache implementation based on new gencache mechanism.
It compiles and works generally, but more thorough testing
is very welcome as it may be quite extensively used piece
of code.

As usual, comments and criticism (not too harsh, please)
are welcome :)


-- 
cheers,
+------------------------------------------------------------+
|Rafal 'Mimir' Szczesniak <mimir at diament.ists.pwr.wroc.pl>   |
|*BSD, GNU/Linux and Samba                                  /
|__________________________________________________________/
-------------- next part --------------
Index: libsmb/namecache.c
===================================================================
RCS file: /cvsroot/samba/source/libsmb/namecache.c,v
retrieving revision 1.8
diff -u -r1.8 namecache.c
--- libsmb/namecache.c	28 Aug 2002 00:17:11 -0000	1.8
+++ libsmb/namecache.c	20 Oct 2002 22:01:43 -0000
@@ -1,9 +1,10 @@
 /* 
    Unix SMB/CIFS implementation.
 
-   NetBIOS name cache module.
-
-   Copyright (C) Tim Potter, 2002
+   NetBIOS name cache module on top of gencache mechanism.
+   
+   Copyright (C) Tim Potter         2002
+   Copyright (C) Rafal Szczesniak   2002
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -22,242 +23,267 @@
 
 #include "includes.h"
 
-static BOOL done_namecache_init;
-static BOOL enable_namecache;
-static TDB_CONTEXT *namecache_tdb;
-
-struct nc_value {
-	time_t expiry;		     /* When entry expires */
-	int count;		     /* Number of addresses */
-	struct in_addr ip_list[1];   /* Address list */
-};
+#define NBTKEY_FMT  "NBT/%s#%02X"
 
-/* Initialise namecache system */
+
+/**
+ * Initialise namecache system. Function calls gencache
+ * initialisation function to perform necessary actions
+ * 
+ * @return true upon successful initialisation of the cache or
+ *         false on failure
+ **/
 
 BOOL namecache_enable(void)
 {
-	/* Check if we have been here before, or name caching disabled
-           by setting the name cache timeout to zero. */ 
-
-	if (done_namecache_init)
-		return False;
-
-	done_namecache_init = True;
+	/*
+	 * Check if name caching disabled by setting the name cache
+	 * timeout to zero.
+	 */ 
 
 	if (lp_name_cache_timeout() == 0) {
 		DEBUG(5, ("namecache_init: disabling netbios name cache\n"));
 		return False;
 	}
 
-	/* Open namecache tdb in read/write or readonly mode */
+	/* Init namecache by calling gencache initialisation */
 
-	namecache_tdb = tdb_open_log(
-		lock_path("namecache.tdb"), 0,
-		TDB_DEFAULT, O_RDWR | O_CREAT, 0644);
-
-	if (!namecache_tdb) {
-		DEBUG(5, ("namecache_init: could not open %s\n",
-			  lock_path("namecache.tdb")));
+	if (!gencache_init()) {
+		DEBUG(2, ("namecache_init: Couldn't initialise namecache on top of gencache.\n"));
 		return False;
 	}
 
+	/* I leave it for now, though I don't think we really need this (mimir, 27.09.2002) */
 	DEBUG(5, ("namecache_init: enabling netbios namecache, timeout %d "
 		  "seconds\n", lp_name_cache_timeout()));
 
-	enable_namecache = True;
-
 	return True;
 }
 
-/* Return a key for a name and name type.  The caller must free
-   retval.dptr when finished. */
+/**
+ * Generates a key for netbios name lookups on basis of
+ * netbios name and type.
+ * The caller must free returned key string when finished.
+ *
+ * @param name netbios name string (case insensitive)
+ * @param name_type netbios type of the name being looked up
+ *
+ * @return string consisted of uppercased name and appended
+ *         type number
+ */
 
-static TDB_DATA namecache_key(const char *name, int name_type)
+static char* namecache_key(const char *name, int name_type)
 {
-	TDB_DATA retval;
 	char *keystr;
+	asprintf(&keystr, NBTKEY_FMT, strupper_static(name), name_type);
 
-	asprintf(&keystr, "%s#%02X", strupper_static(name), name_type);
-
-	retval.dsize = strlen(keystr) + 1;
-	retval.dptr = keystr;
-
-	return retval;
+	return keystr;
 }
 
-/* Return a data value for an IP list.  The caller must free
-   retval.dptr when finished. */
-
-static TDB_DATA namecache_value(struct in_addr *ip_list, int num_names, 
-				time_t expiry)
-{
-	TDB_DATA retval;
-	struct nc_value *value;
-	int size = sizeof(struct nc_value);
-
-	if (num_names > 0)
-		size += sizeof(struct in_addr) * (num_names-1);
-
-	value = (struct nc_value *)malloc(size);
-
-	memset(value, 0, size);
-
-	value->expiry = expiry;
-	value->count = num_names;
-
-	if (ip_list)
-		memcpy(value->ip_list, ip_list, sizeof(struct in_addr) * num_names);
 
-	retval.dptr = (char *)value;
-	retval.dsize = size;
+/**
+ * Store a name(s) in the name cache
+ *
+ * @param name netbios names array
+ * @param name_type integer netbios name type
+ * @param num_names number of names being stored
+ * @param ip_list array of in_addr structures containing
+ *        ip addresses being stored
+ **/
 
-	return retval;
-}
-
-/* Store a name in the name cache */
-
-void namecache_store(const char *name, int name_type,
-		     int num_names, struct in_addr *ip_list)
+BOOL namecache_store(const char *name, int name_type,
+                     int num_names, struct in_addr *ip_list)
 {
-	TDB_DATA key, value;
 	time_t expiry;
-	int i;
-
-	if (!enable_namecache)
-		return;
+	char *key;
+	pstring value_string;
+	int i, value_len;
+
+	/*
+	 * we use gecache call to avoid annoying debug messages about
+	 * initialised namecache again and again...
+	 */
+	if (!gencache_init()) return False;
 
 	DEBUG(5, ("namecache_store: storing %d address%s for %s#%02x: ",
-		  num_names, num_names == 1 ? "": "es", name, name_type));
+	          num_names, num_names == 1 ? "": "es", name, name_type));
 
 	for (i = 0; i < num_names; i++) 
 		DEBUGADD(5, ("%s%s", inet_ntoa(ip_list[i]),
-			     i == (num_names - 1) ? "" : ", "));
+		             i == (num_names - 1) ? "" : ", "));
 
 	DEBUGADD(5, ("\n"));
 
 	key = namecache_key(name, name_type);
 
-	/* Cache pdc location or dc lists for only a little while
-	   otherwise if we lock on to a bad DC we can potentially be
-	   out of action for the entire cache timeout time! */
+	/* 
+	 * Cache pdc location or dc lists for only a little while
+	 * otherwise if we lock on to a bad DC we can potentially be
+	 * out of action for the entire cache timeout time!
+	 */
 
 	if (name_type != 0x1b || name_type != 0x1c)
 		expiry = time(NULL) + 10;
 	else
 		expiry = time(NULL) + lp_name_cache_timeout();
 
-	value = namecache_value(ip_list, num_names, expiry);
-
-	tdb_store(namecache_tdb, key, value, TDB_REPLACE);
+	/*
+	 * Generate string representation of ip addresses list
+	 * First, store the number of ip addresses and then
+	 * place each single ip
+	 */
+	 
+	value_len = snprintf(value_string, PSTRING_LEN, "%d:", num_names);
+	for (i = 0; i < num_names; i++) {
+		/* Attempt to predict any problems with limited length of the stored value */
+		if (value_len + strlen(inet_ntoa(ip_list[i])) >= PSTRING_LEN) {
+			DEBUG(0, ("Address list was too long to store it in the cache!\n"));
+			return False;
+		}
 
-	free(key.dptr);
-	free(value.dptr);
+		value_len += snprintf(&(value_string[value_len]), PSTRING_LEN, "%s:", inet_ntoa(ip_list[i]));
+	};
+	
+	/*
+	 * if an attempt to set fails, then perhaps there's no such
+	 * entry and it's better to add i
+	 */
+	 
+	if (!gencache_set(key, value_string, expiry))
+		return (gencache_add(key, value_string, expiry));
+	
+	return True;
 }
 
-/* Look up a name in the name cache.  Return a mallocated list of IP
-   addresses if the name is contained in the cache. */
+
+/**
+ * Look up a name in the cache.
+ *
+ * @param name netbios name to look up for
+ * @param name_type netbios name type of @param name
+ * @param ip_list mallocated list of IP addresses if found in the cache,
+ *        NULL otherwise
+ * @param num_names number of entries found
+ *
+ * @return true upon successful fetch or
+ *         false if name isn't found in the cache or has expired
+ **/
 
 BOOL namecache_fetch(const char *name, int name_type, struct in_addr **ip_list,
-		     int *num_names)
+                     int *num_names)
 {
-	TDB_DATA key, value;
-	struct nc_value *data;
-	time_t now;
-	int i;
+	char *key, *value;
+	time_t timeout;
+	int i, count, ip_num, n;
 
 	*ip_list = NULL;
+	ip_num = 0;
 	*num_names = 0;
+	n = 0;
 
-	if (!enable_namecache)
-		return False;
+	/* exit now if null pointers were passed as they're required further */
+	if (!ip_list || !num_names) return False;
 
-	/* Read value */
+	if (!gencache_init())
+		return False;
 
+	/* 
+	 * Use gencache interface - lookup the key
+	 */
 	key = namecache_key(name, name_type);
 
-	value = tdb_fetch(namecache_tdb, key);
+	if (!gencache_get(key, &value, &timeout)) {
+		DEBUG(5, ("no entry for %s#%02X found.\n", name, name_type));
+		return False;
+	} else
+		DEBUG(5, ("name %s#%02X found.\n", name, name_type));
 	
-	if (!value.dptr) {
-		DEBUG(5, ("namecache_fetch: %s#%02x not found\n",
-			  name, name_type));
-		goto done;
-	}
-
-	data = (struct nc_value *)value.dptr;
-
-	/* Check expiry time */
-
-	now = time(NULL);
-
-	if (now > data->expiry) {
-
-		DEBUG(5, ("namecache_fetch: entry for %s#%02x expired\n",
-			  name, name_type));
-
-		tdb_delete(namecache_tdb, key);
-
-		value = tdb_null;
-
-		goto done;
-	}
-
-	if ((data->expiry - now) > lp_name_cache_timeout()) {
-
-		/* Someone may have changed the system time on us */
-
-		DEBUG(5, ("namecache_fetch: entry for %s#%02x has bad expiry\n",
-			  name, name_type));
-
-		tdb_delete(namecache_tdb, key);
-
-		value = tdb_null;
-
-		goto done;
-	}
-
-	/* Extract and return namelist */
-
-	DEBUG(5, ("namecache_fetch: returning %d address%s for %s#%02x: ",
-		  data->count, data->count == 1 ? "" : "es", name, name_type));
-
-	if (data->count) {
-
-		*ip_list = (struct in_addr *)malloc(
-			sizeof(struct in_addr) * data->count);
+	/*
+	 * Split up the stored value into the list of IP adresses
+	 */
+	 
+	/* first, get number of stored addresses */
+	sscanf(value, "%d:", &count);
+	DEBUG(5, ("returning %d address%s for %s#%02x: ",
+	          count, count == 1 ? "" : "es", name, name_type));
+
+	if (count) {
+		/* prepare the address(es) storage and indices */
+		fstring ip_str;
+		char* value_start;
+		int value_len;
 		
-		memcpy(*ip_list, data->ip_list, sizeof(struct in_addr) * data->count);
+		/* ip_str must be empty at the begin */
+		ip_str[n] = 0;
 		
-		*num_names = data->count;
-		
-		for (i = 0; i < *num_names; i++)
-			DEBUGADD(5, ("%s%s", inet_ntoa((*ip_list)[i]),
-				     i == (*num_names - 1) ? "" : ", "));
+		/* fetching of addresses should start right after the count value */
+		value_start = strchr(value, ':');
+		value_len = strlen(value_start);
+
+		for (i = 0; i < value_len && ip_num < count; i++) {
+			if (value_start[i] == ':') {
+				if (strlen(ip_str)) {
+					/*
+					 * ip_str is not already empty, so we've found separating colon
+					 */
+					ip_str[n] = '\0';		/* ensure null-termination */
+					ip_list[ip_num] = (struct in_addr *)malloc(sizeof(struct in_addr));
+					
+					if (! inet_aton(ip_str, &((*ip_list)[ip_num]))) {
+						(*ip_list)[ip_num].s_addr = 0;
+					} else {
+						DEBUGADD(5, ("%s%s", inet_ntoa((*ip_list)[ip_num]),
+						             ip_num + 1 < count ? ", " : ""));
+						ip_num++;
+						
+						/* now, fetching of next ip string starts again */
+						ip_str[n = 0] = '\0';
+					}
+				
+				}
+				
+			} else
+				ip_str[n++] = value_start[i];
 
+		}
 	}
-
 	DEBUGADD(5, ("\n"));
 
-done:
-	SAFE_FREE(key.dptr);
-	SAFE_FREE(value.dptr);
+	return ip_num == count;		/* true only if each ip has been fetched */
+}
+
 
-	return value.dsize > 0;
+/**
+ * Delete single namecache entry. Look at the
+ * gencache_iterate definition.
+ *
+ **/
+
+static void flush_netbios_name(const char* key, const char *value, time_t timeout, void* dptr)
+{
+	gencache_del(key);
+	DEBUG(5, ("Deleting entry %s\n", key));
 }
 
-/* Flush all names from the name cache */
+
+/**
+ * Flush all names from the name cache.
+ * It's done by gencache_iterate()
+ *
+ * @return True upon successful deletion or
+ *         False in case of an error
+ **/
 
 void namecache_flush(void)
 {
-	int result;
-
-	if (!namecache_tdb)
+	if (!gencache_init())
 		return;
 
-	result = tdb_traverse(namecache_tdb, tdb_traverse_delete_fn, NULL);
-
-	if (result == -1)
-		DEBUG(5, ("namecache_flush: error deleting cache entries\n"));
-	else
-		DEBUG(5, ("namecache_flush: deleted %d cache entr%s\n", 
-			  result, result == 1 ? "y" : "ies"));
+	/* 
+	 * iterate through each NBT cache's entry and flush it
+	 * by flush_netbios_name function
+	 */
+	gencache_iterate(flush_netbios_name, NULL, "NBT/*");
+	DEBUG(5, ("Namecache flushed\n"));
 }
+


More information about the samba-technical mailing list