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