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
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.
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]
25 * Copyright (c) 1995 - 1996, by Sun Microsystems, Inc.
26 * All rights reserved.
29 #pragma ident "%Z%%M% %I% %E% SMI"
37 #include "automount.h"
38 #include "ns_fnutils.h"
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.
46 * getmapkeys_fn(const char *map, struct dir_entry **, int *error,
47 * int *cache_time, uid_t);
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.
56 * eg: user/jane/service => user/jane + service
57 * org/ssi.eng/user => org/ssi.eng/ + user
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
*);
64 * Split a multi-component composite name into its last component and
65 * its other components. Return zero on success.
68 split_cname(const FN_composite_name_t
*name
, FN_composite_name_t
**last
,
69 FN_composite_name_t
**lead
);
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.
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
83 list_ctx_and_or_nns(FN_ctx_t
*ctx
, FN_ctx_t
*prefix_ctx
, struct dir_entry
**,
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
92 need_nns_only(FN_ctx_t
*ctx
, FN_ctx_t
*prefix_ctx
, FN_status_t
*);
95 * Return true if both the given context and its NNS should be listed.
98 need_ctx_and_nns(FN_ctx_t
*, FN_status_t
*);
101 * Add the subdirectories of a context to the dir_entry list. Return
105 list_ctx(FN_ctx_t
*, struct dir_entry
**, FN_status_t
*);
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.
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:
122 list_ctx_aux(FN_ctx_t
*, FN_compound_name_t
*name
, const FN_attrset_t
*syntax
,
123 struct dir_entry
**, FN_status_t
*);
126 * Add a name to a dir_entry list. Return zero on success.
129 add_name_to_dirlist(const FN_compound_name_t
*, struct dir_entry
**);
132 * Return true if a set of syntax attributes correspond to a
133 * hierarchical namespace with a slash separator. Return false on
137 slash_hierarchy(const FN_attrset_t
*syntax
);
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.
145 non_slash_hierarchy(const FN_attrset_t
*syntax
);
148 * Return true if two syntax attribute sets are equal.
151 syntax_attrs_equal(const FN_attrset_t
*, const FN_attrset_t
*);
154 * Return a value of a given attribute in an attribute set, or NULL
157 static const FN_attrvalue_t
*
158 get_attrval(const FN_attrset_t
*, const FN_identifier_t
*attr_id
);
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.
166 lookup_ctx(FN_ctx_t
*, const FN_composite_name_t
*, bool_t log
, FN_status_t
*);
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().
178 getmapkeys_fn(const char *map
, struct dir_entry
**entries_p
, int *error
,
179 int *cache_time
, uid_t uid
)
181 FN_composite_name_t
*name
;
185 FN_ctx_t
*prefix_ctx
;
188 *cache_time
= RDDIR_CACHE_TIME
;
190 if ((init_fn() != 0) || (status
= fn_status_create()) == NULL
) {
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
);
202 if (strcmp(map
, FNPREFIX
) == 0) {
204 * List the initial context.
205 * Contents of initial ctx is user-relative
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 */
214 if (strcmp(map
, FNPREFIX
"/...") == 0) {
215 /* List X.500 but not DNS. */
216 name
= new_cname("_x500");
218 name
= new_cname(map
+ FNPREFIXLEN
+ 1);
222 } else if (fn_composite_name_count(name
) == 1) {
224 /* List an atomic name. */
225 ctx
= lookup_ctx(init_ctx
, name
, TRUE
, status
);
227 *error
= list_ctx_and_or_nns(ctx
, init_ctx
,
229 fn_ctx_handle_destroy(ctx
);
235 /* List a multi-component name. */
236 *error
= get_contexts(name
, &ctx
, &prefix_ctx
,
239 *error
= list_ctx_and_or_nns(ctx
, prefix_ctx
,
241 fn_ctx_handle_destroy(ctx
);
242 fn_ctx_handle_destroy(prefix_ctx
);
245 fn_composite_name_destroy(name
);
247 fn_status_destroy(status
);
248 fn_ctx_handle_destroy(init_ctx
);
252 * create the binary tree of entries
254 for (p
= *entries_p
; p
!= NULL
; p
= p
->next
)
255 btree_enter(entries_p
, p
);
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
)
264 FN_composite_name_t
*prefix
= NULL
;
265 FN_composite_name_t
*suffix
= NULL
;
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.
273 if (split_cname(name
, &suffix
, &prefix
) != 0) {
277 *prefix_ctxp
= lookup_ctx(init_ctx
, prefix
, TRUE
, status
);
278 fn_composite_name_destroy(prefix
);
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
);
285 fn_ctx_handle_destroy(*prefix_ctxp
);
286 *prefix_ctxp
= nns_ctx
;
288 fn_ctx_handle_destroy(nns_ctx
);
293 lookup_ctx(*prefix_ctxp
, suffix
, FALSE
, status
);
296 fn_composite_name_destroy(suffix
);
297 return (*ctxp
!= NULL
? 0 : -1);
302 split_cname(const FN_composite_name_t
*name
, FN_composite_name_t
**last
,
303 FN_composite_name_t
**lead
)
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
) {
312 fn_composite_name_destroy(*last
);
313 fn_composite_name_destroy(*lead
);
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
)
327 if (!need_nns_only(ctx
, prefix_ctx
, status
)) {
328 if (list_ctx(ctx
, entries_p
, status
) != 0) {
331 if (!need_ctx_and_nns(ctx
, status
)) {
335 nns_ctx
= lookup_ctx(ctx
, slash_cname
, FALSE
, status
);
336 if (nns_ctx
== NULL
) {
339 rc
= list_ctx(nns_ctx
, entries_p
, status
);
340 fn_ctx_handle_destroy(nns_ctx
);
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).
351 need_nns_only(FN_ctx_t
*ctx
, FN_ctx_t
*prefix_ctx
, FN_status_t
*status
)
354 FN_attrset_t
*prefix_syn
;
357 syn
= fn_ctx_get_syntax_attrs(ctx
, empty_cname
, status
);
358 if (syn
== NULL
|| !non_slash_hierarchy(syn
)) {
359 fn_attrset_destroy(syn
);
363 * ctx is hierarchical and not slash-separated. How about prefix_ctx?
365 prefix_syn
= fn_ctx_get_syntax_attrs(prefix_ctx
, empty_cname
, status
);
366 retval
= (prefix_syn
== NULL
) || syntax_attrs_equal(syn
, prefix_syn
);
368 fn_attrset_destroy(syn
);
369 fn_attrset_destroy(prefix_syn
);
375 * True if ctx has a slash-separated hierarchical syntax.
378 need_ctx_and_nns(FN_ctx_t
*ctx
, FN_status_t
*status
)
383 syn
= fn_ctx_get_syntax_attrs(ctx
, empty_cname
, status
);
387 retval
= slash_hierarchy(syn
);
388 fn_attrset_destroy(syn
);
394 list_ctx(FN_ctx_t
*ctx
, struct dir_entry
**entries_p
, FN_status_t
*status
)
396 FN_attrset_t
*syntax
;
397 FN_compound_name_t
*name
;
400 syntax
= fn_ctx_get_syntax_attrs(ctx
, empty_cname
, status
);
401 if (syntax
== NULL
) {
402 logstat(status
, "", "bad syntax attributes");
406 fn_compound_name_from_syntax_attrs(syntax
, empty_string
, status
);
408 logstat(status
, "", "could not create compound name");
409 fn_attrset_destroy(syntax
);
412 if (!non_slash_hierarchy(syntax
)) {
413 fn_attrset_destroy(syntax
);
416 retval
= list_ctx_aux(ctx
, name
, syntax
, entries_p
, status
);
417 fn_attrset_destroy(syntax
);
418 fn_compound_name_destroy(name
);
424 list_ctx_aux(FN_ctx_t
*ctx
, FN_compound_name_t
*name
,
425 const FN_attrset_t
*syntax
, struct dir_entry
**entries_p
,
428 FN_bindinglist_t
*bindings
;
435 bindings
= fn_ctx_list_bindings(ctx
, empty_cname
, status
);
436 if (bindings
== NULL
) {
439 while ((child
= fn_bindinglist_next(bindings
, &ref
, status
)) != NULL
) {
440 if (fn_compound_name_append_comp(name
, child
, &stat
) == 0) {
444 if (add_name_to_dirlist(name
, entries_p
) != 0) {
448 if (syntax
!= NULL
) {
449 /* Traverse hierarchy. */
450 ctx
= fn_ctx_handle_from_ref(ref
, XFN2(0) status
);
452 rc
= list_ctx_aux(ctx
, name
, syntax
, entries_p
,
454 fn_ctx_handle_destroy(ctx
);
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
);
466 fn_string_destroy(child
);
467 fn_bindinglist_destroy(bindings
XFN1(status
));
473 add_name_to_dirlist(const FN_compound_name_t
*name
,
474 struct dir_entry
**entries_p
)
479 struct dir_entry
*entry
;
481 string
= fn_string_from_compound_name(name
);
482 if (string
== NULL
) {
486 str
= (char *)fn_string_str(string
, &stat
);
488 str
= auto_rddir_strdup(str
);
490 fn_string_destroy(string
);
496 /* LINTED pointer alignment */
497 entry
= (struct dir_entry
*)
498 auto_rddir_malloc(sizeof (*entry
));
504 (void) memset((char *)entry
, 0, sizeof (*entry
));
506 entry
->next
= *entries_p
;
513 * Identifiers of syntax attributes for direction and separator.
516 static const FN_identifier_t syntax_direction
= {
518 sizeof ("fn_std_syntax_direction") - 1,
519 "fn_std_syntax_direction"
522 static const FN_identifier_t syntax_separator
= {
524 sizeof ("fn_std_syntax_separator") - 1,
525 "fn_std_syntax_separator"
530 slash_hierarchy(const FN_attrset_t
*syntax
)
532 const FN_attrvalue_t
*dir
= get_attrval(syntax
, &syntax_direction
);
533 const FN_attrvalue_t
*sep
= get_attrval(syntax
, &syntax_separator
);
535 return (dir
!= NULL
&&
536 memcmp("flat", dir
->contents
, dir
->length
) != 0 &&
538 memcmp("/", sep
->contents
, sep
->length
) == 0);
543 non_slash_hierarchy(const FN_attrset_t
*syntax
)
545 const FN_attrvalue_t
*dir
= get_attrval(syntax
, &syntax_direction
);
546 const FN_attrvalue_t
*sep
= get_attrval(syntax
, &syntax_separator
);
548 return (dir
!= NULL
&&
549 memcmp("flat", dir
->contents
, dir
->length
) != 0 &&
551 memcmp("/", sep
->contents
, sep
->length
) != 0);
556 syntax_attrs_equal(const FN_attrset_t
*syn1
, const FN_attrset_t
*syn2
)
558 const FN_attribute_t
*attr
;
559 const FN_attrvalue_t
*val1
;
560 const FN_attrvalue_t
*val2
;
564 if (fn_attrset_count(syn1
) != fn_attrset_count(syn2
)) {
567 for (attr
= fn_attrset_first(syn1
, &iter1
);
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
)) {
576 if (val1
!= NULL
&& val2
!= NULL
) {
577 if (val1
->length
!= val2
->length
||
578 memcmp(val1
->contents
, val2
->contents
,
579 val1
->length
) != 0) {
588 static const FN_attrvalue_t
*
589 get_attrval(const FN_attrset_t
*attrs
, const FN_identifier_t
*attr_id
)
591 const FN_attribute_t
*attr
;
594 attr
= fn_attrset_get(attrs
, attr_id
);
596 return (fn_attribute_first(attr
, &iter
));
604 lookup_ctx(FN_ctx_t
*ctx
, const FN_composite_name_t
*name
, bool_t log
,
610 ref
= fn_ctx_lookup(ctx
, name
, status
);
613 msg
= "lookup failed";
615 ctx
= fn_ctx_handle_from_ref(ref
, XFN2(0) status
);
618 msg
= "could not construct context handle";
621 if (ctx
== NULL
&& verbose
&& (log
|| transient(status
))) {
622 logstat(status
, "", msg
);