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
getmapkeys_fn(const char * map,struct dir_entry ** entries_p,int * error,int * cache_time,uid_t uid)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
get_contexts(const FN_composite_name_t * name,FN_ctx_t ** ctxp,FN_ctx_t ** prefix_ctxp,FN_ctx_t * init_ctx,FN_status_t * status)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
split_cname(const FN_composite_name_t * name,FN_composite_name_t ** last,FN_composite_name_t ** lead)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
list_ctx_and_or_nns(FN_ctx_t * ctx,FN_ctx_t * prefix_ctx,struct dir_entry ** entries_p,FN_status_t * status)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
need_nns_only(FN_ctx_t * ctx,FN_ctx_t * prefix_ctx,FN_status_t * status)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
need_ctx_and_nns(FN_ctx_t * ctx,FN_status_t * status)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
list_ctx(FN_ctx_t * ctx,struct dir_entry ** entries_p,FN_status_t * status)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
list_ctx_aux(FN_ctx_t * ctx,FN_compound_name_t * name,const FN_attrset_t * syntax,struct dir_entry ** entries_p,FN_status_t * status)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
add_name_to_dirlist(const FN_compound_name_t * name,struct dir_entry ** entries_p)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
slash_hierarchy(const FN_attrset_t * syntax)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
non_slash_hierarchy(const FN_attrset_t * syntax)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
syntax_attrs_equal(const FN_attrset_t * syn1,const FN_attrset_t * syn2)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 *
get_attrval(const FN_attrset_t * attrs,const FN_identifier_t * attr_id)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 *
lookup_ctx(FN_ctx_t * ctx,const FN_composite_name_t * name,bool_t log,FN_status_t * status)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