8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / fs.d / autofs / ns_fnmount.c
blobb5fffcd7600aa6212ced04f29647c0fa3914f83c
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * ns_fnmount.c
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <syslog.h>
33 #include <rpc/rpc.h>
34 #include <rpcsvc/nis.h>
35 #include <xfn/xfn.h>
36 #include "automount.h"
37 #include "ns_fnutils.h"
41 * The maximum sizes of map names, key names, composite names, and status
42 * descriptions, including the trailing '\0'.
44 #define MAPNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1)
45 #define KEYNAMESZ (size_t)(AUTOFS_MAXCOMPONENTLEN + 1)
46 #define COMPNAMESZ (size_t)(MAPNAMESZ - FNPREFIXLEN + KEYNAMESZ - 2)
47 #define DESCSZ (size_t)512
49 typedef struct mapent mapent;
50 typedef struct mapline mapline;
54 * The name of an attribute.
56 static const FN_identifier_t attr_exported = {FN_ID_STRING, 8, "exported"};
60 * Given a request by a particular user to mount the name "key" under
61 * map/context "map", and a set of default mount options, return (in
62 * "res") either a list of mapents giving the mounts that need to be
63 * performed, or a symbolic link to be created for a user-relative
64 * context. If "shallow" is true return, in place of the list of
65 * mapents, a single mapent representing an indirect mount point.
67 * void
68 * getmapent_fn(char *key, char *map, char *opts, uid_t uid,
69 * bool_t shallow, getmapent_fn_res *res);
73 * Given a reference, its composite name, default mount options, and a
74 * mapent root, return a list of mapents to mount. If "shallow" is
75 * true return, in place of the list of mapents, a single mapent
76 * representing an indirect mount point. The map and key strings are
77 * pieces of the composite name such that:
78 * "FNPREFIX/cname" == "map/key".
80 static mapent *
81 process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key,
82 char *opts, char *root, bool_t shallow, FN_status_t *status);
85 * Traverse the namespace to find a frontier below ref along which
86 * future mounts may need to be triggered. Add to mapents the
87 * corresponding direct autofs mount points.
88 * map: map name for ref
89 * maplen: strlen(map)
90 * mntpnt: suffix of map where the current mount request begins
91 * (starts off as "", and grows as we traverse the namespace)
92 * opts: default mount options
93 * status: passed from above to avoid having to allocate one on each call
94 * Works by calling frontier_aux() on each name bound under ref.
95 * Return the new mapents, or free mapents and return NULL on failure.
97 static mapent *
98 frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen,
99 char *mntpnt, char *opts, FN_status_t *status);
102 * Called by frontier(), once for each "name" that it finds. map is
103 * passed unchanged from frontier(). ref is the reference named by
104 * "map/name". If ref is found to be along the frontier, add the
105 * corresponding direct autofs mount point to mapents. Otherwise
106 * continue traversing the namespace to find the frontier. Other
107 * arguments and the return value are as for frontier().
109 static mapent *
110 frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen,
111 char *mntpnt, const char *name, char *opts, FN_status_t *status);
114 * Given a reference with an address type of ADDR_HOST and its
115 * composite name, check the attr_exported attribute to determine if
116 * the corresponding directory is exported. Return FALSE on error.
118 static bool_t
119 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status);
122 * Find a reference's address type and, if "data" is not NULL, its
123 * data string. If there is no address of a known type, set *typep to
124 * NUM_ADDRTYPES; if there are several, stop after finding the first.
125 * Return 0 on success.
127 static int
128 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep,
129 char *data, size_t datasz);
132 * Decode an address's data into a string. Return 0 on success.
134 static int
135 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[],
136 size_t strsz);
139 * Given a map name and its current length, append "/name". Return
140 * the new length. On error, syslog a warning and return 0.
142 static size_t
143 append_mapname(char *map, size_t maplen, const char *name);
146 * Concatenate two strings using the given separator. The result is a
147 * newly-allocated string, or NULL on error.
149 static char *
150 concat(const char *s1, char sep, const char *s2);
153 * Add the "nosuid" option to a mapent. Also check for a sneaky
154 * hacker trying to override this option by manually inserting a
155 * multiple mount entry into the XFN namespace. Return FALSE on error.
157 static bool_t
158 safe_mapent(mapent *me);
161 * Append "nosuid" to a list of options. The result is a
162 * newly-allocated string, or NULL on error.
164 static char *
165 safe_opts(const char *opts);
168 * Trim comments and trailing whitespace from ml->linebuf, then
169 * unquote it and leave the result in ml. Return 0 on success.
171 static int
172 trim_line(mapline *ml);
175 * Determine whether ml contains an option string (such as "-ro") and
176 * nothing else.
178 static bool_t
179 opts_only(const mapline *ml);
182 * Allocate a new mapent structure. The arguments must have been
183 * malloc'ed, and are owned by the mapent; they are freed if
184 * new_mapent() fails. If any argument is NULL, the call fails and a
185 * memory allocation failure is logged. A root argument of 'noroot'
186 * indicates that the map_root field does not need to be set (it's
187 * only needed in the first of a list of mapents).
189 static char *noroot = "[no root]";
190 static mapent *
191 new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host,
192 char *dir);
195 * Determine whether cname is a user-relative binding -- such as "myself" --
196 * in the initial context.
198 static bool_t
199 is_user_relative(const char *cname);
202 * Given the name of a user-relative binding, return an equivalent
203 * name that is not user-relative.
205 static char *
206 equiv_name(FN_ctx_t *, const char *cname, FN_status_t *);
208 void
209 getmapent_fn(char *key, char *map, char *opts, uid_t uid, bool_t shallow,
210 getmapent_fn_res *res)
212 size_t maplen;
213 FN_status_t *status;
214 FN_ctx_t *init_ctx = NULL;
215 int statcode;
216 char cname[COMPNAMESZ];
217 FN_composite_name_t *compname;
218 FN_ref_t *ref;
219 char mapname[MAPNAMESZ];
220 char *root;
222 res->type = FN_NONE;
223 res->m_or_l.mapents = NULL;
225 if (init_fn() != 0) {
226 return;
230 * For direct mounts, the key is the entire path, and the map
231 * name already has the final key component appended. Split
232 * apart the map name and key. The "root" of the mapent is
233 * "/key" for indirect mounts, and "" for direct mounts.
235 strcpy(mapname, map);
236 if (key[0] == '/') {
237 key = strrchr(key, '/') + 1;
238 *strrchr(mapname, '/') = '\0';
239 root = strdup("");
240 } else {
241 root = concat("", '/', key);
243 map = mapname;
244 maplen = strlen(map);
246 if ((maplen - FNPREFIXLEN + strlen(key)) >= COMPNAMESZ) {
247 if (verbose) {
248 syslog(LOG_ERR, "name %s/%s too long", map, key);
250 return;
252 if (maplen == FNPREFIXLEN) {
253 strcpy(cname, key);
254 } else {
255 sprintf(cname, "%s/%s", map + FNPREFIXLEN + 1, key);
258 status = fn_status_create();
259 if (status == NULL) {
260 if (verbose) {
261 syslog(LOG_ERR, "Could not create FNS status object");
263 return;
265 init_ctx = _fn_ctx_handle_from_initial_with_uid(uid, 0, status);
266 if (init_ctx == NULL) {
267 logstat(status, "", "No initial context");
268 goto done;
271 #ifndef XFN1ENV
272 if (is_user_relative(cname)) {
273 res->type = FN_SYMLINK;
274 res->m_or_l.symlink = equiv_name(init_ctx, cname, status);
275 goto done;
277 #endif
279 if ((compname = new_cname(cname)) == NULL) {
280 goto done;
282 ref = fn_ctx_lookup(init_ctx, compname, status);
283 statcode = fn_status_code(status);
284 fn_composite_name_destroy(compname);
286 if (trace > 1 && !shallow) {
287 trace_prt(1, " FNS traversal: %s\n", cname);
290 if (ref == NULL) {
291 if ((statcode != FN_E_NAME_NOT_FOUND) &&
292 (statcode != FN_E_NOT_A_CONTEXT)) {
293 logstat(status, "lookup failed on", cname);
295 goto done;
298 res->type = FN_MAPENTS;
299 res->m_or_l.mapents =
300 process_ref(ref, cname, map, key, opts, root, shallow, status);
301 fn_ref_destroy(ref);
302 done:
303 fn_ctx_handle_destroy(init_ctx);
304 fn_status_destroy(status);
308 static mapent *
309 process_ref(const FN_ref_t *ref, const char *cname, char *map, char *key,
310 char *opts, char *root, bool_t shallow, FN_status_t *status)
312 addrtype_t addrtype;
313 mapline ml;
314 char *addrdata = ml.linebuf;
315 mapent *mapents;
316 bool_t self;
317 char *homedir;
318 size_t maplen;
319 char *colon;
320 char *nfshost;
321 char *nfsdir;
323 if ((reftype(ref) < NUM_REFTYPES) &&
324 (addr_from_ref(ref, cname, &addrtype, addrdata, LINESZ) == 0)) {
326 switch (addrtype) {
327 case ADDR_MOUNT:
328 if (trim_line(&ml) != 0) {
329 return (NULL);
331 if (opts_only(&ml)) {
332 /* parse_entry() can't handle such lines */
333 if (macro_expand("&", ml.linebuf,
334 ml.lineqbuf, LINESZ)) {
335 syslog(LOG_ERR,
336 "%s/%s: opts too long (max %d chars)",
337 FNPREFIX, cname, LINESZ - 1);
338 return (NULL);
340 opts = ml.linebuf + 1; /* skip '-' */
341 goto indirect;
343 mapents = parse_entry(key, map, opts, &ml, NULL, 0,
344 TRUE);
345 if (mapents == NULL || !safe_mapent(mapents)) {
346 free_mapent(mapents);
347 return (NULL);
349 free(mapents->map_root);
350 mapents->map_root = root;
351 break;
353 case ADDR_HOST:
355 * Address is of the form "host:dir".
356 * If "dir" is not supplied, it defaults to "/".
358 colon = strchr(addrdata, ':');
359 if (colon == NULL || colon[1] == '\0') {
360 nfsdir = strdup("/");
361 } else {
362 *colon = '\0';
363 nfsdir = strdup(colon + 1);
365 nfshost = strdup(addrdata);
367 * If nfshost is the local host, the NFS mount
368 * request will be converted to a loopback
369 * mount. Otherwise check that the file system
370 * is exported.
372 if (nfshost != NULL) {
373 self = self_check(nfshost);
374 if (!self && !exported(ref, cname, status)) {
375 if (transient(status)) {
376 return (NULL);
377 } else {
378 goto indirect;
382 mapents = new_mapent(root, strdup(""), strdup("nfs"),
383 safe_opts(opts), nfshost, nfsdir);
384 if (self && !shallow) {
385 return (mapents);
387 break;
389 case ADDR_USER:
390 homedir = strdup(addrdata);
391 homedir[strcspn(homedir, " \t\r\n")] = '\0';
392 mapents = new_mapent(root, strdup(""), strdup("lofs"),
393 strdup(opts), strdup(""), homedir);
394 break;
397 if (mapents == NULL) {
398 return (NULL);
400 if (shallow) {
401 mapents->map_root = NULL; /* don't free "root" */
402 free_mapent(mapents);
403 goto indirect;
406 /* "map" => "map/key" */
407 if ((maplen = append_mapname(map, strlen(map), key)) == 0) {
408 return (mapents);
410 return (frontier(mapents, ref, map, maplen, map + maplen,
411 opts, status));
414 /* Ref type wasn't recognized. */
416 indirect:
417 /* Install an indirect autofs mount point. */
418 return (new_mapent(root, strdup(""), strdup("autofs"), strdup(opts),
419 strdup(""), concat(map, '/', key)));
424 * All that this function really does is call frontier_aux() on every
425 * name bound under ref. The rest is error checking(!)
427 * The error handling strategy is to reject the entire mount request
428 * (by freeing mapents) if any (potentially) transient error occurs,
429 * and to treat nontransient errors as holes in the affected portions
430 * of the namespace.
432 static mapent *
433 frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen,
434 char *mntpnt, char *opts, FN_status_t *status)
436 FN_ctx_t *ctx;
437 FN_bindinglist_t *bindings = NULL;
438 FN_ref_t *child_ref;
439 FN_string_t *child_s;
440 const char *child;
441 unsigned int statcode;
443 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status);
444 if (ctx == NULL) {
445 if (fn_status_code(status) != FN_E_NO_SUPPORTED_ADDRESS) {
446 logstat(status, "from_ref failed for", map);
448 goto checkerr_return;
451 bindings = fn_ctx_list_bindings(ctx, empty_cname, status);
452 fn_ctx_handle_destroy(ctx);
453 if (bindings == NULL) {
454 logstat(status, "list_bindings failed for", map);
455 goto checkerr_return;
458 while ((child_s = fn_bindinglist_next(bindings, &child_ref, status))
459 != NULL) {
460 child = (const char *)fn_string_str(child_s, &statcode);
461 if (child == NULL) {
462 if (verbose) {
463 syslog(LOG_ERR,
464 "FNS string error listing %s", map);
466 fn_string_destroy(child_s);
467 goto err_return;
469 mapents = frontier_aux(mapents, child_ref, map, maplen,
470 mntpnt, child, opts, status);
471 fn_string_destroy(child_s);
472 fn_ref_destroy(child_ref);
473 if (mapents == NULL) {
474 goto noerr_return;
477 if (fn_status_is_success(status)) {
478 goto noerr_return;
479 } else {
480 logstat(status, "error while listing", map);
481 /* Fall through to checkerr_return. */
484 checkerr_return:
485 if (!transient(status)) {
486 goto noerr_return;
488 err_return:
489 free_mapent(mapents);
490 mapents = NULL;
491 noerr_return:
492 fn_bindinglist_destroy(bindings XFN1(status));
493 return (mapents);
497 static mapent *
498 frontier_aux(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen,
499 char *mntpnt, const char *name, char *opts, FN_status_t *status)
501 addrtype_t addrtype;
502 bool_t at_frontier;
503 mapent *me;
504 size_t maplen_save = maplen;
505 char *cname = map + FNPREFIXLEN + 1; /* for error msgs */
507 if (reftype(ref) >= NUM_REFTYPES) {
509 * We could instead install an indirect autofs mount point
510 * here. That would allow, for example, a user to be bound
511 * beneath a file system.
513 return (mapents);
516 /* "map" => "map/name" */
517 if ((maplen = append_mapname(map, maplen, name)) == 0) {
518 return (mapents);
520 if (trace > 1) {
521 trace_prt(1, " FNS traversal: %s/\n", cname);
525 * If this is an address type that we know how to mount, then
526 * we have reached the frontier.
528 at_frontier = (addr_from_ref(ref, cname, &addrtype, NULL, 0) == 0);
530 * For an ADDR_HOST address, treat a non-exported directory as
531 * if the address type were not known: continue searching for
532 * exported subdirectories.
534 if (at_frontier && (addrtype == ADDR_HOST)) {
535 if (!exported(ref, cname, status)) {
536 if (transient(status)) {
537 free_mapent(mapents);
538 return (NULL);
539 } else {
540 at_frontier = FALSE;
545 * If we have reached the frontier, install a direct autofs
546 * mount point (which will trigger the actual mount if the
547 * user steps on it later). Otherwise, continue traversing
548 * the namespace looking for known address types.
550 if (at_frontier) {
551 opts = (opts[0] != '\0')
552 ? concat(opts, ',', "direct")
553 : strdup("direct");
554 me = new_mapent(noroot, strdup(mntpnt), strdup("autofs"), opts,
555 strdup(""), strdup(map));
556 if (me != NULL) {
557 /* Link new mapent into list (not at the head). */
558 me->map_next = mapents->map_next;
559 mapents->map_next = me;
560 } else {
561 free_mapent(mapents);
562 mapents = NULL;
564 } else {
565 mapents =
566 frontier(mapents, ref, map, maplen, mntpnt, opts, status);
568 map[maplen_save] = '\0'; /* "map/name" => "map" */
569 return (mapents);
573 static bool_t
574 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status)
576 FN_ctx_t *ctx;
577 FN_attribute_t *attr;
579 ctx = fn_ctx_handle_from_ref(ref, XFN2(0) status);
580 if (ctx == NULL) {
581 logstat(status, "from_ref failed for", cname);
582 return (FALSE);
584 attr = fn_attr_get(ctx, empty_cname, &attr_exported, XFN2(1) status);
585 fn_ctx_handle_destroy(ctx);
587 switch (fn_status_code(status)) {
588 case FN_SUCCESS:
589 fn_attribute_destroy(attr);
590 break;
591 case FN_E_NO_SUCH_ATTRIBUTE:
592 break;
593 default:
594 logstat(status, "could not get attributes for", cname);
596 return (attr != NULL);
600 static int
601 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep,
602 char *data, size_t datasz)
604 const FN_ref_addr_t *addr;
605 void *iter_pos;
607 addr = fn_ref_first(ref, &iter_pos);
608 if (addr == NULL) {
609 if (verbose) {
610 syslog(LOG_ERR, "FNS ref with no address: %s", cname);
612 return (-1);
614 while (addr != NULL) {
615 *typep = addrtype(addr);
616 if (*typep < NUM_ADDRTYPES) {
617 return ((data != NULL)
618 ? str_from_addr(cname, addr, data, datasz)
619 : 0);
621 addr = fn_ref_next(ref, &iter_pos);
623 return (-1);
627 static int
628 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[],
629 size_t strsz)
631 XDR xdr;
632 int res;
634 xdrmem_create(&xdr, (caddr_t)fn_ref_addr_data(addr),
635 fn_ref_addr_length(addr), XDR_DECODE);
636 if (!xdr_string(&xdr, &str, strsz)) {
637 if (verbose) {
638 syslog(LOG_ERR,
639 "Could not decode FNS address for %s", cname);
641 res = -1;
642 } else {
643 res = 0;
645 xdr_destroy(&xdr);
646 return (res);
649 static size_t
650 append_mapname(char *map, size_t maplen, const char *name)
652 size_t namelen = strlen(name);
654 if (maplen + 1 + namelen >= MAPNAMESZ) {
655 if (verbose) {
656 syslog(LOG_ERR, "FNS name %s/%s too long",
657 map + FNPREFIXLEN + 1, name);
659 return (0);
661 sprintf(map + maplen, "/%s", name);
662 return (maplen + 1 + namelen);
666 static char *
667 concat(const char *s1, char sep, const char *s2)
669 char *s = malloc(strlen(s1) + 1 + strlen(s2) + 1);
671 if (s != NULL) {
672 sprintf(s, "%s%c%s", s1, sep, s2);
674 return (s);
678 static bool_t
679 safe_mapent(mapent *me)
681 char *opts;
683 if (me->map_next != NULL) {
684 /* Multiple mounts don't belong in XFN namespace. */
685 return (NULL);
687 opts = me->map_mntopts;
688 me->map_mntopts = safe_opts(opts);
689 free(opts);
690 return (me->map_mntopts != NULL);
694 static char *
695 safe_opts(const char *opts)
697 char *start;
698 size_t len;
700 if (opts[0] == '\0') {
701 return (strdup(MNTOPT_NOSUID));
704 /* A quick-and-dirty check to see if "nosuid" is already there. */
705 start = strstr(opts, MNTOPT_NOSUID);
706 len = sizeof (MNTOPT_NOSUID) - 1; /* "-1" for trailing '\0' */
707 if (start != NULL) {
708 while (start > opts && isspace(*(start - 1))) {
709 start--;
711 if ((start == opts || *(start - 1) == ',') &&
712 opts[len] == ',' || opts[len] == '\0') {
713 return (strdup(opts));
716 return (concat(opts, ',', MNTOPT_NOSUID));
720 static int
721 trim_line(mapline *ml)
723 char *end; /* pointer to '\0' at end of linebuf */
725 end = ml->linebuf + strcspn(ml->linebuf, "#");
726 while ((end > ml->linebuf) && isspace(end[-1])) {
727 end--;
729 if (end <= ml->linebuf) {
730 return (-1);
732 *end = '\0';
733 unquote(ml->linebuf, ml->lineqbuf);
734 return (0);
738 static bool_t
739 opts_only(const mapline *ml)
741 const char *s = ml->linebuf;
742 const char *q = ml->lineqbuf;
744 if (*s != '-') {
745 return (FALSE);
747 for (; *s != '\0'; s++, q++) {
748 if (isspace(*s) && (*q == ' ')) {
749 return (FALSE);
752 return (TRUE);
756 static mapent *
757 new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host,
758 char *dir)
760 mapent *me;
761 struct mapfs *mfs;
762 char *mounter = NULL;
764 me = calloc(1, sizeof (*me));
765 mfs = calloc(1, sizeof (*mfs));
766 if (fstype != NULL) {
767 mounter = strdup(fstype);
769 if ((mntpnt == NULL) || (fstype == NULL) || (mntopts == NULL) ||
770 (host == NULL) || (dir == NULL) || (me == NULL) || (mfs == NULL) ||
771 (mounter == NULL) || (root == NULL)) {
772 log_mem_failure();
773 free(me);
774 free(mfs);
775 free(mounter);
776 free(root);
777 free(mntpnt);
778 free(fstype);
779 free(mntopts);
780 free(host);
781 free(dir);
782 return (NULL);
784 me->map_root = (root != noroot) ? root : NULL;
785 me->map_fstype = fstype;
786 me->map_mounter = mounter;
787 me->map_mntpnt = mntpnt;
788 me->map_mntopts = mntopts;
789 me->map_fsw = NULL;
790 me->map_fswq = NULL;
791 me->map_fs = mfs;
792 mfs->mfs_host = host;
793 mfs->mfs_dir = dir;
794 me->map_mntlevel = -1;
795 me->map_modified = FALSE;
796 me->map_faked = FALSE;
797 me->map_err = 0; /* MAPENT_NOERR */
798 return (me);
802 #ifndef XFN1ENV
805 * User-relative bindings in the initial context, and the leading components
806 * of their non-user-relative equivalents. Leading components are listed in
807 * the order in which they should be tried. Each list is NULL-terminated
808 * (the compiler generously does this for us).
809 * For "myorgunit", for example, we first check if it is equivalent to
810 * "thisorgunit". If not, we translate it into "org/<something>".
812 #define MAX_LEADS 3
814 static struct {
815 const char *binding;
816 const char *leads[MAX_LEADS + 1];
817 } user_rel[] = {
818 {"thisuser", {"user", "thisorgunit", "org"}},
819 {"myself", {"user", "thisorgunit", "org"}},
820 {"_myself", {"_user", "_thisorgunit", "_orgunit"}},
821 {"myorgunit", {"thisorgunit", "org"}},
822 {"_myorgunit", {"_thisorgunit", "_orgunit"}},
823 {"myens", {"thisens"}},
824 {"_myens", {"_thisens"}}
828 static bool_t
829 is_user_relative(const char *cname)
831 int i;
833 for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) {
834 if (strcmp(cname, user_rel[i].binding) == 0) {
835 return (TRUE);
838 return (FALSE);
842 static char *
843 equiv_name(FN_ctx_t *ctx, const char *cname, FN_status_t *status)
845 FN_composite_name_t *name;
846 FN_string_t *leading_name;
847 FN_composite_name_t *equiv;
848 FN_string_t *equiv_string;
849 const char *equiv_str;
850 char *equiv_str_dup;
851 const char **leads;
852 unsigned int stat;
853 int i;
855 for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) {
856 if (strcmp(cname, user_rel[i].binding) == 0) {
857 break;
860 if ((name = new_cname(cname)) == NULL) {
861 return (NULL);
863 leads = user_rel[i].leads; /* array of leading names to try */
864 do {
865 leading_name = fn_string_from_str((unsigned char *)*leads);
866 if (leading_name == NULL) {
867 log_mem_failure();
868 fn_composite_name_destroy(name);
869 return (NULL);
871 equiv = prelim_fn_ctx_equivalent_name(ctx, name, leading_name,
872 status);
873 fn_string_destroy(leading_name);
874 } while (equiv == NULL && *++leads != NULL);
876 fn_composite_name_destroy(name);
878 if (equiv == NULL) {
879 if (transient(status)) {
880 logstat(status, "could not find equivalent of", cname);
882 return (NULL);
884 equiv_string = fn_string_from_composite_name(equiv, &stat);
885 fn_composite_name_destroy(equiv);
886 if (equiv_string == NULL) {
887 log_mem_failure();
888 return (NULL);
890 equiv_str = (const char *)fn_string_str(equiv_string, &stat);
891 if (equiv_str == NULL ||
892 (equiv_str_dup = strdup(equiv_str)) == NULL) {
893 log_mem_failure();
894 fn_string_destroy(equiv_string);
895 return (NULL);
897 fn_string_destroy(equiv_string);
898 return (equiv_str_dup);
901 #endif /* XFN1ENV */