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