xref: /illumos-gate/usr/src/cmd/fs.d/autofs/ns_fnreaddir.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
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