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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * ns_fnmount.c
23 *
24 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
26 */
27
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"
38
39
40 /*
41 * The maximum sizes of map names, key names, composite names, and status
42 * descriptions, including the trailing '\0'.
43 */
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
48
49 typedef struct mapent mapent;
50 typedef struct mapline mapline;
51
52
53 /*
54 * The name of an attribute.
55 */
56 static const FN_identifier_t attr_exported = {FN_ID_STRING, 8, "exported"};
57
58
59 /*
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.
66 *
67 * void
68 * getmapent_fn(char *key, char *map, char *opts, uid_t uid,
69 * bool_t shallow, getmapent_fn_res *res);
70 */
71
72 /*
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".
79 */
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);
83
84 /*
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.
96 */
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);
100
101 /*
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().
108 */
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);
112
113 /*
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.
117 */
118 static bool_t
119 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status);
120
121 /*
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.
126 */
127 static int
128 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep,
129 char *data, size_t datasz);
130
131 /*
132 * Decode an address's data into a string. Return 0 on success.
133 */
134 static int
135 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[],
136 size_t strsz);
137
138 /*
139 * Given a map name and its current length, append "/name". Return
140 * the new length. On error, syslog a warning and return 0.
141 */
142 static size_t
143 append_mapname(char *map, size_t maplen, const char *name);
144
145 /*
146 * Concatenate two strings using the given separator. The result is a
147 * newly-allocated string, or NULL on error.
148 */
149 static char *
150 concat(const char *s1, char sep, const char *s2);
151
152 /*
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.
156 */
157 static bool_t
158 safe_mapent(mapent *me);
159
160 /*
161 * Append "nosuid" to a list of options. The result is a
162 * newly-allocated string, or NULL on error.
163 */
164 static char *
165 safe_opts(const char *opts);
166
167 /*
168 * Trim comments and trailing whitespace from ml->linebuf, then
169 * unquote it and leave the result in ml. Return 0 on success.
170 */
171 static int
172 trim_line(mapline *ml);
173
174 /*
175 * Determine whether ml contains an option string (such as "-ro") and
176 * nothing else.
177 */
178 static bool_t
179 opts_only(const mapline *ml);
180
181 /*
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).
188 */
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);
193
194 /*
195 * Determine whether cname is a user-relative binding -- such as "myself" --
196 * in the initial context.
197 */
198 static bool_t
199 is_user_relative(const char *cname);
200
201 /*
202 * Given the name of a user-relative binding, return an equivalent
203 * name that is not user-relative.
204 */
205 static char *
206 equiv_name(FN_ctx_t *, const char *cname, FN_status_t *);
207
208 void
getmapent_fn(char * key,char * map,char * opts,uid_t uid,bool_t shallow,getmapent_fn_res * res)209 getmapent_fn(char *key, char *map, char *opts, uid_t uid, bool_t shallow,
210 getmapent_fn_res *res)
211 {
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;
221
222 res->type = FN_NONE;
223 res->m_or_l.mapents = NULL;
224
225 if (init_fn() != 0) {
226 return;
227 }
228
229 /*
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.
234 */
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);
242 }
243 map = mapname;
244 maplen = strlen(map);
245
246 if ((maplen - FNPREFIXLEN + strlen(key)) >= COMPNAMESZ) {
247 if (verbose) {
248 syslog(LOG_ERR, "name %s/%s too long", map, key);
249 }
250 return;
251 }
252 if (maplen == FNPREFIXLEN) {
253 strcpy(cname, key);
254 } else {
255 sprintf(cname, "%s/%s", map + FNPREFIXLEN + 1, key);
256 }
257
258 status = fn_status_create();
259 if (status == NULL) {
260 if (verbose) {
261 syslog(LOG_ERR, "Could not create FNS status object");
262 }
263 return;
264 }
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;
269 }
270
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;
276 }
277 #endif
278
279 if ((compname = new_cname(cname)) == NULL) {
280 goto done;
281 }
282 ref = fn_ctx_lookup(init_ctx, compname, status);
283 statcode = fn_status_code(status);
284 fn_composite_name_destroy(compname);
285
286 if (trace > 1 && !shallow) {
287 trace_prt(1, " FNS traversal: %s\n", cname);
288 }
289
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);
294 }
295 goto done;
296 }
297
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);
305 }
306
307
308 static mapent *
process_ref(const FN_ref_t * ref,const char * cname,char * map,char * key,char * opts,char * root,bool_t shallow,FN_status_t * status)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)
311 {
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;
322
323 if ((reftype(ref) < NUM_REFTYPES) &&
324 (addr_from_ref(ref, cname, &addrtype, addrdata, LINESZ) == 0)) {
325
326 switch (addrtype) {
327 case ADDR_MOUNT:
328 if (trim_line(&ml) != 0) {
329 return (NULL);
330 }
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);
339 }
340 opts = ml.linebuf + 1; /* skip '-' */
341 goto indirect;
342 }
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);
348 }
349 free(mapents->map_root);
350 mapents->map_root = root;
351 break;
352
353 case ADDR_HOST:
354 /*
355 * Address is of the form "host:dir".
356 * If "dir" is not supplied, it defaults to "/".
357 */
358 colon = strchr(addrdata, ':');
359 if (colon == NULL || colon[1] == '\0') {
360 nfsdir = strdup("/");
361 } else {
362 *colon = '\0';
363 nfsdir = strdup(colon + 1);
364 }
365 nfshost = strdup(addrdata);
366 /*
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.
371 */
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;
379 }
380 }
381 }
382 mapents = new_mapent(root, strdup(""), strdup("nfs"),
383 safe_opts(opts), nfshost, nfsdir);
384 if (self && !shallow) {
385 return (mapents);
386 }
387 break;
388
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;
395 }
396
397 if (mapents == NULL) {
398 return (NULL);
399 }
400 if (shallow) {
401 mapents->map_root = NULL; /* don't free "root" */
402 free_mapent(mapents);
403 goto indirect;
404 }
405
406 /* "map" => "map/key" */
407 if ((maplen = append_mapname(map, strlen(map), key)) == 0) {
408 return (mapents);
409 }
410 return (frontier(mapents, ref, map, maplen, map + maplen,
411 opts, status));
412 }
413
414 /* Ref type wasn't recognized. */
415
416 indirect:
417 /* Install an indirect autofs mount point. */
418 return (new_mapent(root, strdup(""), strdup("autofs"), strdup(opts),
419 strdup(""), concat(map, '/', key)));
420 }
421
422
423 /*
424 * All that this function really does is call frontier_aux() on every
425 * name bound under ref. The rest is error checking(!)
426 *
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.
431 */
432 static mapent *
frontier(mapent * mapents,const FN_ref_t * ref,char * map,size_t maplen,char * mntpnt,char * opts,FN_status_t * status)433 frontier(mapent *mapents, const FN_ref_t *ref, char *map, size_t maplen,
434 char *mntpnt, char *opts, FN_status_t *status)
435 {
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;
442
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);
447 }
448 goto checkerr_return;
449 }
450
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;
456 }
457
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);
465 }
466 fn_string_destroy(child_s);
467 goto err_return;
468 }
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;
475 }
476 }
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. */
482 }
483
484 checkerr_return:
485 if (!transient(status)) {
486 goto noerr_return;
487 }
488 err_return:
489 free_mapent(mapents);
490 mapents = NULL;
491 noerr_return:
492 fn_bindinglist_destroy(bindings XFN1(status));
493 return (mapents);
494 }
495
496
497 static mapent *
frontier_aux(mapent * mapents,const FN_ref_t * ref,char * map,size_t maplen,char * mntpnt,const char * name,char * opts,FN_status_t * status)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)
500 {
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 */
506
507 if (reftype(ref) >= NUM_REFTYPES) {
508 /*
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.
512 */
513 return (mapents);
514 }
515
516 /* "map" => "map/name" */
517 if ((maplen = append_mapname(map, maplen, name)) == 0) {
518 return (mapents);
519 }
520 if (trace > 1) {
521 trace_prt(1, " FNS traversal: %s/\n", cname);
522 }
523
524 /*
525 * If this is an address type that we know how to mount, then
526 * we have reached the frontier.
527 */
528 at_frontier = (addr_from_ref(ref, cname, &addrtype, NULL, 0) == 0);
529 /*
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.
533 */
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;
541 }
542 }
543 }
544 /*
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.
549 */
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;
563 }
564 } else {
565 mapents =
566 frontier(mapents, ref, map, maplen, mntpnt, opts, status);
567 }
568 map[maplen_save] = '\0'; /* "map/name" => "map" */
569 return (mapents);
570 }
571
572
573 static bool_t
exported(const FN_ref_t * ref,const char * cname,FN_status_t * status)574 exported(const FN_ref_t *ref, const char *cname, FN_status_t *status)
575 {
576 FN_ctx_t *ctx;
577 FN_attribute_t *attr;
578
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);
583 }
584 attr = fn_attr_get(ctx, empty_cname, &attr_exported, XFN2(1) status);
585 fn_ctx_handle_destroy(ctx);
586
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);
595 }
596 return (attr != NULL);
597 }
598
599
600 static int
addr_from_ref(const FN_ref_t * ref,const char * cname,addrtype_t * typep,char * data,size_t datasz)601 addr_from_ref(const FN_ref_t *ref, const char *cname, addrtype_t *typep,
602 char *data, size_t datasz)
603 {
604 const FN_ref_addr_t *addr;
605 void *iter_pos;
606
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);
611 }
612 return (-1);
613 }
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);
620 }
621 addr = fn_ref_next(ref, &iter_pos);
622 }
623 return (-1);
624 }
625
626
627 static int
str_from_addr(const char * cname,const FN_ref_addr_t * addr,char str[],size_t strsz)628 str_from_addr(const char *cname, const FN_ref_addr_t *addr, char str[],
629 size_t strsz)
630 {
631 XDR xdr;
632 int res;
633
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);
640 }
641 res = -1;
642 } else {
643 res = 0;
644 }
645 xdr_destroy(&xdr);
646 return (res);
647 }
648
649 static size_t
append_mapname(char * map,size_t maplen,const char * name)650 append_mapname(char *map, size_t maplen, const char *name)
651 {
652 size_t namelen = strlen(name);
653
654 if (maplen + 1 + namelen >= MAPNAMESZ) {
655 if (verbose) {
656 syslog(LOG_ERR, "FNS name %s/%s too long",
657 map + FNPREFIXLEN + 1, name);
658 }
659 return (0);
660 }
661 sprintf(map + maplen, "/%s", name);
662 return (maplen + 1 + namelen);
663 }
664
665
666 static char *
concat(const char * s1,char sep,const char * s2)667 concat(const char *s1, char sep, const char *s2)
668 {
669 char *s = malloc(strlen(s1) + 1 + strlen(s2) + 1);
670
671 if (s != NULL) {
672 sprintf(s, "%s%c%s", s1, sep, s2);
673 }
674 return (s);
675 }
676
677
678 static bool_t
safe_mapent(mapent * me)679 safe_mapent(mapent *me)
680 {
681 char *opts;
682
683 if (me->map_next != NULL) {
684 /* Multiple mounts don't belong in XFN namespace. */
685 return (NULL);
686 }
687 opts = me->map_mntopts;
688 me->map_mntopts = safe_opts(opts);
689 free(opts);
690 return (me->map_mntopts != NULL);
691 }
692
693
694 static char *
safe_opts(const char * opts)695 safe_opts(const char *opts)
696 {
697 char *start;
698 size_t len;
699
700 if (opts[0] == '\0') {
701 return (strdup(MNTOPT_NOSUID));
702 }
703
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--;
710 }
711 if ((start == opts || *(start - 1) == ',') &&
712 opts[len] == ',' || opts[len] == '\0') {
713 return (strdup(opts));
714 }
715 }
716 return (concat(opts, ',', MNTOPT_NOSUID));
717 }
718
719
720 static int
trim_line(mapline * ml)721 trim_line(mapline *ml)
722 {
723 char *end; /* pointer to '\0' at end of linebuf */
724
725 end = ml->linebuf + strcspn(ml->linebuf, "#");
726 while ((end > ml->linebuf) && isspace(end[-1])) {
727 end--;
728 }
729 if (end <= ml->linebuf) {
730 return (-1);
731 }
732 *end = '\0';
733 unquote(ml->linebuf, ml->lineqbuf);
734 return (0);
735 }
736
737
738 static bool_t
opts_only(const mapline * ml)739 opts_only(const mapline *ml)
740 {
741 const char *s = ml->linebuf;
742 const char *q = ml->lineqbuf;
743
744 if (*s != '-') {
745 return (FALSE);
746 }
747 for (; *s != '\0'; s++, q++) {
748 if (isspace(*s) && (*q == ' ')) {
749 return (FALSE);
750 }
751 }
752 return (TRUE);
753 }
754
755
756 static mapent *
new_mapent(char * root,char * mntpnt,char * fstype,char * mntopts,char * host,char * dir)757 new_mapent(char *root, char *mntpnt, char *fstype, char *mntopts, char *host,
758 char *dir)
759 {
760 mapent *me;
761 struct mapfs *mfs;
762 char *mounter = NULL;
763
764 me = calloc(1, sizeof (*me));
765 mfs = calloc(1, sizeof (*mfs));
766 if (fstype != NULL) {
767 mounter = strdup(fstype);
768 }
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);
783 }
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);
799 }
800
801
802 #ifndef XFN1ENV
803
804 /*
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>".
811 */
812 #define MAX_LEADS 3
813
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"}}
825 };
826
827
828 static bool_t
is_user_relative(const char * cname)829 is_user_relative(const char *cname)
830 {
831 int i;
832
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);
836 }
837 }
838 return (FALSE);
839 }
840
841
842 static char *
equiv_name(FN_ctx_t * ctx,const char * cname,FN_status_t * status)843 equiv_name(FN_ctx_t *ctx, const char *cname, FN_status_t *status)
844 {
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;
854
855 for (i = 0; i < sizeof (user_rel) / sizeof (user_rel[0]); i++) {
856 if (strcmp(cname, user_rel[i].binding) == 0) {
857 break;
858 }
859 }
860 if ((name = new_cname(cname)) == NULL) {
861 return (NULL);
862 }
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);
870 }
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);
875
876 fn_composite_name_destroy(name);
877
878 if (equiv == NULL) {
879 if (transient(status)) {
880 logstat(status, "could not find equivalent of", cname);
881 }
882 return (NULL);
883 }
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);
889 }
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);
896 }
897 fn_string_destroy(equiv_string);
898 return (equiv_str_dup);
899 }
900
901 #endif /* XFN1ENV */
902