[autofs] - add upstream fixes and fix for bug 1086887.

Ian Kent iankent at fedoraproject.org
Sun Apr 13 10:47:03 UTC 2014


commit 86506c6a3931c304d806c40fb6f9e417cb19f969
Author: Ian Kent <raven at themaw.net>
Date:   Sun Apr 13 18:46:05 2014 +0800

    - add upstream fixes and fix for bug 1086887.

 ...p-update-lookup-hesiod-to-handle-amd-keys.patch |  423 +++++++++
 ...kup-update-lookup-ldap-to-handle-amd-keys.patch |  954 ++++++++++++++++++++
 ...existent-negative-entries-in-lookup_ghost.patch |   64 ++
 autofs-5.1.0-beta1-fix-wildcard-key-lookup.patch   |  170 ++++
 autofs.spec                                        |   18 +-
 5 files changed, 1628 insertions(+), 1 deletions(-)
---
diff --git a/autofs-5.0.8-amd-lookup-update-lookup-hesiod-to-handle-amd-keys.patch b/autofs-5.0.8-amd-lookup-update-lookup-hesiod-to-handle-amd-keys.patch
new file mode 100644
index 0000000..aae211d
--- /dev/null
+++ b/autofs-5.0.8-amd-lookup-update-lookup-hesiod-to-handle-amd-keys.patch
@@ -0,0 +1,423 @@
+autofs-5.0.8 - amd lookup update lookup hesiod to handle amd keys
+
+From: Ian Kent <raven at themaw.net>
+
+Warning, this is completely untested.
+
+I don't have a hesiod test environment so I can't test this at all.
+If we do in fact have hesiod users then I'll need to work with them
+to fix any reported problems.
+---
+ CHANGELOG               |    3 
+ modules/lookup_hesiod.c |  330 ++++++++++++++++++++++++++++++++++++++---------
+ 2 files changed, 272 insertions(+), 61 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 16a8ab4..f44f6f5 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -4,7 +4,8 @@
+ - add amd map format parser.
+ - amd lookup update lookup ldap to handle amd keys
+   - inadvertantly drop from initial series.
+-
++- amd lookup update lookup hesiod to handle amd keys
++  - inadvertantly drop from initial series.
+ 
+ 28/03/2014 autofs-5.0.9
+ =======================
+diff --git a/modules/lookup_hesiod.c b/modules/lookup_hesiod.c
+index c4f3558..526f294 100644
+--- a/modules/lookup_hesiod.c
++++ b/modules/lookup_hesiod.c
+@@ -20,12 +20,15 @@
+ #include "automount.h"
+ #include "nsswitch.h"
+ 
+-#define MAPFMT_DEFAULT "hesiod"
++#define MAPFMT_DEFAULT	   "hesiod"
++#define AMD_MAP_PREFIX	   "hesiod."
++#define AMD_MAP_PREFIX_LEN 7
+ 
+ #define MODPREFIX "lookup(hesiod): "
+ #define HESIOD_LEN 512
+ 
+ struct lookup_context {
++	const char *mapname;
+ 	struct parse_mod *parser;
+ 	void *hesiod_context;
+ };
+@@ -50,6 +53,7 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **co
+ 		logerr(MODPREFIX "malloc: %s", estr);
+ 		return 1;
+ 	}
++	memset(ctxt, 0, sizeof(struct lookup_context));
+ 
+ 	/* Initialize the resolver. */
+ 	res_init();
+@@ -66,6 +70,20 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **co
+ 	if (!mapfmt)
+ 		mapfmt = MAPFMT_DEFAULT;
+ 
++	if (!strcmp(mapfmt, "amd")) {
++		/* amd formated hesiod maps have a map name */
++		const char *mapname = argv[0];
++		if (strncmp(mapname, AMD_MAP_PREFIX, AMD_MAP_PREFIX_LEN)) {
++			logerr(MODPREFIX
++			      "incorrect prefix for hesiod map %s", mapname);
++			free(ctxt);
++			return 1;
++		}
++		ctxt->mapname = mapname;
++		argc--;
++		argv++;
++	}
++
+ 	/* Open the parser, if we can. */
+ 	ctxt->parser = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1);
+ 	if (!ctxt->parser) {
+@@ -97,16 +115,203 @@ int lookup_read_map(struct autofs_point *ap, time_t age, void *context)
+  * it's an ERR filesystem, it's an error message we should log.  Otherwise,
+  * assume it's something we know how to deal with already (generic).
+  */
++static int lookup_one(struct autofs_point *ap,
++		      struct map_source *source,
++		      const char *key, int key_len,
++		      struct lookup_context *ctxt)
++{
++	struct mapent_cache *mc;
++	char **hes_result;
++	char **record, *best_record = NULL, *p;
++	int priority, lowest_priority = INT_MAX;
++	int ret, status;
++
++	mc = source->mc;
++
++	status = pthread_mutex_lock(&hesiod_mutex);
++	if (status)
++		fatal(status);
++
++	hes_result = hesiod_resolve(ctxt->hesiod_context, key, "filsys");
++	if (!hes_result || !hes_result[0]) {
++		int err = errno;
++		error(ap->logopt,
++		      MODPREFIX "key \"%s\" not found in map", key);
++		status = pthread_mutex_unlock(&hesiod_mutex);
++		if (status)
++			fatal(status);
++		if (err == HES_ER_NOTFOUND)
++			return CHE_MISSING;
++		else
++			return CHE_FAIL;
++	}
++
++	/* autofs doesn't support falling back to alternate records, so just
++	   find the record with the lowest priority and hope it works.
++	   -- Aaron Ucko <amu at alum.mit.edu> 2002-03-11 */
++	for (record = hes_result; *record; ++record) {
++	    p = strrchr(*record, ' ');
++	    if ( p && isdigit(p[1]) ) {
++		priority = atoi(p+1);
++	    } else {
++		priority = INT_MAX - 1;
++	    }
++	    if (priority < lowest_priority) {
++		lowest_priority = priority;
++		best_record = *record;
++	    }
++	}
++
++	cache_writelock(mc);
++	ret = cache_update(mc, source, key, best_record, time(NULL));
++	cache_unlock(mc);
++	if (ret == CHE_FAIL) {
++		hesiod_free_list(ctxt->hesiod_context, hes_result);
++		status = pthread_mutex_unlock(&hesiod_mutex);
++		if (status)
++			fatal(status);
++		return ret;
++	}
++
++	debug(ap->logopt,
++	      MODPREFIX "lookup for \"%s\" gave \"%s\"",
++	      key, best_record);
++
++	hesiod_free_list(ctxt->hesiod_context, hes_result);
++
++	status = pthread_mutex_unlock(&hesiod_mutex);
++	if (status)
++		fatal(status);
++
++	return ret;
++}
++
++static int lookup_one_amd(struct autofs_point *ap,
++			  struct map_source *source,
++			  const char *key, int key_len,
++			  struct lookup_context *ctxt)
++{
++	struct mapent_cache *mc;
++	char *hesiod_base;
++	char **hes_result;
++	char *lkp_key;
++	int status, ret;
++
++	mc = source->mc;
++
++	hesiod_base = conf_amd_get_hesiod_base();
++	if (!hesiod_base)
++		return CHE_FAIL;
++
++	lkp_key = malloc(key_len + strlen(ctxt->mapname) - 7 + 2);
++	if (!lkp_key) {
++		free(hesiod_base);
++		return CHE_FAIL;
++	}
++
++	strcpy(lkp_key, key);
++	strcat(lkp_key, ".");
++	strcat(lkp_key, ctxt->mapname + AMD_MAP_PREFIX_LEN);
++
++	status = pthread_mutex_lock(&hesiod_mutex);
++	if (status)
++		fatal(status);
++
++	hes_result = hesiod_resolve(ctxt->hesiod_context, lkp_key, hesiod_base);
++	if (!hes_result || !hes_result[0]) {
++		int err = errno;
++		if (err == HES_ER_NOTFOUND)
++			ret = CHE_MISSING;
++		else
++			ret = CHE_FAIL;
++		goto done;
++	}
++
++	cache_writelock(mc);
++	ret = cache_update(mc, source, lkp_key, *hes_result, time(NULL));
++	cache_unlock(mc);
++
++	if (hes_result)
++		hesiod_free_list(ctxt->hesiod_context, hes_result);
++done:
++	free(lkp_key);
++
++	status = pthread_mutex_unlock(&hesiod_mutex);
++	if (status)
++		fatal(status);
++
++	return ret;
++}
++
++static int match_amd_key(struct autofs_point *ap,
++			 struct map_source *source,
++			 const char *key, int key_len,
++			 struct lookup_context *ctxt)
++{
++	char buf[MAX_ERR_BUF];
++	char *lkp_key;
++	char *prefix;
++	int ret;
++
++	ret = lookup_one_amd(ap, source, key, key_len, ctxt);
++	if (ret == CHE_OK || ret == CHE_UPDATED)
++		return ret;
++
++	lkp_key = strdup(key);
++	if (!lkp_key) {
++		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++		error(ap->logopt, MODPREFIX "strdup: %s", estr);
++		return CHE_FAIL;
++	}
++
++	ret = CHE_MISSING;
++
++	/*
++	 * Now strip successive directory components and try a
++	 * match against map entries ending with a wildcard and
++	 * finally try the wilcard entry itself.
++	 */
++	while ((prefix = strrchr(lkp_key, '/'))) {
++		char *match;
++		size_t len;
++		*prefix = '\0';
++		len = strlen(lkp_key) + 3;
++		match = malloc(len);
++		if (!match) {
++			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++			error(ap->logopt, MODPREFIX "malloc: %s", estr);
++			ret = CHE_FAIL;
++			goto done;
++		}
++		len--;
++		strcpy(match, lkp_key);
++		strcat(match, "/*");
++		ret = lookup_one_amd(ap, source, match, len, ctxt);
++		free(match);
++		if (ret == CHE_OK || ret == CHE_UPDATED)
++			goto done;
++	}
++
++	/* Lastly try the wildcard */
++	ret = lookup_one_amd(ap, source, "*", 1, ctxt);
++done:
++	free(lkp_key);
++	return ret;
++}
++
+ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *context)
+ {
+ 	struct lookup_context *ctxt = (struct lookup_context *) context;
+-	struct map_source *source;
+ 	struct mapent_cache *mc;
++	char buf[MAX_ERR_BUF];
++	struct map_source *source;
+ 	struct mapent *me;
+-	char **hes_result;
+-	int status, rv;
+-	char **record, *best_record = NULL, *p;
+-	int priority, lowest_priority = INT_MAX;	
++	char key[KEY_MAX_LEN + 1];
++	size_t key_len;
++	char *lkp_key;
++	size_t len;
++	char *mapent;
++	int rv;
+ 
+ 	source = ap->entry->current;
+ 	ap->entry->current = NULL;
+@@ -118,6 +323,19 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 	      MODPREFIX "looking up root=\"%s\", name=\"%s\"",
+ 	      ap->path, name);
+ 
++	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
++		key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
++		if (key_len > KEY_MAX_LEN)
++			return NSS_STATUS_NOTFOUND;
++	} else {
++		key_len = expandamdent(name, NULL, NULL);
++		if (key_len > KEY_MAX_LEN)
++			return NSS_STATUS_NOTFOUND;
++		expandamdent(name, key, NULL);
++		key[key_len] = '\0';
++		debug(ap->logopt, MODPREFIX "expanded key: \"%s\"", key);
++	}
++
+ 	/* Check if we recorded a mount fail for this key anywhere */
+ 	me = lookup_source_mapent(ap, name, LKP_DISTINCT);
+ 	if (me) {
+@@ -144,69 +362,61 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 		}
+ 	}
+ 
+-	chdir("/");		/* If this is not here the filesystem stays
+-				   busy, for some reason... */
+-
+-	status = pthread_mutex_lock(&hesiod_mutex);
+-	if (status)
+-		fatal(status);
+-
+-	hes_result = hesiod_resolve(ctxt->hesiod_context, name, "filsys");
+-	if (!hes_result || !hes_result[0]) {
+-		/* Note: it is not clear to me how to distinguish between
+-		 * the "no search results" case and other failures.  --JM */
+-		error(ap->logopt,
+-		      MODPREFIX "key \"%s\" not found in map", name);
+-		status = pthread_mutex_unlock(&hesiod_mutex);
+-		if (status)
+-			fatal(status);
+-		return NSS_STATUS_NOTFOUND;
++	/* If this is not here the filesystem stays busy, for some reason... */
++	if (chdir("/"))
++		warn(ap->logopt,
++		     MODPREFIX "failed to set working directory to \"/\"");
++
++	len = key_len;
++	if (!(source->flags & MAP_FLAG_FORMAT_AMD))
++		lkp_key = strdup(key);
++	else {
++		rv = lookup_one_amd(ap, source, "/defaults", 9, ctxt);
++		if (rv == CHE_FAIL)
++			warn(ap->logopt,
++			     MODPREFIX "failed to lookup \"/defaults\" entry");
++
++		if (!ap->pref)
++			lkp_key = strdup(key);
++		else {
++			len += strlen(ap->pref);
++			lkp_key = malloc(len + 1);
++			if (lkp_key) {
++				strcpy(lkp_key, ap->pref);
++				strcat(lkp_key, name);
++			}
++		}
+ 	}
+ 
+-	/* autofs doesn't support falling back to alternate records, so just
+-	   find the record with the lowest priority and hope it works.
+-	   -- Aaron Ucko <amu at alum.mit.edu> 2002-03-11 */
+-	for (record = hes_result; *record; ++record) {
+-	    p = strrchr(*record, ' ');
+-	    if ( p && isdigit(p[1]) ) {
+-		priority = atoi(p+1);
+-	    } else {
+-		priority = INT_MAX - 1;
+-	    }
+-	    if (priority < lowest_priority) {
+-		lowest_priority = priority;
+-		best_record = *record;
+-	    }
++	if (!lkp_key) {
++		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++		error(ap->logopt, "malloc: %s", estr);
++		return NSS_STATUS_UNKNOWN;
+ 	}
+ 
+-	cache_writelock(mc);
+-	rv = cache_update(mc, source, name, best_record, time(NULL));
+-	cache_unlock(mc);
+-	if (rv == CHE_FAIL)
+-		return NSS_STATUS_UNAVAIL;
++	if (source->flags & MAP_FLAG_FORMAT_AMD)
++		rv = match_amd_key(ap, source, lkp_key, len, ctxt);
++	else
++		rv = lookup_one(ap, source, lkp_key, len, ctxt);
+ 
+-	debug(ap->logopt,
+-	      MODPREFIX "lookup for \"%s\" gave \"%s\"",
+-	      name, best_record);
++	if (rv == CHE_FAIL) {
++		free(lkp_key);
++		return NSS_STATUS_UNAVAIL;
++	}
+ 
+-	rv = ctxt->parser->parse_mount(ap, name, name_len, best_record,
+-				       ctxt->parser->context);
++	me = match_cached_key(ap, MODPREFIX, source, lkp_key);
++	free(lkp_key);
++	if (!me)
++		return NSS_STATUS_NOTFOUND;
+ 
+-	hesiod_free_list(ctxt->hesiod_context, hes_result);
++	if (!me->mapent)
++		return NSS_STATUS_UNAVAIL;
+ 
+-	status = pthread_mutex_unlock(&hesiod_mutex);
+-	if (status)
+-		fatal(status);
++	mapent = strdup(me->mapent);
+ 
+-	if (rv) {
+-		/* Don't update negative cache when re-connecting */
+-		if (ap->flags & MOUNT_FLAG_REMOUNT)
+-			return NSS_STATUS_TRYAGAIN;
+-		cache_writelock(mc);
+-		cache_update_negative(mc, source, name, ap->negative_timeout);
+-		cache_unlock(mc);
+-		return NSS_STATUS_TRYAGAIN;
+-	}
++	rv = ctxt->parser->parse_mount(ap, key, key_len,
++				       mapent, ctxt->parser->context);
++	free(mapent);
+ 
+ 	/*
+ 	 * Unavailable due to error such as module load fail 
diff --git a/autofs-5.0.8-amd-lookup-update-lookup-ldap-to-handle-amd-keys.patch b/autofs-5.0.8-amd-lookup-update-lookup-ldap-to-handle-amd-keys.patch
new file mode 100644
index 0000000..a40d4ce
--- /dev/null
+++ b/autofs-5.0.8-amd-lookup-update-lookup-ldap-to-handle-amd-keys.patch
@@ -0,0 +1,954 @@
+autofs-5.0.8 - amd lookup update lookup ldap to handle amd keys
+
+From: Ian Kent <raven at themaw.net>
+
+
+---
+ CHANGELOG             |    3 
+ include/lookup_ldap.h |    3 
+ modules/lookup_ldap.c |  707 +++++++++++++++++++++++++++++++++++++++++++++----
+ 3 files changed, 654 insertions(+), 59 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 5cc1506..16a8ab4 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -2,6 +2,9 @@
+ =======================
+ - fix mistake in assignment.
+ - add amd map format parser.
++- amd lookup update lookup ldap to handle amd keys
++  - inadvertantly drop from initial series.
++
+ 
+ 28/03/2014 autofs-5.0.9
+ =======================
+diff --git a/include/lookup_ldap.h b/include/lookup_ldap.h
+index f34c029..ba817aa 100644
+--- a/include/lookup_ldap.h
++++ b/include/lookup_ldap.h
+@@ -36,6 +36,7 @@ struct ldap_searchdn {
+ 
+ struct lookup_context {
+ 	char *mapname;
++	unsigned int format;
+ 
+ 	char *server;
+ 	int port;
+@@ -43,6 +44,8 @@ struct lookup_context {
+ 	char *qdn;
+ 	unsigned int timeout;
+ 	unsigned int network_timeout;
++	unsigned long timestamp;
++	unsigned int check_defaults;
+ 
+ 	/* LDAP version 2 or 3 */
+ 	int version;
+diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c
+index c22d100..833cb86 100644
+--- a/modules/lookup_ldap.c
++++ b/modules/lookup_ldap.c
+@@ -29,6 +29,7 @@
+ #include <resolv.h>
+ #include <lber.h>
+ #include <libxml/tree.h>
++#include <stdlib.h>
+ 
+ #define MODULE_LOOKUP
+ #include "automount.h"
+@@ -52,6 +53,14 @@ static struct ldap_schema common_schema[] = {
+ };
+ static unsigned int common_schema_count = sizeof(common_schema)/sizeof(struct ldap_schema);
+ 
++static struct ldap_schema amd_timestamp = {
++	"madmap", "amdmapName", "amdmapTimestamp", NULL, "amdmapTimestamp"
++};
++
++static struct ldap_schema amd_schema = {
++	"amdmap", "amdmapName", "amdmap", "amdmapKey", "amdmapValue"
++};
++
+ /*
+  * Initialization and de-initialization of LDAP and OpenSSL must be
+  * always serialized to avoid corruption of context structures inside
+@@ -62,6 +71,7 @@ pthread_mutex_t ldapinit_mutex = PTHREAD_MUTEX_INITIALIZER;
+ struct ldap_search_params {
+ 	struct autofs_point *ap;
+ 	LDAP *ldap;
++	char *base;
+ 	char *query, **attrs;
+ 	struct berval *cookie;
+ 	ber_int_t pageSize;
+@@ -531,6 +541,16 @@ static int find_query_dn(unsigned logopt, LDAP *ldap, struct lookup_context *ctx
+ 	if (ctxt->schema)
+ 		return 0;
+ 
++	if (ctxt->format & MAP_FLAG_FORMAT_AMD) {
++		schema = alloc_common_schema(&amd_schema);
++		if (!schema) {
++			error(logopt, MODPREFIX "failed to allocate schema");
++			return 0;
++		}
++		ctxt->schema = schema;
++		return 1;
++	}
++
+ 	for (i = 0; i < common_schema_count; i++) {
+ 		const char *class = common_schema[i].map_class;
+ 		const char *key = common_schema[i].map_attr;
+@@ -587,8 +607,10 @@ static int do_bind(unsigned logopt, LDAP *ldap, const char *uri, struct lookup_c
+ 
+ 	if (!ctxt->cur_host) {
+ 		ctxt->cur_host = nhost;
+-		/* Check if schema defined in conf first time only */
+-		ctxt->schema = defaults_get_schema();
++		if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
++			/* Check if schema defined in conf first time only */
++			ctxt->schema = defaults_get_schema();
++		}
+ 	} else {
+ 		/* If connection host has changed update */
+ 		if (strcmp(ctxt->cur_host, nhost)) {
+@@ -614,7 +636,7 @@ static int do_bind(unsigned logopt, LDAP *ldap, const char *uri, struct lookup_c
+ 			      MODPREFIX "failed to find valid query dn");
+ 			return 0;
+ 		}
+-	} else {
++	} else if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
+ 		const char *class = ctxt->schema->map_class;
+ 		const char *key = ctxt->schema->map_attr;
+ 		if (!get_query_dn(logopt, ldap, ctxt, class, key)) {
+@@ -648,6 +670,126 @@ static LDAP *do_connect(unsigned logopt, const char *uri, struct lookup_context
+ 	return ldap;
+ }
+ 
++static unsigned long get_amd_timestamp(struct lookup_context *ctxt)
++{
++	LDAP *ldap;
++	LDAPMessage *result = NULL, *e;
++	char *query;
++	int scope = LDAP_SCOPE_SUBTREE;
++	char *map, *class, *value;
++	char *attrs[2];
++	struct berval **bvValues;
++	unsigned long timestamp = 0;
++	int rv, l, ql;
++
++	ldap = do_connect(LOGOPT_ANY, ctxt->server, ctxt);
++	if (!ldap)
++		return 0;
++
++	map = amd_timestamp.map_attr;
++	class = amd_timestamp.entry_class;
++	value = amd_timestamp.value_attr;
++
++	attrs[0] = value;
++	attrs[1] = NULL;
++
++	/* Build a query string. */
++	l = strlen(class) +
++	    strlen(map) + strlen(ctxt->mapname) + 21;
++
++	query = malloc(l);
++	if (query == NULL) {
++		char buf[MAX_ERR_BUF];
++		char *estr = strerror_r(errno, buf, sizeof(buf));
++		crit(LOGOPT_ANY, MODPREFIX "malloc: %s", estr);
++		return 0;
++	}
++
++	/*
++	 * Look for an entry in class under ctxt-base
++	 * whose entry is equal to qKey.
++	 */
++	ql = sprintf(query, "(&(objectclass=%s)(%s=%s))",
++		     class, map, ctxt->mapname);
++	if (ql >= l) {
++		error(LOGOPT_ANY,
++		      MODPREFIX "error forming query string");
++		free(query);
++		return 0;
++	}
++
++	rv = ldap_search_s(ldap, ctxt->base, scope, query, attrs, 0, &result);
++	if ((rv != LDAP_SUCCESS) || !result) {
++		crit(LOGOPT_ANY, MODPREFIX "timestamp query failed %s", query);
++		unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
++		if (result)
++			ldap_msgfree(result);
++		free(query);
++		return 0;
++	}
++
++	e = ldap_first_entry(ldap, result);
++	if (!e) {
++		debug(LOGOPT_ANY,
++		     MODPREFIX "got answer, but no entry for timestamp");
++		ldap_msgfree(result);
++		unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
++		free(query);
++		return CHE_MISSING;
++	}
++
++	while (e) {
++		char *v_val;
++		char *endptr;
++
++		bvValues = ldap_get_values_len(ldap, e, value);
++		if (!bvValues || !*bvValues) {
++			debug(LOGOPT_ANY,
++			      MODPREFIX "no value found in timestamp");
++			goto next;
++		}
++
++		/* There should be one value for a timestamp */
++		v_val = bvValues[0]->bv_val;
++
++		timestamp = strtol(v_val, &endptr, 0);
++		if ((errno == ERANGE &&
++		    (timestamp == LONG_MAX || timestamp == LONG_MIN)) ||
++		    (errno != 0 && timestamp == 0)) {
++			debug(LOGOPT_ANY,
++			      MODPREFIX "invalid value in timestamp");
++			free(query);
++			return 0;
++		}
++
++		if (endptr == v_val) {
++			debug(LOGOPT_ANY,
++			      MODPREFIX "no digits found in timestamp");
++			free(query);
++			return 0;
++		}
++
++		if (*endptr != '\0') {
++			warn(LOGOPT_ANY, MODPREFIX
++			     "characters found after number: %s", endptr);
++			warn(LOGOPT_ANY,
++			     MODPREFIX "timestamp may be invalid");
++		}
++
++		ldap_value_free_len(bvValues);
++		break;
++next:
++		ldap_value_free_len(bvValues);
++		e = ldap_next_entry(ldap, e);
++	}
++
++	ldap_msgfree(result);
++	unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
++	free(query);
++
++	return timestamp;
++}
++
+ static LDAP *connect_to_server(unsigned logopt, const char *uri, struct lookup_context *ctxt)
+ {
+ 	LDAP *ldap;
+@@ -1215,7 +1357,7 @@ static int parse_server_string(unsigned logopt, const char *url, struct lookup_c
+ 		const char *q = NULL;
+ 
+ 		/* Isolate the server(s). */
+-		if ((q = strchr(s, '/'))) {
++		if ((q = strchr(s, '/')) || (q = strchr(s, '\0'))) {
+ 			l = q - s;
+ 			if (*proto) {
+ 				al_len = l + strlen(proto) + 2;
+@@ -1318,8 +1460,7 @@ static int parse_server_string(unsigned logopt, const char *url, struct lookup_c
+ 		ptr += l + 1;
+ 	}
+ 
+-	/* TODO: why did I do this - how can the map name "and" base dn be missing? */
+-	if (!ptr)
++	if (!ptr || ctxt->format & MAP_FLAG_FORMAT_AMD)
+ 		goto done;
+ 
+ 	/*
+@@ -1505,36 +1646,83 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **co
+ 	/* If a map type isn't explicitly given, parse it like sun entries. */
+ 	if (mapfmt == NULL)
+ 		mapfmt = MAPFMT_DEFAULT;
+-
+-	/*
+-	 * Parse out the server name and base dn, and fill them
+-	 * into the proper places in the lookup context structure.
+-	 */
+-	if (!parse_server_string(LOGOPT_NONE, argv[0], ctxt)) {
+-		error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
+-		free_context(ctxt);
+-		return 1;
++	if (!strcmp(mapfmt, "amd")) {
++		ctxt->format = MAP_FLAG_FORMAT_AMD;
++		ctxt->check_defaults = 1;
+ 	}
+ 
+-	if (!ctxt->base)
+-		ctxt->sdns = defaults_get_searchdns();
+-
+ 	ctxt->timeout = defaults_get_ldap_timeout();
+ 	ctxt->network_timeout = defaults_get_ldap_network_timeout();
+ 
+-	if (!ctxt->server) {
+-		struct list_head *uris = defaults_get_uris();
+-		if (uris) {
+-			validate_uris(uris);
+-			if (!list_empty(uris))
+-				ctxt->uris = uris;
+-			else {
+-				error(LOGOPT_ANY,
+-				      "no valid uris found in config list"
+-				      ", using default system config");
+-				free(uris);
++	if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
++		/*
++		 * Parse out the server name and base dn, and fill them
++		 * into the proper places in the lookup context structure.
++		 */
++		if (!parse_server_string(LOGOPT_NONE, argv[0], ctxt)) {
++			error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
++			free_context(ctxt);
++			return 1;
++		}
++
++		if (!ctxt->base)
++			ctxt->sdns = defaults_get_searchdns();
++
++		if (!ctxt->server) {
++			struct list_head *uris = defaults_get_uris();
++			if (uris) {
++				validate_uris(uris);
++				if (!list_empty(uris))
++					ctxt->uris = uris;
++				else {
++					error(LOGOPT_ANY, MODPREFIX
++					    "no valid uris found in config list"
++					    ", using default system config");
++					free(uris);
++				}
+ 			}
+ 		}
++	} else {
++		char *tmp = conf_amd_get_ldap_base();
++		if (!tmp) {
++			error(LOGOPT_ANY, MODPREFIX "failed to get base dn");
++			free_context(ctxt);
++			return 1;
++		}
++		ctxt->base = tmp;
++
++		tmp = conf_amd_get_ldap_hostports();
++		if (!tmp) {
++			error(LOGOPT_ANY,
++			      MODPREFIX "failed to get ldap_hostports");
++			free_context(ctxt);
++			return 1;
++		}
++
++		/*
++		 * Parse out the server name and port, and save them in
++		 * the proper places in the lookup context structure.
++		 */
++		if (!parse_server_string(LOGOPT_NONE, tmp, ctxt)) {
++			error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
++			free_context(ctxt);
++			return 1;
++		}
++		free(tmp);
++
++		if (!ctxt->server) {
++			error(LOGOPT_ANY, MODPREFIX "ldap_hostports not valid");
++			free_context(ctxt);
++			return 1;
++		}
++
++		tmp = strdup(argv[0]);
++		if (!tmp) {
++			error(LOGOPT_ANY, MODPREFIX "failed to set mapname");
++			free_context(ctxt);
++			return 1;
++		}
++		ctxt->mapname = tmp;
+ 	}
+ 
+ 	/*
+@@ -1558,6 +1746,8 @@ int lookup_init(const char *mapfmt, int argc, const char *const *argv, void **co
+ 	}
+ #endif
+ 
++	ctxt->timestamp = get_amd_timestamp(ctxt);
++
+ 	/* Open the parser, if we can. */
+ 	ctxt->parse = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1);
+ 	if (!ctxt->parse) {
+@@ -2029,7 +2219,7 @@ static int do_paged_query(struct ldap_search_params *sp, struct lookup_context *
+ 	if (sp->morePages == TRUE)
+ 		goto do_paged;
+ 
+-	rv = ldap_search_s(sp->ldap, ctxt->qdn, scope, sp->query, sp->attrs, 0, &sp->result);
++	rv = ldap_search_s(sp->ldap, sp->base, scope, sp->query, sp->attrs, 0, &sp->result);
+ 	if ((rv != LDAP_SUCCESS) || !sp->result) {
+ 		/*
+  		 * Check for Size Limit exceeded and force run through loop
+@@ -2063,7 +2253,7 @@ do_paged:
+ 
+ 	/* Search for entries in the directory using the parmeters. */
+ 	rv = ldap_search_ext_s(sp->ldap,
+-			       ctxt->qdn, scope, sp->query, sp->attrs,
++			       sp->base, scope, sp->query, sp->attrs,
+ 			       0, controls, NULL, NULL, 0, &sp->result);
+ 	if ((rv != LDAP_SUCCESS) && (rv != LDAP_PARTIAL_RESULTS)) {
+ 		ldap_control_free(pageControl);
+@@ -2364,6 +2554,115 @@ next:
+ 	return LDAP_SUCCESS;
+ }
+ 
++static int do_get_amd_entries(struct ldap_search_params *sp,
++			      struct map_source *source,
++			      struct lookup_context *ctxt)
++{
++	struct autofs_point *ap = sp->ap;
++	struct mapent_cache *mc = source->mc;
++	struct berval **bvKey;
++	struct berval **bvValues;
++	LDAPMessage *e;
++	char *entry, *value;
++	int rv, ret, count;
++
++	entry = ctxt->schema->entry_attr;
++	value = ctxt->schema->value_attr;
++
++	e = ldap_first_entry(sp->ldap, sp->result);
++	if (!e) {
++		debug(ap->logopt,
++		      MODPREFIX "query succeeded, no matches for %s",
++		      sp->query);
++		ret = ldap_parse_result(sp->ldap, sp->result,
++					&rv, NULL, NULL, NULL, NULL, 0);
++		if (ret == LDAP_SUCCESS)
++			return rv;
++		else
++			return LDAP_OPERATIONS_ERROR;
++	} else
++		debug(ap->logopt, MODPREFIX "examining entries");
++
++	while (e) {
++		char *k_val, *v_val;
++		ber_len_t k_len;
++		char *s_key;
++
++		bvKey = ldap_get_values_len(sp->ldap, e, entry);
++		if (!bvKey || !*bvKey) {
++			e = ldap_next_entry(sp->ldap, e);
++			if (!e) {
++				debug(ap->logopt, MODPREFIX
++				      "failed to get next entry for query %s",
++				      sp->query);
++				ret = ldap_parse_result(sp->ldap,
++							sp->result, &rv,
++							NULL, NULL, NULL, NULL, 0);
++				if (ret == LDAP_SUCCESS)
++					return rv;
++				else
++					return LDAP_OPERATIONS_ERROR;
++			}
++			continue;
++		}
++
++		/* By definition keys should be unique within each map entry */
++		k_val = NULL;
++		k_len = 0;
++
++		count = ldap_count_values_len(bvKey);
++		if (count > 1)
++			warn(ap->logopt, MODPREFIX
++			     "more than one %s, using first", entry);
++
++		k_val = bvKey[0]->bv_val;
++		k_len = bvKey[0]->bv_len;
++
++		bvValues = ldap_get_values_len(sp->ldap, e, value);
++		if (!bvValues || !*bvValues) {
++			debug(ap->logopt,
++			      MODPREFIX "no %s defined for %s",
++			      value, sp->query);
++			goto next;
++		}
++
++		count = ldap_count_values_len(bvValues);
++		if (count > 1)
++			warn(ap->logopt, MODPREFIX
++			     "more than one %s, using first", value);
++
++		v_val = bvValues[0]->bv_val;
++
++		/* Don't fail on "/" in key => type == 0 */
++		s_key = sanitize_path(k_val, k_len, 0, ap->logopt);
++		if (!s_key)
++			goto next;
++
++		cache_writelock(mc);
++		cache_update(mc, source, s_key, v_val, sp->age);
++		cache_unlock(mc);
++
++		free(s_key);
++next:
++		ldap_value_free_len(bvValues);
++		ldap_value_free_len(bvKey);
++		e = ldap_next_entry(sp->ldap, e);
++		if (!e) {
++			debug(ap->logopt, MODPREFIX
++			      "failed to get next entry for query %s",
++			      sp->query);
++			ret = ldap_parse_result(sp->ldap,
++						sp->result, &rv,
++						NULL, NULL, NULL, NULL, 0);
++			if (ret == LDAP_SUCCESS)
++				return rv;
++			else
++				return LDAP_OPERATIONS_ERROR;
++		}
++	}
++
++	return LDAP_SUCCESS;
++}
+ 
+ static int read_one_map(struct autofs_point *ap,
+ 			struct map_source *source,
+@@ -2419,9 +2718,14 @@ static int read_one_map(struct autofs_point *ap,
+ 		return NSS_STATUS_UNAVAIL;
+ 	}
+ 
++	if (ctxt->format & MAP_FLAG_FORMAT_AMD)
++		sp.base = ctxt->base;
++	else
++		sp.base = ctxt->qdn;
++
+ 	/* Look around. */
+ 	debug(ap->logopt,
+-	      MODPREFIX "searching for \"%s\" under \"%s\"", sp.query, ctxt->qdn);
++	      MODPREFIX "searching for \"%s\" under \"%s\"", sp.query, sp.base);
+ 
+ 	sp.cookie = NULL;
+ 	sp.pageSize = 2000;
+@@ -2465,7 +2769,10 @@ static int read_one_map(struct autofs_point *ap,
+ 			return NSS_STATUS_UNAVAIL;
+ 		}
+ 
+-		rv = do_get_entries(&sp, source, ctxt);
++		if (source->flags & MAP_FLAG_FORMAT_AMD)
++			rv = do_get_amd_entries(&sp, source, ctxt);
++		else
++			rv = do_get_entries(&sp, source, ctxt);
+ 		if (rv != LDAP_SUCCESS) {
+ 			ldap_msgfree(sp.result);
+ 			unbind_ldap_connection(ap->logopt, sp.ldap, ctxt);
+@@ -2874,6 +3181,219 @@ next:
+ 	return ret;
+ }
+ 
++static int lookup_one_amd(struct autofs_point *ap,
++			  struct map_source *source,
++			  char *qKey, int qKey_len,
++			  struct lookup_context *ctxt)
++{
++	struct mapent_cache *mc = source->mc;
++	LDAP *ldap;
++	LDAPMessage *result = NULL, *e;
++	char *query;
++	int scope = LDAP_SCOPE_SUBTREE;
++	char *map, *class, *entry, *value;
++	char *attrs[3];
++	struct berval **bvKey;
++	struct berval **bvValues;
++	char buf[MAX_ERR_BUF];
++	time_t age = time(NULL);
++	int rv, l, ql, count;
++	int ret = CHE_MISSING;
++
++	if (ctxt == NULL) {
++		crit(ap->logopt, MODPREFIX "context was NULL");
++		return CHE_FAIL;
++	}
++
++	/* Initialize the LDAP context. */
++	ldap = do_reconnect(ap->logopt, ctxt);
++	if (!ldap)
++		return CHE_UNAVAIL;
++
++	map = ctxt->schema->map_attr;
++	class = ctxt->schema->entry_class;
++	entry = ctxt->schema->entry_attr;
++	value = ctxt->schema->value_attr;
++
++	attrs[0] = entry;
++	attrs[1] = value;
++	attrs[2] = NULL;
++
++	/* Build a query string. */
++	l = strlen(class) +
++	    strlen(map) + strlen(ctxt->mapname) +
++	    strlen(entry) + strlen(qKey) + 24;
++
++	query = malloc(l);
++	if (query == NULL) {
++		char *estr = strerror_r(errno, buf, sizeof(buf));
++		crit(ap->logopt, MODPREFIX "malloc: %s", estr);
++		return CHE_FAIL;
++	}
++
++	/*
++	 * Look for an entry in class under ctxt-base
++	 * whose entry is equal to qKey.
++	 */
++	ql = sprintf(query, "(&(objectclass=%s)(%s=%s)(%s=%s))",
++		     class, map, ctxt->mapname, entry, qKey);
++	if (ql >= l) {
++		error(ap->logopt,
++		      MODPREFIX "error forming query string");
++		free(query);
++		return CHE_FAIL;
++	}
++
++	debug(ap->logopt,
++	      MODPREFIX "searching for \"%s\" under \"%s\"", query, ctxt->base);
++
++	rv = ldap_search_s(ldap, ctxt->base, scope, query, attrs, 0, &result);
++	if ((rv != LDAP_SUCCESS) || !result) {
++		crit(ap->logopt, MODPREFIX "query failed for %s", query);
++		unbind_ldap_connection(ap->logopt, ldap, ctxt);
++		if (result)
++			ldap_msgfree(result);
++		free(query);
++		return CHE_FAIL;
++	}
++
++	debug(ap->logopt,
++	      MODPREFIX "getting first entry for %s=\"%s\"", entry, qKey);
++
++	e = ldap_first_entry(ldap, result);
++	if (!e) {
++		debug(ap->logopt,
++		     MODPREFIX "got answer, but no entry for %s", query);
++		ldap_msgfree(result);
++		unbind_ldap_connection(ap->logopt, ldap, ctxt);
++		free(query);
++		return CHE_MISSING;
++	}
++
++	while (e) {
++		char *k_val, *v_val;
++		ber_len_t k_len;
++		char *s_key;
++
++		bvKey = ldap_get_values_len(ldap, e, entry);
++		if (!bvKey || !*bvKey) {
++			e = ldap_next_entry(ldap, e);
++			continue;
++		}
++
++		/* By definition keys should be unique within each map entry */
++		k_val = NULL;
++		k_len = 0;
++
++		count = ldap_count_values_len(bvKey);
++		if (count > 1)
++			warn(ap->logopt, MODPREFIX
++			     "more than one %s, using first", entry);
++
++		k_val = bvKey[0]->bv_val;
++		k_len = bvKey[0]->bv_len;
++
++		debug(ap->logopt, MODPREFIX "examining first entry");
++
++		bvValues = ldap_get_values_len(ldap, e, value);
++		if (!bvValues || !*bvValues) {
++			debug(ap->logopt,
++			      MODPREFIX "no %s defined for %s", value, query);
++			goto next;
++		}
++
++		count = ldap_count_values_len(bvValues);
++		if (count > 1)
++			warn(ap->logopt, MODPREFIX
++			     "more than one %s, using first", value);
++
++		/* There should be one value for a key, use first value */
++		v_val = bvValues[0]->bv_val;
++
++		/* Don't fail on "/" in key => type == 0 */
++		s_key = sanitize_path(k_val, k_len, 0, ap->logopt);
++		if (!s_key)
++			goto next;
++
++		cache_writelock(mc);
++		ret = cache_update(mc, source, s_key, v_val, age);
++		cache_unlock(mc);
++
++		free(s_key);
++next:
++		ldap_value_free_len(bvValues);
++		ldap_value_free_len(bvKey);
++		e = ldap_next_entry(ldap, e);
++	}
++
++	ldap_msgfree(result);
++	unbind_ldap_connection(ap->logopt, ldap, ctxt);
++	free(query);
++
++	return ret;
++}
++
++static int match_key(struct autofs_point *ap,
++		     struct map_source *source,
++		     char *key, int key_len,
++		     struct lookup_context *ctxt)
++{
++	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
++	char buf[MAX_ERR_BUF];
++	char *lkp_key;
++	char *prefix;
++	int ret;
++
++	if (is_amd_format)
++		ret = lookup_one_amd(ap, source, key, key_len, ctxt);
++	else
++		ret = lookup_one(ap, source, key, key_len, ctxt);
++
++	if (ret == CHE_OK || ret == CHE_UPDATED)
++		return ret;
++
++	if (!is_amd_format)
++		return CHE_FAIL;
++
++	lkp_key = strdup(key);
++	if (!lkp_key) {
++		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++		error(ap->logopt, MODPREFIX "strdup: %s", estr);
++		return CHE_FAIL;
++	}
++
++	ret = CHE_MISSING;
++
++	/*
++	 * Now strip successive directory components and try a
++	 * match against map entries ending with a wildcard and
++	 * finally try the wilcard entry itself.
++	 */
++	while ((prefix = strrchr(lkp_key, '/'))) {
++		char *match;
++		size_t len;
++		*prefix = '\0';
++		len = strlen(lkp_key + 3);
++		match = malloc(len);
++		if (!match) {
++			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++			error(ap->logopt, MODPREFIX "malloc: %s", estr);
++			ret = CHE_FAIL;
++			goto done;
++		}
++		len--;
++		strcpy(match, lkp_key);
++		strcat(match, "/*");
++		ret = lookup_one_amd(ap, source, match, len, ctxt);
++		free(match);
++		if (ret == CHE_OK || ret == CHE_UPDATED)
++			goto done;
++	}
++done:
++	free(lkp_key);
++	return ret;
++}
++
+ static int check_map_indirect(struct autofs_point *ap,
+ 			      struct map_source *source,
+ 			      char *key, int key_len,
+@@ -2888,16 +3408,43 @@ static int check_map_indirect(struct autofs_point *ap,
+ 	mc = source->mc;
+ 
+ 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
+-	ret = lookup_one(ap, source, key, key_len, ctxt);
++
++	pthread_mutex_lock(&ap->entry->current_mutex);
++	if (source->flags & MAP_FLAG_FORMAT_AMD) {
++		unsigned long timestamp = get_amd_timestamp(ctxt);
++		if (timestamp > ctxt->timestamp) {
++			ctxt->timestamp = timestamp;
++			source->stale = 1;
++			ctxt->check_defaults = 1;
++		}
++
++		if (ctxt->check_defaults) {
++			/* Check for a /defaults entry */
++			ret = lookup_one_amd(ap, source, "/defaults", 9, ctxt);
++			if (ret == CHE_FAIL) {
++				warn(ap->logopt, MODPREFIX
++				     "error getting /defaults from map %s",
++				     ctxt->mapname);
++			} else
++				ctxt->check_defaults = 0;
++		}
++	}
++	pthread_mutex_unlock(&ap->entry->current_mutex);
++
++	ret = match_key(ap, source, key, key_len, ctxt);
+ 	if (ret == CHE_FAIL) {
+ 		pthread_setcancelstate(cur_state, NULL);
+ 		return NSS_STATUS_NOTFOUND;
+ 	} else if (ret == CHE_UNAVAIL) {
++		struct mapent *exists;
+ 		/*
+ 		 * If the server is down and the entry exists in the cache
+ 		 * and belongs to this map return success and use the entry.
+ 		 */
+-		struct mapent *exists = cache_lookup(mc, key);
++		if (source->flags & MAP_FLAG_FORMAT_AMD)
++			exists = match_cached_key(ap, MODPREFIX, source, key);
++		else
++			exists = cache_lookup(mc, key);
+ 		if (exists && exists->source == source) {
+ 			pthread_setcancelstate(cur_state, NULL);
+ 			return NSS_STATUS_SUCCESS;
+@@ -2910,24 +3457,28 @@ static int check_map_indirect(struct autofs_point *ap,
+ 	}
+ 	pthread_setcancelstate(cur_state, NULL);
+ 
+-	/*
+-	 * Check for map change and update as needed for
+-	 * following cache lookup.
+-	 */
+-	cache_readlock(mc);
+-	t_last_read = ap->exp_runfreq + 1;
+-	me = cache_lookup_first(mc);
+-	while (me) {
+-		if (me->source == source) {
+-			t_last_read = now - me->age;
+-			break;
++	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
++		/*
++		 * Check for map change and update as needed for
++		 * following cache lookup.
++		 */
++		cache_readlock(mc);
++		t_last_read = ap->exp_runfreq + 1;
++		me = cache_lookup_first(mc);
++		while (me) {
++			if (me->source == source) {
++				t_last_read = now - me->age;
++				break;
++			}
++			me = cache_lookup_next(mc, me);
+ 		}
+-		me = cache_lookup_next(mc, me);
+-	}
+-	cache_unlock(mc);
++		cache_unlock(mc);
+ 
+-	if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
+-		source->stale = 1;
++		pthread_mutex_lock(&ap->entry->current_mutex);
++		if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
++			source->stale = 1;
++		pthread_mutex_unlock(&ap->entry->current_mutex);
++	}
+ 
+ 	cache_readlock(mc);
+ 	me = cache_lookup_distinct(mc, "*");
+@@ -2948,8 +3499,10 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 	struct mapent *me;
+ 	char key[KEY_MAX_LEN + 1];
+ 	int key_len;
++	char *lkp_key;
+ 	char *mapent = NULL;
+ 	char mapent_buf[MAPENT_MAX_LEN + 1];
++	char buf[MAX_ERR_BUF];
+ 	int status = 0;
+ 	int ret = 1;
+ 
+@@ -2961,9 +3514,18 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 
+ 	debug(ap->logopt, MODPREFIX "looking up %s", name);
+ 
+-	key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
+-	if (key_len > KEY_MAX_LEN)
+-		return NSS_STATUS_NOTFOUND;
++	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
++		key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
++		if (key_len > KEY_MAX_LEN)
++			return NSS_STATUS_NOTFOUND;
++	} else {
++		key_len = expandamdent(name, NULL, NULL);
++		if (key_len > KEY_MAX_LEN)
++			return NSS_STATUS_NOTFOUND;
++		expandamdent(name, key, NULL);
++		key[key_len] = '\0';
++		debug(ap->logopt, MODPREFIX "expanded key: \"%s\"", key);
++	}
+ 
+ 	/* Check if we recorded a mount fail for this key anywhere */
+ 	me = lookup_source_mapent(ap, key, LKP_DISTINCT);
+@@ -2997,18 +3559,26 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 	 * we never know about it.
+ 	 */
+ 	if (ap->type == LKP_INDIRECT && *key != '/') {
+-		char *lkp_key;
+-
+ 		cache_readlock(mc);
+ 		me = cache_lookup_distinct(mc, key);
+ 		if (me && me->multi)
+ 			lkp_key = strdup(me->multi->key);
+-		else
++		else if (!ap->pref)
+ 			lkp_key = strdup(key);
++		else {
++			lkp_key = malloc(strlen(ap->pref) + strlen(key) + 1);
++			if (lkp_key) {
++				strcpy(lkp_key, ap->pref);
++				strcat(lkp_key, key);
++			}
++		}
+ 		cache_unlock(mc);
+ 
+-		if (!lkp_key)
++		if (!lkp_key) {
++			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++			error(ap->logopt, MODPREFIX "malloc: %s", estr);
+ 			return NSS_STATUS_UNKNOWN;
++		}
+ 
+ 		status = check_map_indirect(ap, source,
+ 					    lkp_key, strlen(lkp_key), ctxt);
+@@ -3029,7 +3599,25 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 		cache_readlock(mc);
+ 	else
+ 		cache_writelock(mc);
+-	me = cache_lookup(mc, key);
++
++	if (!ap->pref)
++		lkp_key = strdup(key);
++	else {
++		lkp_key = malloc(strlen(ap->pref) + strlen(key) + 1);
++		if (lkp_key) {
++			strcpy(lkp_key, ap->pref);
++			strcat(lkp_key, key);
++		}
++	}
++
++	if (!lkp_key) {
++		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
++		error(ap->logopt, MODPREFIX "malloc: %s", estr);
++		cache_unlock(mc);
++		return NSS_STATUS_UNKNOWN;
++	}
++
++	me = match_cached_key(ap, MODPREFIX, source, lkp_key);
+ 	/* Stale mapent => check for entry in alternate source or wildcard */
+ 	if (me && !me->mapent) {
+ 		while ((me = cache_lookup_key_next(me)))
+@@ -3055,6 +3643,7 @@ int lookup_mount(struct autofs_point *ap, const char *name, int name_len, void *
+ 		}
+ 	}
+ 	cache_unlock(mc);
++	free(lkp_key);
+ 
+ 	if (!mapent)
+ 		return NSS_STATUS_TRYAGAIN;
diff --git a/autofs-5.0.9-check-for-non-existent-negative-entries-in-lookup_ghost.patch b/autofs-5.0.9-check-for-non-existent-negative-entries-in-lookup_ghost.patch
new file mode 100644
index 0000000..f313140
--- /dev/null
+++ b/autofs-5.0.9-check-for-non-existent-negative-entries-in-lookup_ghost.patch
@@ -0,0 +1,64 @@
+autofs-5.0.9 - check for non existent negative entries in lookup_ghost()
+
+From: Ian Kent <raven at themaw.net>
+
+Map entries that have been created in the cache due to a negative lookup
+but don't exist in the map source shouldn't have directories created.
+
+This can be detected by checking me->status.
+
+For negative entries that are present in the map source me->status will
+have been set to 0 in lookup_prune_one_cache() and negavive entries that
+have been created in the same second as the map read will always have
+me->status > 0 and so will also be skipped by lookup_ghost().
+---
+ CHANGELOG       |    1 +
+ daemon/lookup.c |   16 +++++++++++++++-
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index 8c1da44..113dfb8 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -7,6 +7,7 @@
+ - amd lookup update lookup hesiod to handle amd keys
+   - inadvertantly drop from initial series.
+ - fix wildcard key lookup.
++- check for non existent negative entries in lookup_ghost().
+ 
+ 28/03/2014 autofs-5.0.9
+ =======================
+diff --git a/daemon/lookup.c b/daemon/lookup.c
+index 999be9d..b4cdcce 100644
+--- a/daemon/lookup.c
++++ b/daemon/lookup.c
+@@ -716,6 +716,17 @@ int lookup_ghost(struct autofs_point *ap, const char *root)
+ 		cache_readlock(mc);
+ 		me = cache_enumerate(mc, NULL);
+ 		while (me) {
++			/*
++			 * Map entries that have been created in the cache
++			 * due to a negative lookup but don't exist in the
++			 * map source shouldn't have directories created.
++			 * me->status of negative entries that are present
++			 * in the map source will have me->status set to 0
++			 * in lookup_prune_one_cache().
++			 */
++			if (me->status && !me->mapent)
++				goto next;
++
+ 			if (!strcmp(me->key, "*"))
+ 				goto next;
+ 
+@@ -1339,7 +1350,10 @@ void lookup_prune_one_cache(struct autofs_point *ap, struct mapent_cache *mc, ti
+ 
+ 		if (valid)
+ 			cache_delete(mc, key);
+-		else if (!is_mounted(_PROC_MOUNTS, path, MNTS_AUTOFS)) {
++		else if (this->status) {
++			cache_unlock(mc);
++			goto next;
++		 } else if (!is_mounted(_PROC_MOUNTS, path, MNTS_AUTOFS)) {
+ 			dev_t devid = ap->dev;
+ 			status = CHE_FAIL;
+ 			if (ap->type == LKP_DIRECT)
diff --git a/autofs-5.1.0-beta1-fix-wildcard-key-lookup.patch b/autofs-5.1.0-beta1-fix-wildcard-key-lookup.patch
new file mode 100644
index 0000000..3b50780
--- /dev/null
+++ b/autofs-5.1.0-beta1-fix-wildcard-key-lookup.patch
@@ -0,0 +1,170 @@
+autofs-5.1.0-beta1 - fix wildcard key lookup
+
+From: Ian Kent <raven at themaw.net>
+
+The changes to key matching caused wildcard key lookups for autofs
+format maps to fail.
+---
+ CHANGELOG                |    1 +
+ modules/lookup_ldap.c    |   10 ++++------
+ modules/lookup_nisplus.c |   11 +++++------
+ modules/lookup_program.c |    4 ++--
+ modules/lookup_yp.c      |    6 ++----
+ 5 files changed, 14 insertions(+), 18 deletions(-)
+
+diff --git a/CHANGELOG b/CHANGELOG
+index f44f6f5..8c1da44 100644
+--- a/CHANGELOG
++++ b/CHANGELOG
+@@ -6,6 +6,7 @@
+   - inadvertantly drop from initial series.
+ - amd lookup update lookup hesiod to handle amd keys
+   - inadvertantly drop from initial series.
++- fix wildcard key lookup.
+ 
+ 28/03/2014 autofs-5.0.9
+ =======================
+diff --git a/modules/lookup_ldap.c b/modules/lookup_ldap.c
+index 833cb86..dac346c 100644
+--- a/modules/lookup_ldap.c
++++ b/modules/lookup_ldap.c
+@@ -3349,12 +3349,9 @@ static int match_key(struct autofs_point *ap,
+ 	else
+ 		ret = lookup_one(ap, source, key, key_len, ctxt);
+ 
+-	if (ret == CHE_OK || ret == CHE_UPDATED)
++	if (ret == CHE_OK || ret == CHE_UPDATED || !is_amd_format)
+ 		return ret;
+ 
+-	if (!is_amd_format)
+-		return CHE_FAIL;
+-
+ 	lkp_key = strdup(key);
+ 	if (!lkp_key) {
+ 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+@@ -3399,6 +3396,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 			      char *key, int key_len,
+ 			      struct lookup_context *ctxt)
+ {
++	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
+ 	struct mapent_cache *mc;
+ 	struct mapent *me;
+ 	time_t now = time(NULL);
+@@ -3410,7 +3408,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
+ 
+ 	pthread_mutex_lock(&ap->entry->current_mutex);
+-	if (source->flags & MAP_FLAG_FORMAT_AMD) {
++	if (is_amd_format) {
+ 		unsigned long timestamp = get_amd_timestamp(ctxt);
+ 		if (timestamp > ctxt->timestamp) {
+ 			ctxt->timestamp = timestamp;
+@@ -3457,7 +3455,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 	}
+ 	pthread_setcancelstate(cur_state, NULL);
+ 
+-	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
++	if (!is_amd_format) {
+ 		/*
+ 		 * Check for map change and update as needed for
+ 		 * following cache lookup.
+diff --git a/modules/lookup_nisplus.c b/modules/lookup_nisplus.c
+index e9444c9..db1b162 100644
+--- a/modules/lookup_nisplus.c
++++ b/modules/lookup_nisplus.c
+@@ -339,6 +339,7 @@ static int match_key(struct autofs_point *ap,
+ 		     const char *key, int key_len,
+ 		     struct lookup_context *ctxt)
+ {
++	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
+ 	char buf[MAX_ERR_BUF];
+ 	char *lkp_key;
+ 	char *prefix;
+@@ -347,12 +348,9 @@ static int match_key(struct autofs_point *ap,
+ 	ret = lookup_one(ap, source, key, key_len, ctxt);
+ 	if (ret < 0)
+ 		return ret;
+-	if (ret == CHE_OK || ret == CHE_UPDATED)
++	if (ret == CHE_OK || ret == CHE_UPDATED || is_amd_format)
+ 		return ret;
+ 
+-	if (!(source->flags & MAP_FLAG_FORMAT_AMD))
+-		return CHE_FAIL;
+-
+ 	lkp_key = strdup(key);
+ 	if (!lkp_key) {
+ 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
+@@ -504,6 +502,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 			      char *key, int key_len,
+ 			      struct lookup_context *ctxt)
+ {
++	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
+ 	struct mapent_cache *mc;
+ 	struct mapent *me, *exists;
+ 	time_t now = time(NULL);
+@@ -512,7 +511,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 
+ 	mc = source->mc;
+ 
+-	if (source->flags & MAP_FLAG_FORMAT_AMD) {
++	if (is_amd_format) {
+ 		/* Check for a /defaults entry to update the map source */
+ 		if (lookup_amd_defaults(ap, source, ctxt) == CHE_FAIL) {
+ 			warn(ap->logopt, MODPREFIX
+@@ -559,7 +558,7 @@ static int check_map_indirect(struct autofs_point *ap,
+ 		}
+ 		me = cache_lookup_next(mc, me);
+ 	}
+-	if (source->flags & MAP_FLAG_FORMAT_AMD)
++	if (is_amd_format)
+ 		exists = match_cached_key(ap, MODPREFIX, source, key);
+ 	else
+ 		exists = cache_lookup_distinct(mc, key);
+diff --git a/modules/lookup_program.c b/modules/lookup_program.c
+index 08d14ff..aae0ec0 100644
+--- a/modules/lookup_program.c
++++ b/modules/lookup_program.c
+@@ -382,7 +382,7 @@ static int match_key(struct autofs_point *ap,
+ 	char *prefix;
+ 	int ret;
+ 
+-	if (source->flags & MAP_FLAG_FORMAT_AMD) {
++	if (is_amd_format) {
+ 		ret = lookup_amd_defaults(ap, source, ctxt);
+ 		if (ret != NSS_STATUS_SUCCESS) {
+ 			warn(ap->logopt,
+@@ -420,7 +420,7 @@ static int match_key(struct autofs_point *ap,
+ 	ment = lookup_one(ap, lkp_key, lkp_len, ctxt);
+ 	if (ment) {
+ 		char *start = ment;
+-		if (source->flags & MAP_FLAG_FORMAT_AMD) {
++		if (is_amd_format) {
+ 			start = ment + lkp_len;
+ 			while (isblank(*start))
+ 				start++;
+diff --git a/modules/lookup_yp.c b/modules/lookup_yp.c
+index 146e39e..fcf470a 100644
+--- a/modules/lookup_yp.c
++++ b/modules/lookup_yp.c
+@@ -457,6 +457,7 @@ static int match_key(struct autofs_point *ap,
+ 		     const char *key, int key_len,
+ 		     struct lookup_context *ctxt)
+ {
++	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
+ 	char buf[MAX_ERR_BUF];
+ 	char *lkp_key;
+ 	char *prefix;
+@@ -465,12 +466,9 @@ static int match_key(struct autofs_point *ap,
+ 	ret = lookup_one(ap, source, key, strlen(key), ctxt);
+ 	if (ret < 0)
+ 		return ret;
+-	if (ret == CHE_OK || ret == CHE_UPDATED)
++	if (ret == CHE_OK || ret == CHE_UPDATED || !is_amd_format)
+ 		return ret;
+ 
+-	if (!(source->flags & MAP_FLAG_FORMAT_AMD))
+-		return CHE_FAIL;
+-
+ 	lkp_key = strdup(key);
+ 	if (!lkp_key) {
+ 		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
diff --git a/autofs.spec b/autofs.spec
index 521e3cb..90cda9b 100644
--- a/autofs.spec
+++ b/autofs.spec
@@ -8,11 +8,15 @@
 Summary: A tool for automatically mounting and unmounting filesystems
 Name: autofs
 Version: 5.1.0
-Release: 0.beta1%{?dist}
+Release: 0.beta1.1%{?dist}
 Epoch: 1
 License: GPLv2+
 Group: System Environment/Daemons
 Source: ftp://ftp.kernel.org/pub/linux/daemons/autofs/v5/autofs-%{version}-beta1.tar.gz
+Patch1: autofs-5.0.8-amd-lookup-update-lookup-ldap-to-handle-amd-keys.patch
+Patch2: autofs-5.0.8-amd-lookup-update-lookup-hesiod-to-handle-amd-keys.patch
+Patch3: autofs-5.1.0-beta1-fix-wildcard-key-lookup.patch
+Patch4: autofs-5.0.9-check-for-non-existent-negative-entries-in-lookup_ghost.patch
 Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 %if %{with_systemd}
 BuildRequires: systemd-units
@@ -70,6 +74,10 @@ echo %{version}-%{release} > .version
   %define unitdir %{?_unitdir:/usr/lib/systemd/system}
   %define systemd_configure_arg --with-systemd
 %endif
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
 
 %build
 LDFLAGS=-Wl,-z,now
@@ -162,6 +170,14 @@ fi
 %dir /etc/auto.master.d
 
 %changelog
+* Sun Apr 13 2014 Ian Kent <ikent at redhat.com> - 1:5.1.0-0.beta1.1
+- amd lookup update lookup ldap to handle amd keys
+  - inadvertantly drop from initial series.
+- amd lookup update lookup hesiod to handle amd keys
+  - inadvertantly drop from initial series.
+- fix wildcard key lookup.
+- check for non existent negative entries in lookup_ghost().
+
 * Wed Apr 2 2014 Ian Kent <ikent at redhat.com> - 1:5.1.0-0.beta1
 - Update to autofs-5.0.1-beta1.
 


More information about the scm-commits mailing list