Blame SOURCES/autofs-5.0.8-amd-lookup-update-lookup-ldap-to-handle-amd-keys.patch

4d476f
autofs-5.0.8 - amd lookup update lookup ldap to handle amd keys
4d476f
4d476f
From: Ian Kent <raven@themaw.net>
4d476f
4d476f
4d476f
---
4d476f
 include/lookup_ldap.h |    3 
4d476f
 modules/lookup_ldap.c |  707 +++++++++++++++++++++++++++++++++++++++++++++-----
4d476f
 2 files changed, 651 insertions(+), 59 deletions(-)
4d476f
4d476f
--- autofs-5.0.7.orig/include/lookup_ldap.h
4d476f
+++ autofs-5.0.7/include/lookup_ldap.h
4d476f
@@ -36,6 +36,7 @@ struct ldap_searchdn {
4d476f
 
4d476f
 struct lookup_context {
4d476f
 	char *mapname;
4d476f
+	unsigned int format;
4d476f
 
4d476f
 	char *server;
4d476f
 	int port;
4d476f
@@ -43,6 +44,8 @@ struct lookup_context {
4d476f
 	char *qdn;
4d476f
 	unsigned int timeout;
4d476f
 	unsigned int network_timeout;
4d476f
+	unsigned long timestamp;
4d476f
+	unsigned int check_defaults;
4d476f
 
4d476f
 	/* LDAP version 2 or 3 */
4d476f
 	int version;
4d476f
--- autofs-5.0.7.orig/modules/lookup_ldap.c
4d476f
+++ autofs-5.0.7/modules/lookup_ldap.c
4d476f
@@ -29,6 +29,7 @@
4d476f
 #include <resolv.h>
4d476f
 #include <lber.h>
4d476f
 #include <libxml/tree.h>
4d476f
+#include <stdlib.h>
4d476f
 
4d476f
 #define MODULE_LOOKUP
4d476f
 #include "automount.h"
4d476f
@@ -52,6 +53,14 @@ static struct ldap_schema common_schema[
4d476f
 };
4d476f
 static unsigned int common_schema_count = sizeof(common_schema)/sizeof(struct ldap_schema);
4d476f
 
4d476f
+static struct ldap_schema amd_timestamp = {
4d476f
+	"madmap", "amdmapName", "amdmapTimestamp", NULL, "amdmapTimestamp"
4d476f
+};
4d476f
+
4d476f
+static struct ldap_schema amd_schema = {
4d476f
+	"amdmap", "amdmapName", "amdmap", "amdmapKey", "amdmapValue"
4d476f
+};
4d476f
+
4d476f
 /*
4d476f
  * Initialization and de-initialization of LDAP and OpenSSL must be
4d476f
  * always serialized to avoid corruption of context structures inside
4d476f
@@ -62,6 +71,7 @@ pthread_mutex_t ldapinit_mutex = PTHREAD
4d476f
 struct ldap_search_params {
4d476f
 	struct autofs_point *ap;
4d476f
 	LDAP *ldap;
4d476f
+	char *base;
4d476f
 	char *query, **attrs;
4d476f
 	struct berval *cookie;
4d476f
 	ber_int_t pageSize;
4d476f
@@ -531,6 +541,16 @@ static int find_query_dn(unsigned logopt
4d476f
 	if (ctxt->schema)
4d476f
 		return 0;
4d476f
 
4d476f
+	if (ctxt->format & MAP_FLAG_FORMAT_AMD) {
4d476f
+		schema = alloc_common_schema(&amd_schema);
4d476f
+		if (!schema) {
4d476f
+			error(logopt, MODPREFIX "failed to allocate schema");
4d476f
+			return 0;
4d476f
+		}
4d476f
+		ctxt->schema = schema;
4d476f
+		return 1;
4d476f
+	}
4d476f
+
4d476f
 	for (i = 0; i < common_schema_count; i++) {
4d476f
 		const char *class = common_schema[i].map_class;
4d476f
 		const char *key = common_schema[i].map_attr;
4d476f
@@ -587,8 +607,10 @@ static int do_bind(unsigned logopt, LDAP
4d476f
 
4d476f
 	if (!ctxt->cur_host) {
4d476f
 		ctxt->cur_host = nhost;
4d476f
-		/* Check if schema defined in conf first time only */
4d476f
-		ctxt->schema = defaults_get_schema();
4d476f
+		if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
4d476f
+			/* Check if schema defined in conf first time only */
4d476f
+			ctxt->schema = defaults_get_schema();
4d476f
+		}
4d476f
 	} else {
4d476f
 		/* If connection host has changed update */
4d476f
 		if (strcmp(ctxt->cur_host, nhost)) {
4d476f
@@ -614,7 +636,7 @@ static int do_bind(unsigned logopt, LDAP
4d476f
 			      MODPREFIX "failed to find valid query dn");
4d476f
 			return 0;
4d476f
 		}
4d476f
-	} else {
4d476f
+	} else if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
4d476f
 		const char *class = ctxt->schema->map_class;
4d476f
 		const char *key = ctxt->schema->map_attr;
4d476f
 		if (!get_query_dn(logopt, ldap, ctxt, class, key)) {
4d476f
@@ -648,6 +670,126 @@ static LDAP *do_connect(unsigned logopt,
4d476f
 	return ldap;
4d476f
 }
4d476f
 
4d476f
+static unsigned long get_amd_timestamp(struct lookup_context *ctxt)
4d476f
+{
4d476f
+	LDAP *ldap;
4d476f
+	LDAPMessage *result = NULL, *e;
4d476f
+	char *query;
4d476f
+	int scope = LDAP_SCOPE_SUBTREE;
4d476f
+	char *map, *class, *value;
4d476f
+	char *attrs[2];
4d476f
+	struct berval **bvValues;
4d476f
+	unsigned long timestamp = 0;
4d476f
+	int rv, l, ql;
4d476f
+
4d476f
+	ldap = do_connect(LOGOPT_ANY, ctxt->server, ctxt);
4d476f
+	if (!ldap)
4d476f
+		return 0;
4d476f
+
4d476f
+	map = amd_timestamp.map_attr;
4d476f
+	class = amd_timestamp.entry_class;
4d476f
+	value = amd_timestamp.value_attr;
4d476f
+
4d476f
+	attrs[0] = value;
4d476f
+	attrs[1] = NULL;
4d476f
+
4d476f
+	/* Build a query string. */
4d476f
+	l = strlen(class) +
4d476f
+	    strlen(map) + strlen(ctxt->mapname) + 21;
4d476f
+
4d476f
+	query = malloc(l);
4d476f
+	if (query == NULL) {
4d476f
+		char buf[MAX_ERR_BUF];
4d476f
+		char *estr = strerror_r(errno, buf, sizeof(buf));
4d476f
+		crit(LOGOPT_ANY, MODPREFIX "malloc: %s", estr);
4d476f
+		return 0;
4d476f
+	}
4d476f
+
4d476f
+	/*
4d476f
+	 * Look for an entry in class under ctxt-base
4d476f
+	 * whose entry is equal to qKey.
4d476f
+	 */
4d476f
+	ql = sprintf(query, "(&(objectclass=%s)(%s=%s))",
4d476f
+		     class, map, ctxt->mapname);
4d476f
+	if (ql >= l) {
4d476f
+		error(LOGOPT_ANY,
4d476f
+		      MODPREFIX "error forming query string");
4d476f
+		free(query);
4d476f
+		return 0;
4d476f
+	}
4d476f
+
4d476f
+	rv = ldap_search_s(ldap, ctxt->base, scope, query, attrs, 0, &result);
4d476f
+	if ((rv != LDAP_SUCCESS) || !result) {
4d476f
+		crit(LOGOPT_ANY, MODPREFIX "timestamp query failed %s", query);
4d476f
+		unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
4d476f
+		if (result)
4d476f
+			ldap_msgfree(result);
4d476f
+		free(query);
4d476f
+		return 0;
4d476f
+	}
4d476f
+
4d476f
+	e = ldap_first_entry(ldap, result);
4d476f
+	if (!e) {
4d476f
+		debug(LOGOPT_ANY,
4d476f
+		     MODPREFIX "got answer, but no entry for timestamp");
4d476f
+		ldap_msgfree(result);
4d476f
+		unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
4d476f
+		free(query);
4d476f
+		return CHE_MISSING;
4d476f
+	}
4d476f
+
4d476f
+	while (e) {
4d476f
+		char *v_val;
4d476f
+		char *endptr;
4d476f
+
4d476f
+		bvValues = ldap_get_values_len(ldap, e, value);
4d476f
+		if (!bvValues || !*bvValues) {
4d476f
+			debug(LOGOPT_ANY,
4d476f
+			      MODPREFIX "no value found in timestamp");
4d476f
+			goto next;
4d476f
+		}
4d476f
+
4d476f
+		/* There should be one value for a timestamp */
4d476f
+		v_val = bvValues[0]->bv_val;
4d476f
+
4d476f
+		timestamp = strtol(v_val, &endptr, 0);
4d476f
+		if ((errno == ERANGE &&
4d476f
+		    (timestamp == LONG_MAX || timestamp == LONG_MIN)) ||
4d476f
+		    (errno != 0 && timestamp == 0)) {
4d476f
+			debug(LOGOPT_ANY,
4d476f
+			      MODPREFIX "invalid value in timestamp");
4d476f
+			free(query);
4d476f
+			return 0;
4d476f
+		}
4d476f
+
4d476f
+		if (endptr == v_val) {
4d476f
+			debug(LOGOPT_ANY,
4d476f
+			      MODPREFIX "no digits found in timestamp");
4d476f
+			free(query);
4d476f
+			return 0;
4d476f
+		}
4d476f
+
4d476f
+		if (*endptr != '\0') {
4d476f
+			warn(LOGOPT_ANY, MODPREFIX
4d476f
+			     "characters found after number: %s", endptr);
4d476f
+			warn(LOGOPT_ANY,
4d476f
+			     MODPREFIX "timestamp may be invalid");
4d476f
+		}
4d476f
+
4d476f
+		ldap_value_free_len(bvValues);
4d476f
+		break;
4d476f
+next:
4d476f
+		ldap_value_free_len(bvValues);
4d476f
+		e = ldap_next_entry(ldap, e);
4d476f
+	}
4d476f
+
4d476f
+	ldap_msgfree(result);
4d476f
+	unbind_ldap_connection(LOGOPT_ANY, ldap, ctxt);
4d476f
+	free(query);
4d476f
+
4d476f
+	return timestamp;
4d476f
+}
4d476f
+
4d476f
 static LDAP *connect_to_server(unsigned logopt, const char *uri, struct lookup_context *ctxt)
4d476f
 {
4d476f
 	LDAP *ldap;
4d476f
@@ -1215,7 +1357,7 @@ static int parse_server_string(unsigned
4d476f
 		const char *q = NULL;
4d476f
 
4d476f
 		/* Isolate the server(s). */
4d476f
-		if ((q = strchr(s, '/'))) {
4d476f
+		if ((q = strchr(s, '/')) || (q = strchr(s, '\0'))) {
4d476f
 			l = q - s;
4d476f
 			if (*proto) {
4d476f
 				al_len = l + strlen(proto) + 2;
4d476f
@@ -1318,8 +1460,7 @@ static int parse_server_string(unsigned
4d476f
 		ptr += l + 1;
4d476f
 	}
4d476f
 
4d476f
-	/* TODO: why did I do this - how can the map name "and" base dn be missing? */
4d476f
-	if (!ptr)
4d476f
+	if (!ptr || ctxt->format & MAP_FLAG_FORMAT_AMD)
4d476f
 		goto done;
4d476f
 
4d476f
 	/*
4d476f
@@ -1505,36 +1646,83 @@ int lookup_init(const char *mapfmt, int
4d476f
 	/* If a map type isn't explicitly given, parse it like sun entries. */
4d476f
 	if (mapfmt == NULL)
4d476f
 		mapfmt = MAPFMT_DEFAULT;
4d476f
-
4d476f
-	/*
4d476f
-	 * Parse out the server name and base dn, and fill them
4d476f
-	 * into the proper places in the lookup context structure.
4d476f
-	 */
4d476f
-	if (!parse_server_string(LOGOPT_NONE, argv[0], ctxt)) {
4d476f
-		error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
4d476f
-		free_context(ctxt);
4d476f
-		return 1;
4d476f
+	if (!strcmp(mapfmt, "amd")) {
4d476f
+		ctxt->format = MAP_FLAG_FORMAT_AMD;
4d476f
+		ctxt->check_defaults = 1;
4d476f
 	}
4d476f
 
4d476f
-	if (!ctxt->base)
4d476f
-		ctxt->sdns = defaults_get_searchdns();
4d476f
-
4d476f
 	ctxt->timeout = defaults_get_ldap_timeout();
4d476f
 	ctxt->network_timeout = defaults_get_ldap_network_timeout();
4d476f
 
4d476f
-	if (!ctxt->server) {
4d476f
-		struct list_head *uris = defaults_get_uris();
4d476f
-		if (uris) {
4d476f
-			validate_uris(uris);
4d476f
-			if (!list_empty(uris))
4d476f
-				ctxt->uris = uris;
4d476f
-			else {
4d476f
-				error(LOGOPT_ANY,
4d476f
-				      "no valid uris found in config list"
4d476f
-				      ", using default system config");
4d476f
-				free(uris);
4d476f
+	if (!(ctxt->format & MAP_FLAG_FORMAT_AMD)) {
4d476f
+		/*
4d476f
+		 * Parse out the server name and base dn, and fill them
4d476f
+		 * into the proper places in the lookup context structure.
4d476f
+		 */
4d476f
+		if (!parse_server_string(LOGOPT_NONE, argv[0], ctxt)) {
4d476f
+			error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+
4d476f
+		if (!ctxt->base)
4d476f
+			ctxt->sdns = defaults_get_searchdns();
4d476f
+
4d476f
+		if (!ctxt->server) {
4d476f
+			struct list_head *uris = defaults_get_uris();
4d476f
+			if (uris) {
4d476f
+				validate_uris(uris);
4d476f
+				if (!list_empty(uris))
4d476f
+					ctxt->uris = uris;
4d476f
+				else {
4d476f
+					error(LOGOPT_ANY, MODPREFIX
4d476f
+					    "no valid uris found in config list"
4d476f
+					    ", using default system config");
4d476f
+					free(uris);
4d476f
+				}
4d476f
 			}
4d476f
 		}
4d476f
+	} else {
4d476f
+		char *tmp = conf_amd_get_ldap_base();
4d476f
+		if (!tmp) {
4d476f
+			error(LOGOPT_ANY, MODPREFIX "failed to get base dn");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+		ctxt->base = tmp;
4d476f
+
4d476f
+		tmp = conf_amd_get_ldap_hostports();
4d476f
+		if (!tmp) {
4d476f
+			error(LOGOPT_ANY,
4d476f
+			      MODPREFIX "failed to get ldap_hostports");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+
4d476f
+		/*
4d476f
+		 * Parse out the server name and port, and save them in
4d476f
+		 * the proper places in the lookup context structure.
4d476f
+		 */
4d476f
+		if (!parse_server_string(LOGOPT_NONE, tmp, ctxt)) {
4d476f
+			error(LOGOPT_ANY, MODPREFIX "cannot parse server string");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+		free(tmp);
4d476f
+
4d476f
+		if (!ctxt->server) {
4d476f
+			error(LOGOPT_ANY, MODPREFIX "ldap_hostports not valid");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+
4d476f
+		tmp = strdup(argv[0]);
4d476f
+		if (!tmp) {
4d476f
+			error(LOGOPT_ANY, MODPREFIX "failed to set mapname");
4d476f
+			free_context(ctxt);
4d476f
+			return 1;
4d476f
+		}
4d476f
+		ctxt->mapname = tmp;
4d476f
 	}
4d476f
 
4d476f
 	/*
4d476f
@@ -1558,6 +1746,8 @@ int lookup_init(const char *mapfmt, int
4d476f
 	}
4d476f
 #endif
4d476f
 
4d476f
+	ctxt->timestamp = get_amd_timestamp(ctxt);
4d476f
+
4d476f
 	/* Open the parser, if we can. */
4d476f
 	ctxt->parse = open_parse(mapfmt, MODPREFIX, argc - 1, argv + 1);
4d476f
 	if (!ctxt->parse) {
4d476f
@@ -2029,7 +2219,7 @@ static int do_paged_query(struct ldap_se
4d476f
 	if (sp->morePages == TRUE)
4d476f
 		goto do_paged;
4d476f
 
4d476f
-	rv = ldap_search_s(sp->ldap, ctxt->qdn, scope, sp->query, sp->attrs, 0, &sp->result);
4d476f
+	rv = ldap_search_s(sp->ldap, sp->base, scope, sp->query, sp->attrs, 0, &sp->result);
4d476f
 	if ((rv != LDAP_SUCCESS) || !sp->result) {
4d476f
 		/*
4d476f
  		 * Check for Size Limit exceeded and force run through loop
4d476f
@@ -2063,7 +2253,7 @@ do_paged:
4d476f
 
4d476f
 	/* Search for entries in the directory using the parmeters. */
4d476f
 	rv = ldap_search_ext_s(sp->ldap,
4d476f
-			       ctxt->qdn, scope, sp->query, sp->attrs,
4d476f
+			       sp->base, scope, sp->query, sp->attrs,
4d476f
 			       0, controls, NULL, NULL, 0, &sp->result);
4d476f
 	if ((rv != LDAP_SUCCESS) && (rv != LDAP_PARTIAL_RESULTS)) {
4d476f
 		ldap_control_free(pageControl);
4d476f
@@ -2364,6 +2554,115 @@ next:
4d476f
 	return LDAP_SUCCESS;
4d476f
 }
4d476f
 
4d476f
+static int do_get_amd_entries(struct ldap_search_params *sp,
4d476f
+			      struct map_source *source,
4d476f
+			      struct lookup_context *ctxt)
4d476f
+{
4d476f
+	struct autofs_point *ap = sp->ap;
4d476f
+	struct mapent_cache *mc = source->mc;
4d476f
+	struct berval **bvKey;
4d476f
+	struct berval **bvValues;
4d476f
+	LDAPMessage *e;
4d476f
+	char *entry, *value;
4d476f
+	int rv, ret, count;
4d476f
+
4d476f
+	entry = ctxt->schema->entry_attr;
4d476f
+	value = ctxt->schema->value_attr;
4d476f
+
4d476f
+	e = ldap_first_entry(sp->ldap, sp->result);
4d476f
+	if (!e) {
4d476f
+		debug(ap->logopt,
4d476f
+		      MODPREFIX "query succeeded, no matches for %s",
4d476f
+		      sp->query);
4d476f
+		ret = ldap_parse_result(sp->ldap, sp->result,
4d476f
+					&rv, NULL, NULL, NULL, NULL, 0);
4d476f
+		if (ret == LDAP_SUCCESS)
4d476f
+			return rv;
4d476f
+		else
4d476f
+			return LDAP_OPERATIONS_ERROR;
4d476f
+	} else
4d476f
+		debug(ap->logopt, MODPREFIX "examining entries");
4d476f
+
4d476f
+	while (e) {
4d476f
+		char *k_val, *v_val;
4d476f
+		ber_len_t k_len;
4d476f
+		char *s_key;
4d476f
+
4d476f
+		bvKey = ldap_get_values_len(sp->ldap, e, entry);
4d476f
+		if (!bvKey || !*bvKey) {
4d476f
+			e = ldap_next_entry(sp->ldap, e);
4d476f
+			if (!e) {
4d476f
+				debug(ap->logopt, MODPREFIX
4d476f
+				      "failed to get next entry for query %s",
4d476f
+				      sp->query);
4d476f
+				ret = ldap_parse_result(sp->ldap,
4d476f
+							sp->result, &rv,
4d476f
+							NULL, NULL, NULL, NULL, 0);
4d476f
+				if (ret == LDAP_SUCCESS)
4d476f
+					return rv;
4d476f
+				else
4d476f
+					return LDAP_OPERATIONS_ERROR;
4d476f
+			}
4d476f
+			continue;
4d476f
+		}
4d476f
+
4d476f
+		/* By definition keys should be unique within each map entry */
4d476f
+		k_val = NULL;
4d476f
+		k_len = 0;
4d476f
+
4d476f
+		count = ldap_count_values_len(bvKey);
4d476f
+		if (count > 1)
4d476f
+			warn(ap->logopt, MODPREFIX
4d476f
+			     "more than one %s, using first", entry);
4d476f
+
4d476f
+		k_val = bvKey[0]->bv_val;
4d476f
+		k_len = bvKey[0]->bv_len;
4d476f
+
4d476f
+		bvValues = ldap_get_values_len(sp->ldap, e, value);
4d476f
+		if (!bvValues || !*bvValues) {
4d476f
+			debug(ap->logopt,
4d476f
+			      MODPREFIX "no %s defined for %s",
4d476f
+			      value, sp->query);
4d476f
+			goto next;
4d476f
+		}
4d476f
+
4d476f
+		count = ldap_count_values_len(bvValues);
4d476f
+		if (count > 1)
4d476f
+			warn(ap->logopt, MODPREFIX
4d476f
+			     "more than one %s, using first", value);
4d476f
+
4d476f
+		v_val = bvValues[0]->bv_val;
4d476f
+
4d476f
+		/* Don't fail on "/" in key => type == 0 */
4d476f
+		s_key = sanitize_path(k_val, k_len, 0, ap->logopt);
4d476f
+		if (!s_key)
4d476f
+			goto next;
4d476f
+
4d476f
+		cache_writelock(mc);
4d476f
+		cache_update(mc, source, s_key, v_val, sp->age);
4d476f
+		cache_unlock(mc);
4d476f
+
4d476f
+		free(s_key);
4d476f
+next:
4d476f
+		ldap_value_free_len(bvValues);
4d476f
+		ldap_value_free_len(bvKey);
4d476f
+		e = ldap_next_entry(sp->ldap, e);
4d476f
+		if (!e) {
4d476f
+			debug(ap->logopt, MODPREFIX
4d476f
+			      "failed to get next entry for query %s",
4d476f
+			      sp->query);
4d476f
+			ret = ldap_parse_result(sp->ldap,
4d476f
+						sp->result, &rv,
4d476f
+						NULL, NULL, NULL, NULL, 0);
4d476f
+			if (ret == LDAP_SUCCESS)
4d476f
+				return rv;
4d476f
+			else
4d476f
+				return LDAP_OPERATIONS_ERROR;
4d476f
+		}
4d476f
+	}
4d476f
+
4d476f
+	return LDAP_SUCCESS;
4d476f
+}
4d476f
 
4d476f
 static int read_one_map(struct autofs_point *ap,
4d476f
 			struct map_source *source,
4d476f
@@ -2419,9 +2718,14 @@ static int read_one_map(struct autofs_po
4d476f
 		return NSS_STATUS_UNAVAIL;
4d476f
 	}
4d476f
 
4d476f
+	if (ctxt->format & MAP_FLAG_FORMAT_AMD)
4d476f
+		sp.base = ctxt->base;
4d476f
+	else
4d476f
+		sp.base = ctxt->qdn;
4d476f
+
4d476f
 	/* Look around. */
4d476f
 	debug(ap->logopt,
4d476f
-	      MODPREFIX "searching for \"%s\" under \"%s\"", sp.query, ctxt->qdn);
4d476f
+	      MODPREFIX "searching for \"%s\" under \"%s\"", sp.query, sp.base);
4d476f
 
4d476f
 	sp.cookie = NULL;
4d476f
 	sp.pageSize = 2000;
4d476f
@@ -2465,7 +2769,10 @@ static int read_one_map(struct autofs_po
4d476f
 			return NSS_STATUS_UNAVAIL;
4d476f
 		}
4d476f
 
4d476f
-		rv = do_get_entries(&sp, source, ctxt);
4d476f
+		if (source->flags & MAP_FLAG_FORMAT_AMD)
4d476f
+			rv = do_get_amd_entries(&sp, source, ctxt);
4d476f
+		else
4d476f
+			rv = do_get_entries(&sp, source, ctxt);
4d476f
 		if (rv != LDAP_SUCCESS) {
4d476f
 			ldap_msgfree(sp.result);
4d476f
 			unbind_ldap_connection(ap->logopt, sp.ldap, ctxt);
4d476f
@@ -2874,6 +3181,219 @@ next:
4d476f
 	return ret;
4d476f
 }
4d476f
 
4d476f
+static int lookup_one_amd(struct autofs_point *ap,
4d476f
+			  struct map_source *source,
4d476f
+			  char *qKey, int qKey_len,
4d476f
+			  struct lookup_context *ctxt)
4d476f
+{
4d476f
+	struct mapent_cache *mc = source->mc;
4d476f
+	LDAP *ldap;
4d476f
+	LDAPMessage *result = NULL, *e;
4d476f
+	char *query;
4d476f
+	int scope = LDAP_SCOPE_SUBTREE;
4d476f
+	char *map, *class, *entry, *value;
4d476f
+	char *attrs[3];
4d476f
+	struct berval **bvKey;
4d476f
+	struct berval **bvValues;
4d476f
+	char buf[MAX_ERR_BUF];
4d476f
+	time_t age = time(NULL);
4d476f
+	int rv, l, ql, count;
4d476f
+	int ret = CHE_MISSING;
4d476f
+
4d476f
+	if (ctxt == NULL) {
4d476f
+		crit(ap->logopt, MODPREFIX "context was NULL");
4d476f
+		return CHE_FAIL;
4d476f
+	}
4d476f
+
4d476f
+	/* Initialize the LDAP context. */
4d476f
+	ldap = do_reconnect(ap->logopt, ctxt);
4d476f
+	if (!ldap)
4d476f
+		return CHE_UNAVAIL;
4d476f
+
4d476f
+	map = ctxt->schema->map_attr;
4d476f
+	class = ctxt->schema->entry_class;
4d476f
+	entry = ctxt->schema->entry_attr;
4d476f
+	value = ctxt->schema->value_attr;
4d476f
+
4d476f
+	attrs[0] = entry;
4d476f
+	attrs[1] = value;
4d476f
+	attrs[2] = NULL;
4d476f
+
4d476f
+	/* Build a query string. */
4d476f
+	l = strlen(class) +
4d476f
+	    strlen(map) + strlen(ctxt->mapname) +
4d476f
+	    strlen(entry) + strlen(qKey) + 24;
4d476f
+
4d476f
+	query = malloc(l);
4d476f
+	if (query == NULL) {
4d476f
+		char *estr = strerror_r(errno, buf, sizeof(buf));
4d476f
+		crit(ap->logopt, MODPREFIX "malloc: %s", estr);
4d476f
+		return CHE_FAIL;
4d476f
+	}
4d476f
+
4d476f
+	/*
4d476f
+	 * Look for an entry in class under ctxt-base
4d476f
+	 * whose entry is equal to qKey.
4d476f
+	 */
4d476f
+	ql = sprintf(query, "(&(objectclass=%s)(%s=%s)(%s=%s))",
4d476f
+		     class, map, ctxt->mapname, entry, qKey);
4d476f
+	if (ql >= l) {
4d476f
+		error(ap->logopt,
4d476f
+		      MODPREFIX "error forming query string");
4d476f
+		free(query);
4d476f
+		return CHE_FAIL;
4d476f
+	}
4d476f
+
4d476f
+	debug(ap->logopt,
4d476f
+	      MODPREFIX "searching for \"%s\" under \"%s\"", query, ctxt->base);
4d476f
+
4d476f
+	rv = ldap_search_s(ldap, ctxt->base, scope, query, attrs, 0, &result);
4d476f
+	if ((rv != LDAP_SUCCESS) || !result) {
4d476f
+		crit(ap->logopt, MODPREFIX "query failed for %s", query);
4d476f
+		unbind_ldap_connection(ap->logopt, ldap, ctxt);
4d476f
+		if (result)
4d476f
+			ldap_msgfree(result);
4d476f
+		free(query);
4d476f
+		return CHE_FAIL;
4d476f
+	}
4d476f
+
4d476f
+	debug(ap->logopt,
4d476f
+	      MODPREFIX "getting first entry for %s=\"%s\"", entry, qKey);
4d476f
+
4d476f
+	e = ldap_first_entry(ldap, result);
4d476f
+	if (!e) {
4d476f
+		debug(ap->logopt,
4d476f
+		     MODPREFIX "got answer, but no entry for %s", query);
4d476f
+		ldap_msgfree(result);
4d476f
+		unbind_ldap_connection(ap->logopt, ldap, ctxt);
4d476f
+		free(query);
4d476f
+		return CHE_MISSING;
4d476f
+	}
4d476f
+
4d476f
+	while (e) {
4d476f
+		char *k_val, *v_val;
4d476f
+		ber_len_t k_len;
4d476f
+		char *s_key;
4d476f
+
4d476f
+		bvKey = ldap_get_values_len(ldap, e, entry);
4d476f
+		if (!bvKey || !*bvKey) {
4d476f
+			e = ldap_next_entry(ldap, e);
4d476f
+			continue;
4d476f
+		}
4d476f
+
4d476f
+		/* By definition keys should be unique within each map entry */
4d476f
+		k_val = NULL;
4d476f
+		k_len = 0;
4d476f
+
4d476f
+		count = ldap_count_values_len(bvKey);
4d476f
+		if (count > 1)
4d476f
+			warn(ap->logopt, MODPREFIX
4d476f
+			     "more than one %s, using first", entry);
4d476f
+
4d476f
+		k_val = bvKey[0]->bv_val;
4d476f
+		k_len = bvKey[0]->bv_len;
4d476f
+
4d476f
+		debug(ap->logopt, MODPREFIX "examining first entry");
4d476f
+
4d476f
+		bvValues = ldap_get_values_len(ldap, e, value);
4d476f
+		if (!bvValues || !*bvValues) {
4d476f
+			debug(ap->logopt,
4d476f
+			      MODPREFIX "no %s defined for %s", value, query);
4d476f
+			goto next;
4d476f
+		}
4d476f
+
4d476f
+		count = ldap_count_values_len(bvValues);
4d476f
+		if (count > 1)
4d476f
+			warn(ap->logopt, MODPREFIX
4d476f
+			     "more than one %s, using first", value);
4d476f
+
4d476f
+		/* There should be one value for a key, use first value */
4d476f
+		v_val = bvValues[0]->bv_val;
4d476f
+
4d476f
+		/* Don't fail on "/" in key => type == 0 */
4d476f
+		s_key = sanitize_path(k_val, k_len, 0, ap->logopt);
4d476f
+		if (!s_key)
4d476f
+			goto next;
4d476f
+
4d476f
+		cache_writelock(mc);
4d476f
+		ret = cache_update(mc, source, s_key, v_val, age);
4d476f
+		cache_unlock(mc);
4d476f
+
4d476f
+		free(s_key);
4d476f
+next:
4d476f
+		ldap_value_free_len(bvValues);
4d476f
+		ldap_value_free_len(bvKey);
4d476f
+		e = ldap_next_entry(ldap, e);
4d476f
+	}
4d476f
+
4d476f
+	ldap_msgfree(result);
4d476f
+	unbind_ldap_connection(ap->logopt, ldap, ctxt);
4d476f
+	free(query);
4d476f
+
4d476f
+	return ret;
4d476f
+}
4d476f
+
4d476f
+static int match_key(struct autofs_point *ap,
4d476f
+		     struct map_source *source,
4d476f
+		     char *key, int key_len,
4d476f
+		     struct lookup_context *ctxt)
4d476f
+{
4d476f
+	unsigned int is_amd_format = source->flags & MAP_FLAG_FORMAT_AMD;
4d476f
+	char buf[MAX_ERR_BUF];
4d476f
+	char *lkp_key;
4d476f
+	char *prefix;
4d476f
+	int ret;
4d476f
+
4d476f
+	if (is_amd_format)
4d476f
+		ret = lookup_one_amd(ap, source, key, key_len, ctxt);
4d476f
+	else
4d476f
+		ret = lookup_one(ap, source, key, key_len, ctxt);
4d476f
+
4d476f
+	if (ret == CHE_OK || ret == CHE_UPDATED)
4d476f
+		return ret;
4d476f
+
4d476f
+	if (!is_amd_format)
4d476f
+		return CHE_FAIL;
4d476f
+
4d476f
+	lkp_key = strdup(key);
4d476f
+	if (!lkp_key) {
4d476f
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
4d476f
+		error(ap->logopt, MODPREFIX "strdup: %s", estr);
4d476f
+		return CHE_FAIL;
4d476f
+	}
4d476f
+
4d476f
+	ret = CHE_MISSING;
4d476f
+
4d476f
+	/*
4d476f
+	 * Now strip successive directory components and try a
4d476f
+	 * match against map entries ending with a wildcard and
4d476f
+	 * finally try the wilcard entry itself.
4d476f
+	 */
4d476f
+	while ((prefix = strrchr(lkp_key, '/'))) {
4d476f
+		char *match;
4d476f
+		size_t len;
4d476f
+		*prefix = '\0';
4d476f
+		len = strlen(lkp_key + 3);
4d476f
+		match = malloc(len);
4d476f
+		if (!match) {
4d476f
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
4d476f
+			error(ap->logopt, MODPREFIX "malloc: %s", estr);
4d476f
+			ret = CHE_FAIL;
4d476f
+			goto done;
4d476f
+		}
4d476f
+		len--;
4d476f
+		strcpy(match, lkp_key);
4d476f
+		strcat(match, "/*");
4d476f
+		ret = lookup_one_amd(ap, source, match, len, ctxt);
4d476f
+		free(match);
4d476f
+		if (ret == CHE_OK || ret == CHE_UPDATED)
4d476f
+			goto done;
4d476f
+	}
4d476f
+done:
4d476f
+	free(lkp_key);
4d476f
+	return ret;
4d476f
+}
4d476f
+
4d476f
 static int check_map_indirect(struct autofs_point *ap,
4d476f
 			      struct map_source *source,
4d476f
 			      char *key, int key_len,
4d476f
@@ -2888,16 +3408,43 @@ static int check_map_indirect(struct aut
4d476f
 	mc = source->mc;
4d476f
 
4d476f
 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
4d476f
-	ret = lookup_one(ap, source, key, key_len, ctxt);
4d476f
+
4d476f
+	pthread_mutex_lock(&ap->entry->current_mutex);
4d476f
+	if (source->flags & MAP_FLAG_FORMAT_AMD) {
4d476f
+		unsigned long timestamp = get_amd_timestamp(ctxt);
4d476f
+		if (timestamp > ctxt->timestamp) {
4d476f
+			ctxt->timestamp = timestamp;
4d476f
+			source->stale = 1;
4d476f
+			ctxt->check_defaults = 1;
4d476f
+		}
4d476f
+
4d476f
+		if (ctxt->check_defaults) {
4d476f
+			/* Check for a /defaults entry */
4d476f
+			ret = lookup_one_amd(ap, source, "/defaults", 9, ctxt);
4d476f
+			if (ret == CHE_FAIL) {
4d476f
+				warn(ap->logopt, MODPREFIX
4d476f
+				     "error getting /defaults from map %s",
4d476f
+				     ctxt->mapname);
4d476f
+			} else
4d476f
+				ctxt->check_defaults = 0;
4d476f
+		}
4d476f
+	}
4d476f
+	pthread_mutex_unlock(&ap->entry->current_mutex);
4d476f
+
4d476f
+	ret = match_key(ap, source, key, key_len, ctxt);
4d476f
 	if (ret == CHE_FAIL) {
4d476f
 		pthread_setcancelstate(cur_state, NULL);
4d476f
 		return NSS_STATUS_NOTFOUND;
4d476f
 	} else if (ret == CHE_UNAVAIL) {
4d476f
+		struct mapent *exists;
4d476f
 		/*
4d476f
 		 * If the server is down and the entry exists in the cache
4d476f
 		 * and belongs to this map return success and use the entry.
4d476f
 		 */
4d476f
-		struct mapent *exists = cache_lookup(mc, key);
4d476f
+		if (source->flags & MAP_FLAG_FORMAT_AMD)
4d476f
+			exists = match_cached_key(ap, MODPREFIX, source, key);
4d476f
+		else
4d476f
+			exists = cache_lookup(mc, key);
4d476f
 		if (exists && exists->source == source) {
4d476f
 			pthread_setcancelstate(cur_state, NULL);
4d476f
 			return NSS_STATUS_SUCCESS;
4d476f
@@ -2910,24 +3457,28 @@ static int check_map_indirect(struct aut
4d476f
 	}
4d476f
 	pthread_setcancelstate(cur_state, NULL);
4d476f
 
4d476f
-	/*
4d476f
-	 * Check for map change and update as needed for
4d476f
-	 * following cache lookup.
4d476f
-	 */
4d476f
-	cache_readlock(mc);
4d476f
-	t_last_read = ap->exp_runfreq + 1;
4d476f
-	me = cache_lookup_first(mc);
4d476f
-	while (me) {
4d476f
-		if (me->source == source) {
4d476f
-			t_last_read = now - me->age;
4d476f
-			break;
4d476f
+	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
4d476f
+		/*
4d476f
+		 * Check for map change and update as needed for
4d476f
+		 * following cache lookup.
4d476f
+		 */
4d476f
+		cache_readlock(mc);
4d476f
+		t_last_read = ap->exp_runfreq + 1;
4d476f
+		me = cache_lookup_first(mc);
4d476f
+		while (me) {
4d476f
+			if (me->source == source) {
4d476f
+				t_last_read = now - me->age;
4d476f
+				break;
4d476f
+			}
4d476f
+			me = cache_lookup_next(mc, me);
4d476f
 		}
4d476f
-		me = cache_lookup_next(mc, me);
4d476f
-	}
4d476f
-	cache_unlock(mc);
4d476f
+		cache_unlock(mc);
4d476f
 
4d476f
-	if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
4d476f
-		source->stale = 1;
4d476f
+		pthread_mutex_lock(&ap->entry->current_mutex);
4d476f
+		if (t_last_read > ap->exp_runfreq && ret & CHE_UPDATED)
4d476f
+			source->stale = 1;
4d476f
+		pthread_mutex_unlock(&ap->entry->current_mutex);
4d476f
+	}
4d476f
 
4d476f
 	cache_readlock(mc);
4d476f
 	me = cache_lookup_distinct(mc, "*");
4d476f
@@ -2948,8 +3499,10 @@ int lookup_mount(struct autofs_point *ap
4d476f
 	struct mapent *me;
4d476f
 	char key[KEY_MAX_LEN + 1];
4d476f
 	int key_len;
4d476f
+	char *lkp_key;
4d476f
 	char *mapent = NULL;
4d476f
 	char mapent_buf[MAPENT_MAX_LEN + 1];
4d476f
+	char buf[MAX_ERR_BUF];
4d476f
 	int status = 0;
4d476f
 	int ret = 1;
4d476f
 
4d476f
@@ -2961,9 +3514,18 @@ int lookup_mount(struct autofs_point *ap
4d476f
 
4d476f
 	debug(ap->logopt, MODPREFIX "looking up %s", name);
4d476f
 
4d476f
-	key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
4d476f
-	if (key_len > KEY_MAX_LEN)
4d476f
-		return NSS_STATUS_NOTFOUND;
4d476f
+	if (!(source->flags & MAP_FLAG_FORMAT_AMD)) {
4d476f
+		key_len = snprintf(key, KEY_MAX_LEN + 1, "%s", name);
4d476f
+		if (key_len > KEY_MAX_LEN)
4d476f
+			return NSS_STATUS_NOTFOUND;
4d476f
+	} else {
4d476f
+		key_len = expandamdent(name, NULL, NULL);
4d476f
+		if (key_len > KEY_MAX_LEN)
4d476f
+			return NSS_STATUS_NOTFOUND;
4d476f
+		expandamdent(name, key, NULL);
4d476f
+		key[key_len] = '\0';
4d476f
+		debug(ap->logopt, MODPREFIX "expanded key: \"%s\"", key);
4d476f
+	}
4d476f
 
4d476f
 	/* Check if we recorded a mount fail for this key anywhere */
4d476f
 	me = lookup_source_mapent(ap, key, LKP_DISTINCT);
4d476f
@@ -2997,18 +3559,26 @@ int lookup_mount(struct autofs_point *ap
4d476f
 	 * we never know about it.
4d476f
 	 */
4d476f
 	if (ap->type == LKP_INDIRECT && *key != '/') {
4d476f
-		char *lkp_key;
4d476f
-
4d476f
 		cache_readlock(mc);
4d476f
 		me = cache_lookup_distinct(mc, key);
4d476f
 		if (me && me->multi)
4d476f
 			lkp_key = strdup(me->multi->key);
4d476f
-		else
4d476f
+		else if (!ap->pref)
4d476f
 			lkp_key = strdup(key);
4d476f
+		else {
4d476f
+			lkp_key = malloc(strlen(ap->pref) + strlen(key) + 1);
4d476f
+			if (lkp_key) {
4d476f
+				strcpy(lkp_key, ap->pref);
4d476f
+				strcat(lkp_key, key);
4d476f
+			}
4d476f
+		}
4d476f
 		cache_unlock(mc);
4d476f
 
4d476f
-		if (!lkp_key)
4d476f
+		if (!lkp_key) {
4d476f
+			char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
4d476f
+			error(ap->logopt, MODPREFIX "malloc: %s", estr);
4d476f
 			return NSS_STATUS_UNKNOWN;
4d476f
+		}
4d476f
 
4d476f
 		status = check_map_indirect(ap, source,
4d476f
 					    lkp_key, strlen(lkp_key), ctxt);
4d476f
@@ -3029,7 +3599,25 @@ int lookup_mount(struct autofs_point *ap
4d476f
 		cache_readlock(mc);
4d476f
 	else
4d476f
 		cache_writelock(mc);
4d476f
-	me = cache_lookup(mc, key);
4d476f
+
4d476f
+	if (!ap->pref)
4d476f
+		lkp_key = strdup(key);
4d476f
+	else {
4d476f
+		lkp_key = malloc(strlen(ap->pref) + strlen(key) + 1);
4d476f
+		if (lkp_key) {
4d476f
+			strcpy(lkp_key, ap->pref);
4d476f
+			strcat(lkp_key, key);
4d476f
+		}
4d476f
+	}
4d476f
+
4d476f
+	if (!lkp_key) {
4d476f
+		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
4d476f
+		error(ap->logopt, MODPREFIX "malloc: %s", estr);
4d476f
+		cache_unlock(mc);
4d476f
+		return NSS_STATUS_UNKNOWN;
4d476f
+	}
4d476f
+
4d476f
+	me = match_cached_key(ap, MODPREFIX, source, lkp_key);
4d476f
 	/* Stale mapent => check for entry in alternate source or wildcard */
4d476f
 	if (me && !me->mapent) {
4d476f
 		while ((me = cache_lookup_key_next(me)))
4d476f
@@ -3055,6 +3643,7 @@ int lookup_mount(struct autofs_point *ap
4d476f
 		}
4d476f
 	}
4d476f
 	cache_unlock(mc);
4d476f
+	free(lkp_key);
4d476f
 
4d476f
 	if (!mapent)
4d476f
 		return NSS_STATUS_TRYAGAIN;