Home | History | Annotate | Download | only in autofs
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * ns_fnreaddir.c
     24  *
     25  * Copyright (c) 1995 - 1996, by Sun Microsystems, Inc.
     26  * All rights reserved.
     27  */
     28 
     29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     30 
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <syslog.h>
     35 #include <rpc/rpc.h>
     36 #include <xfn/xfn.h>
     37 #include "automount.h"
     38 #include "ns_fnutils.h"
     39 
     40 
     41 /*
     42  * Given the name of an XFN map, create a list of the map entries for a
     43  * given user.  Set error to zero on success.
     44  *
     45  *	extern void
     46  *	getmapkeys_fn(const char *map, struct dir_entry **, int *error,
     47  *	    int *cache_time, uid_t);
     48  */
     49 
     50 /*
     51  * Given a multi-component composite name, construct the corresponding
     52  * context handle and the context handle of its prefix.  The prefix is
     53  * that part of the name up to (and possibly including) the last slash
     54  * in the name.  Return zero on success.
     55  *
     56  * eg:	user/jane/service  =>  user/jane + service
     57  *	org/ssi.eng/user   =>  org/ssi.eng/ + user
     58  */
     59 static int
     60 get_contexts(const FN_composite_name_t *, FN_ctx_t **ctxp,
     61     FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *);
     62 
     63 /*
     64  * Split a multi-component composite name into its last component and
     65  * its other components.  Return zero on success.
     66  */
     67 static int
     68 split_cname(const FN_composite_name_t *name, FN_composite_name_t **last,
     69     FN_composite_name_t **lead);
     70 
     71 /*
     72  * Given a context and its prefix context (defined above), determine
     73  * whether the context, its NNS context, or both should be listed.
     74  * (The syntaxes of the contexts are used to help make this
     75  * determination.)  Add the subdirectories of the appropriate
     76  * context(s) to the dir_entry list.  Return zero on success.
     77  *
     78  * eg:	"ls /xfn/user		=>  list context only
     79  *	"ls /xfn/org/ssi.eng"	=>  list NNS only
     80  *	"ls /xfn/.../c=us"	=>  list context and NNS
     81  */
     82 static int
     83 list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, struct dir_entry **,
     84     FN_status_t *);
     85 
     86 /*
     87  * Given a context and its prefix context (defined above), return true
     88  * if the NNS of the context should be listed but the context itself
     89  * should not.
     90  */
     91 static bool_t
     92 need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *);
     93 
     94 /*
     95  * Return true if both the given context and its NNS should be listed.
     96  */
     97 static bool_t
     98 need_ctx_and_nns(FN_ctx_t *, FN_status_t *);
     99 
    100 /*
    101  * Add the subdirectories of a context to the dir_entry list.  Return
    102  * zero on success.
    103  */
    104 static int
    105 list_ctx(FN_ctx_t *, struct dir_entry **, FN_status_t *);
    106 
    107 /*
    108  * Given a context and its name relative to the root of its rightmost
    109  * naming system, add the context's subdirectories to the dir_entry
    110  * list.  If syntax is non-NULL recursively list names until a context
    111  * with a different syntax is encountered, otherwise list one level
    112  * only.  May modify "name".  Return zero on success.
    113  *
    114  * eg:  For the context org/eng with syntax "dot-separated, right-to-left",
    115  * the compound name "eng" would be passed in, and the following might
    116  * be added to the dir_entry list:
    117  * 	ssi.eng
    118  *	feds.ssi.eng
    119  * 	ste.eng
    120  */
    121 static int
    122 list_ctx_aux(FN_ctx_t *, FN_compound_name_t *name, const FN_attrset_t *syntax,
    123     struct dir_entry **, FN_status_t *);
    124 
    125 /*
    126  * Add a name to a dir_entry list.  Return zero on success.
    127  */
    128 static int
    129 add_name_to_dirlist(const FN_compound_name_t *, struct dir_entry **);
    130 
    131 /*
    132  * Return true if a set of syntax attributes correspond to a
    133  * hierarchical namespace with a slash separator.  Return false on
    134  * error.
    135  */
    136 static bool_t
    137 slash_hierarchy(const FN_attrset_t *syntax);
    138 
    139 /*
    140  * Return true if a set of syntax attributes correspond to a
    141  * hierarchical namespace with a separator other than a slash.
    142  * Return false on error.
    143  */
    144 static bool_t
    145 non_slash_hierarchy(const FN_attrset_t *syntax);
    146 
    147 /*
    148  * Return true if two syntax attribute sets are equal.
    149  */
    150 static bool_t
    151 syntax_attrs_equal(const FN_attrset_t *, const FN_attrset_t *);
    152 
    153 /*
    154  * Return a value of a given attribute in an attribute set, or NULL
    155  * on error.
    156  */
    157 static const FN_attrvalue_t *
    158 get_attrval(const FN_attrset_t *, const FN_identifier_t *attr_id);
    159 
    160 /*
    161  * Lookup a name and return the corresponding context handle.  On
    162  * error return NULL and, if "log" is true or the error is transient,
    163  * log an error message.
    164  */
    165 static FN_ctx_t *
    166 lookup_ctx(FN_ctx_t *, const FN_composite_name_t *, bool_t log, FN_status_t *);
    167 
    168 
    169 /*
    170  * Unlike during a lookup or mount, transient errors are tolerated.  A
    171  * potentially transient error during a readdir() (such as no response
    172  * from an X.500 server) could result in an incomplete listing, but at
    173  * least readdir() will return everything that it can.  Note that it
    174  * is still possible to mount a directory that for some reason did not
    175  * show up in a prior readdir().
    176  */
    177 void
    178 getmapkeys_fn(const char *map, struct dir_entry **entries_p, int *error,
    179     int *cache_time, uid_t uid)
    180 {
    181 	FN_composite_name_t	*name;
    182 	FN_status_t		*status;
    183 	FN_ctx_t		*init_ctx;
    184 	FN_ctx_t		*ctx;
    185 	FN_ctx_t		*prefix_ctx;
    186 	struct dir_entry	*p;
    187 
    188 	*cache_time = RDDIR_CACHE_TIME;
    189 
    190 	if ((init_fn() != 0) || (status = fn_status_create()) == NULL) {
    191 		log_mem_failure();
    192 		*error = -1;
    193 		return;
    194 	}
    195 	init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status);
    196 	if (init_ctx == NULL) {
    197 		logstat(status, "", "No initial context");
    198 		fn_status_destroy(status);
    199 		return;
    200 	}
    201 
    202 	if (strcmp(map, FNPREFIX) == 0) {
    203 		/*
    204 		 * List the initial context.
    205 		 * Contents of initial ctx is user-relative
    206 		 */
    207 		*cache_time = 0;
    208 		*error = list_ctx(init_ctx, entries_p, status);
    209 	} else if (strcmp(map, FNPREFIX "/_dns") == 0) {
    210 		/* Cannot list DNS; report success but no entries. */
    211 		*cache_time = 1000000;	/* no sense trying again */
    212 		*error = 0;
    213 	} else {
    214 		if (strcmp(map, FNPREFIX "/...") == 0) {
    215 			/* List X.500 but not DNS. */
    216 			name = new_cname("_x500");
    217 		} else {
    218 			name = new_cname(map + FNPREFIXLEN + 1);
    219 		}
    220 		if (name == NULL) {
    221 			*error = -1;
    222 		} else if (fn_composite_name_count(name) == 1) {
    223 
    224 			/* List an atomic name. */
    225 			ctx = lookup_ctx(init_ctx, name, TRUE, status);
    226 			if (ctx != NULL) {
    227 				*error = list_ctx_and_or_nns(ctx, init_ctx,
    228 				    entries_p, status);
    229 				fn_ctx_handle_destroy(ctx);
    230 			} else {
    231 				*error = -1;
    232 			}
    233 		} else {
    234 
    235 			/* List a multi-component name. */
    236 			*error = get_contexts(name, &ctx, &prefix_ctx,
    237 			    init_ctx, status);
    238 			if (*error == 0) {
    239 				*error = list_ctx_and_or_nns(ctx, prefix_ctx,
    240 				    entries_p, status);
    241 				fn_ctx_handle_destroy(ctx);
    242 				fn_ctx_handle_destroy(prefix_ctx);
    243 			}
    244 		}
    245 		fn_composite_name_destroy(name);
    246 	}
    247 	fn_status_destroy(status);
    248 	fn_ctx_handle_destroy(init_ctx);
    249 
    250 	if (*error == 0) {
    251 		/*
    252 		 * create the binary tree of entries
    253 		 */
    254 		for (p = *entries_p; p != NULL; p = p->next)
    255 			btree_enter(entries_p, p);
    256 	}
    257 }
    258 
    259 
    260 static int
    261 get_contexts(const FN_composite_name_t *name, FN_ctx_t **ctxp,
    262     FN_ctx_t **prefix_ctxp, FN_ctx_t *init_ctx, FN_status_t *status)
    263 {
    264 	FN_composite_name_t	*prefix = NULL;
    265 	FN_composite_name_t	*suffix = NULL;
    266 	FN_ctx_t		*nns_ctx;
    267 
    268 	/*
    269 	 * Break a name such as "pre/fix/suffix" into "pre/fix/" and
    270 	 * "suffix".  If that fails, try "pre/fix" and "suffix".  This
    271 	 * can be more efficient than doing it the reverse order.
    272 	 */
    273 	if (split_cname(name, &suffix, &prefix) != 0) {
    274 		return (-1);
    275 	}
    276 	*ctxp = NULL;
    277 	*prefix_ctxp = lookup_ctx(init_ctx, prefix, TRUE, status);
    278 	fn_composite_name_destroy(prefix);
    279 
    280 	if (*prefix_ctxp != NULL) {
    281 		nns_ctx = lookup_ctx(*prefix_ctxp, slash_cname, FALSE, status);
    282 		if (nns_ctx != NULL) {
    283 			*ctxp = lookup_ctx(nns_ctx, suffix, FALSE, status);
    284 			if (*ctxp != NULL) {
    285 				fn_ctx_handle_destroy(*prefix_ctxp);
    286 				*prefix_ctxp = nns_ctx;
    287 			} else {
    288 				fn_ctx_handle_destroy(nns_ctx);
    289 			}
    290 		}
    291 		if (*ctxp == NULL) {
    292 			*ctxp =
    293 			    lookup_ctx(*prefix_ctxp, suffix, FALSE, status);
    294 		}
    295 	}
    296 	fn_composite_name_destroy(suffix);
    297 	return (*ctxp != NULL ? 0 : -1);
    298 }
    299 
    300 
    301 static int
    302 split_cname(const FN_composite_name_t *name, FN_composite_name_t **last,
    303     FN_composite_name_t **lead)
    304 {
    305 	void	*iter;
    306 
    307 	(void) fn_composite_name_last(name, &iter);
    308 	*last = fn_composite_name_suffix(name, iter);
    309 	*lead = fn_composite_name_prefix(name, iter);
    310 	if (*last == NULL || *lead == NULL) {
    311 		log_mem_failure();
    312 		fn_composite_name_destroy(*last);
    313 		fn_composite_name_destroy(*lead);
    314 		return (-1);
    315 	}
    316 	return (0);
    317 }
    318 
    319 
    320 static int
    321 list_ctx_and_or_nns(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx,
    322     struct dir_entry **entries_p, FN_status_t *status)
    323 {
    324 	FN_ctx_t	*nns_ctx;
    325 	int		rc;
    326 
    327 	if (!need_nns_only(ctx, prefix_ctx, status)) {
    328 		if (list_ctx(ctx, entries_p, status) != 0) {
    329 			return (-1);
    330 		}
    331 		if (!need_ctx_and_nns(ctx, status)) {
    332 			return (0);
    333 		}
    334 	}
    335 	nns_ctx = lookup_ctx(ctx, slash_cname, FALSE, status);
    336 	if (nns_ctx == NULL) {
    337 		return (0);
    338 	}
    339 	rc = list_ctx(nns_ctx, entries_p, status);
    340 	fn_ctx_handle_destroy(nns_ctx);
    341 	return (rc);
    342 }
    343 
    344 
    345 /*
    346  * True if ctx has a hierarchical syntax with a non-slash separator
    347  * and prefix_ctx either has the same syntax or does not provide any
    348  * syntax ("..." should be the only example of the latter condition).
    349  */
    350 static bool_t
    351 need_nns_only(FN_ctx_t *ctx, FN_ctx_t *prefix_ctx, FN_status_t *status)
    352 {
    353 	FN_attrset_t	*syn;
    354 	FN_attrset_t	*prefix_syn;
    355 	bool_t		retval;
    356 
    357 	syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status);
    358 	if (syn == NULL || !non_slash_hierarchy(syn)) {
    359 		fn_attrset_destroy(syn);
    360 		return (FALSE);
    361 	}
    362 	/*
    363 	 * ctx is hierarchical and not slash-separated.  How about prefix_ctx?
    364 	 */
    365 	prefix_syn = fn_ctx_get_syntax_attrs(prefix_ctx, empty_cname, status);
    366 	retval = (prefix_syn == NULL) || syntax_attrs_equal(syn, prefix_syn);
    367 
    368 	fn_attrset_destroy(syn);
    369 	fn_attrset_destroy(prefix_syn);
    370 	return (retval);
    371 }
    372 
    373 
    374 /*
    375  * True if ctx has a slash-separated hierarchical syntax.
    376  */
    377 static bool_t
    378 need_ctx_and_nns(FN_ctx_t *ctx, FN_status_t *status)
    379 {
    380 	FN_attrset_t	*syn;
    381 	bool_t		retval;
    382 
    383 	syn = fn_ctx_get_syntax_attrs(ctx, empty_cname, status);
    384 	if (syn == NULL) {
    385 		return (FALSE);
    386 	}
    387 	retval = slash_hierarchy(syn);
    388 	fn_attrset_destroy(syn);
    389 	return (retval);
    390 }
    391 
    392 
    393 static int
    394 list_ctx(FN_ctx_t *ctx, struct dir_entry **entries_p, FN_status_t *status)
    395 {
    396 	FN_attrset_t		*syntax;
    397 	FN_compound_name_t	*name;
    398 	int			retval;
    399 
    400 	syntax = fn_ctx_get_syntax_attrs(ctx, empty_cname, status);
    401 	if (syntax == NULL) {
    402 		logstat(status, "", "bad syntax attributes");
    403 		return (-1);
    404 	}
    405 	name =
    406 	    fn_compound_name_from_syntax_attrs(syntax, empty_string, status);
    407 	if (name == NULL) {
    408 		logstat(status, "", "could not create compound name");
    409 		fn_attrset_destroy(syntax);
    410 		return (-1);
    411 	}
    412 	if (!non_slash_hierarchy(syntax)) {
    413 		fn_attrset_destroy(syntax);
    414 		syntax = NULL;
    415 	}
    416 	retval = list_ctx_aux(ctx, name, syntax, entries_p, status);
    417 	fn_attrset_destroy(syntax);
    418 	fn_compound_name_destroy(name);
    419 	return (retval);
    420 }
    421 
    422 
    423 static int
    424 list_ctx_aux(FN_ctx_t *ctx, FN_compound_name_t *name,
    425     const FN_attrset_t *syntax, struct dir_entry **entries_p,
    426     FN_status_t *status)
    427 {
    428 	FN_bindinglist_t	*bindings;
    429 	FN_string_t		*child;
    430 	FN_ref_t		*ref;
    431 	unsigned int		stat;
    432 	int			rc = 0;
    433 	void			*iter;
    434 
    435 	bindings = fn_ctx_list_bindings(ctx, empty_cname, status);
    436 	if (bindings == NULL) {
    437 		return (0);
    438 	}
    439 	while ((child = fn_bindinglist_next(bindings, &ref, status)) != NULL) {
    440 		if (fn_compound_name_append_comp(name, child, &stat) == 0) {
    441 			rc = -1;
    442 			break;
    443 		}
    444 		if (add_name_to_dirlist(name, entries_p) != 0) {
    445 			rc = -1;
    446 			break;
    447 		}
    448 		if (syntax != NULL) {
    449 			/* Traverse hierarchy. */
    450 			ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status);
    451 			if (ctx != NULL) {
    452 				rc = list_ctx_aux(ctx, name, syntax, entries_p,
    453 				    status);
    454 				fn_ctx_handle_destroy(ctx);
    455 				if (rc != 0) {
    456 					break;
    457 				}
    458 			}
    459 		}
    460 		fn_ref_destroy(ref);
    461 		fn_string_destroy(child);
    462 		(void) fn_compound_name_last(name, &iter);
    463 		(void) fn_compound_name_next(name, &iter);
    464 		(void) fn_compound_name_delete_comp(name, &iter);
    465 	}
    466 	fn_string_destroy(child);
    467 	fn_bindinglist_destroy(bindings XFN1(status));
    468 	return (rc);
    469 }
    470 
    471 
    472 static int
    473 add_name_to_dirlist(const FN_compound_name_t *name,
    474     struct dir_entry **entries_p)
    475 {
    476 	FN_string_t		*string;
    477 	char			*str;
    478 	unsigned int		stat;
    479 	struct dir_entry	*entry;
    480 
    481 	string = fn_string_from_compound_name(name);
    482 	if (string == NULL) {
    483 		log_mem_failure();
    484 		return (-1);
    485 	}
    486 	str = (char *)fn_string_str(string, &stat);
    487 	if (str != NULL) {
    488 		str = auto_rddir_strdup(str);
    489 	}
    490 	fn_string_destroy(string);
    491 	if (str == NULL) {
    492 		log_mem_failure();
    493 		return (-1);
    494 	}
    495 
    496 	/* LINTED pointer alignment */
    497 	entry = (struct dir_entry *)
    498 		auto_rddir_malloc(sizeof (*entry));
    499 	if (entry == NULL) {
    500 		log_mem_failure();
    501 		free(str);
    502 		return (-1);
    503 	}
    504 	(void) memset((char *)entry, 0, sizeof (*entry));
    505 	entry->name = str;
    506 	entry->next = *entries_p;
    507 	*entries_p = entry;
    508 	return (0);
    509 }
    510 
    511 
    512 /*
    513  * Identifiers of syntax attributes for direction and separator.
    514  */
    515 
    516 static const FN_identifier_t syntax_direction = {
    517 	FN_ID_STRING,
    518 	sizeof ("fn_std_syntax_direction") - 1,
    519 	"fn_std_syntax_direction"
    520 };
    521 
    522 static const FN_identifier_t syntax_separator = {
    523 	FN_ID_STRING,
    524 	sizeof ("fn_std_syntax_separator") - 1,
    525 	"fn_std_syntax_separator"
    526 };
    527 
    528 
    529 static bool_t
    530 slash_hierarchy(const FN_attrset_t *syntax)
    531 {
    532 	const FN_attrvalue_t	*dir = get_attrval(syntax, &syntax_direction);
    533 	const FN_attrvalue_t	*sep = get_attrval(syntax, &syntax_separator);
    534 
    535 	return (dir != NULL &&
    536 	    memcmp("flat", dir->contents, dir->length) != 0 &&
    537 	    sep != NULL &&
    538 	    memcmp("/", sep->contents, sep->length) == 0);
    539 }
    540 
    541 
    542 static bool_t
    543 non_slash_hierarchy(const FN_attrset_t *syntax)
    544 {
    545 	const FN_attrvalue_t	*dir = get_attrval(syntax, &syntax_direction);
    546 	const FN_attrvalue_t	*sep = get_attrval(syntax, &syntax_separator);
    547 
    548 	return (dir != NULL &&
    549 	    memcmp("flat", dir->contents, dir->length) != 0 &&
    550 	    sep != NULL &&
    551 	    memcmp("/", sep->contents, sep->length) != 0);
    552 }
    553 
    554 
    555 static bool_t
    556 syntax_attrs_equal(const FN_attrset_t *syn1, const FN_attrset_t *syn2)
    557 {
    558 	const FN_attribute_t	*attr;
    559 	const FN_attrvalue_t	*val1;
    560 	const FN_attrvalue_t	*val2;
    561 	void			*iter1;
    562 	void			*iter2;
    563 
    564 	if (fn_attrset_count(syn1) != fn_attrset_count(syn2)) {
    565 		return (FALSE);
    566 	}
    567 	for (attr = fn_attrset_first(syn1, &iter1);
    568 	    attr != NULL;
    569 	    attr = fn_attrset_next(syn1, &iter1)) {
    570 		val1 = fn_attribute_first(attr, &iter2);
    571 		val2 = get_attrval(syn2, fn_attribute_identifier(attr));
    572 		if ((val1 == NULL && val2 != NULL) ||
    573 		    (val1 != NULL && val2 == NULL)) {
    574 			return (FALSE);
    575 		}
    576 		if (val1 != NULL && val2 != NULL) {
    577 			if (val1->length != val2->length ||
    578 			    memcmp(val1->contents, val2->contents,
    579 				    val1->length) != 0) {
    580 				return (FALSE);
    581 			}
    582 		}
    583 	}
    584 	return (TRUE);
    585 }
    586 
    587 
    588 static const FN_attrvalue_t *
    589 get_attrval(const FN_attrset_t *attrs, const FN_identifier_t *attr_id)
    590 {
    591 	const FN_attribute_t	*attr;
    592 	void			*iter;
    593 
    594 	attr = fn_attrset_get(attrs, attr_id);
    595 	if (attr != NULL) {
    596 		return (fn_attribute_first(attr, &iter));
    597 	} else {
    598 		return (NULL);
    599 	}
    600 }
    601 
    602 
    603 static FN_ctx_t *
    604 lookup_ctx(FN_ctx_t *ctx, const FN_composite_name_t *name, bool_t log,
    605     FN_status_t *status)
    606 {
    607 	FN_ref_t	*ref;
    608 	char		*msg;
    609 
    610 	ref = fn_ctx_lookup(ctx, name, status);
    611 	if (ref == NULL) {
    612 		ctx = NULL;
    613 		msg = "lookup failed";
    614 	} else {
    615 		ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status);
    616 		fn_ref_destroy(ref);
    617 		if (ctx == NULL) {
    618 			msg = "could not construct context handle";
    619 		}
    620 	}
    621 	if (ctx == NULL && verbose && (log || transient(status))) {
    622 		logstat(status, "", msg);
    623 	}
    624 	return (ctx);
    625 }
    626