Bug Summary

File:src/usr.sbin/amd/amd/opts.c
Warning:line 170, column 9
Dereference of null pointer (loaded from variable 'cp')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name opts.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/amd/amd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/amd/amd/../rpcx -I /usr/src/usr.sbin/amd/amd/../include -D ARCH_REP="amd64" -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/amd/amd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/amd/amd/opts.c
1/*-
2 * Copyright (c) 1989 Jan-Simon Pendry
3 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry at Imperial College, London.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "am.h"
36
37/*
38 * static copy of the options with
39 * which to play
40 */
41static struct am_opts fs_static;
42
43static char *opt_host = hostname;
44static char *opt_hostd = hostd;
45static char nullstr[] = "";
46static char *opt_key = nullstr;
47static char *opt_map = nullstr;
48static char *opt_path = nullstr;
49
50static char *vars[8];
51
52/*
53 * Length of longest option name
54 */
55#define NLEN16 16 /* conservative */
56#define S(x)(x) , (sizeof(x)-1) (x) , (sizeof(x)-1)
57static struct opt {
58 char *name; /* Name of the option */
59 int nlen; /* Length of option name */
60 char **optp; /* Pointer to option value string */
61 char **sel_p; /* Pointer to selector value string */
62} opt_fields[] = {
63 /* Options in something corresponding to frequency of use */
64 { S("opts")("opts") , (sizeof("opts")-1), &fs_static.opt_opts, 0 },
65 { S("host")("host") , (sizeof("host")-1), 0, &opt_host },
66 { S("hostd")("hostd") , (sizeof("hostd")-1), 0, &opt_hostd },
67 { S("type")("type") , (sizeof("type")-1), &fs_static.opt_type, 0 },
68 { S("rhost")("rhost") , (sizeof("rhost")-1), &fs_static.opt_rhost, 0 },
69 { S("rfs")("rfs") , (sizeof("rfs")-1), &fs_static.opt_rfs, 0 },
70 { S("fs")("fs") , (sizeof("fs")-1), &fs_static.opt_fs, 0 },
71 { S("key")("key") , (sizeof("key")-1), 0, &opt_key },
72 { S("map")("map") , (sizeof("map")-1), 0, &opt_map },
73 { S("sublink")("sublink") , (sizeof("sublink")-1), &fs_static.opt_sublink, 0 },
74 { S("arch")("arch") , (sizeof("arch")-1), 0, &arch },
75 { S("dev")("dev") , (sizeof("dev")-1), &fs_static.opt_dev, 0 },
76 { S("pref")("pref") , (sizeof("pref")-1), &fs_static.opt_pref, 0 },
77 { S("path")("path") , (sizeof("path")-1), 0, &opt_path },
78 { S("autodir")("autodir") , (sizeof("autodir")-1), 0, &auto_dir },
79 { S("delay")("delay") , (sizeof("delay")-1), &fs_static.opt_delay, 0 },
80 { S("domain")("domain") , (sizeof("domain")-1), 0, &hostdomain },
81 { S("karch")("karch") , (sizeof("karch")-1), 0, &karch },
82 { S("cluster")("cluster") , (sizeof("cluster")-1), 0, &cluster },
83 { S("wire")("wire") , (sizeof("wire")-1), 0, &wire },
84 { S("byte")("byte") , (sizeof("byte")-1), 0, &endian },
85 { S("os")("os") , (sizeof("os")-1), 0, &op_sys },
86 { S("remopts")("remopts") , (sizeof("remopts")-1), &fs_static.opt_remopts, 0 },
87 { S("mount")("mount") , (sizeof("mount")-1), &fs_static.opt_mount, 0 },
88 { S("unmount")("unmount") , (sizeof("unmount")-1), &fs_static.opt_unmount, 0 },
89 { S("cache")("cache") , (sizeof("cache")-1), &fs_static.opt_cache, 0 },
90 { S("user")("user") , (sizeof("user")-1), &fs_static.opt_user, 0 },
91 { S("group")("group") , (sizeof("group")-1), &fs_static.opt_group, 0 },
92 { S("var0")("var0") , (sizeof("var0")-1), &vars[0], 0 },
93 { S("var1")("var1") , (sizeof("var1")-1), &vars[1], 0 },
94 { S("var2")("var2") , (sizeof("var2")-1), &vars[2], 0 },
95 { S("var3")("var3") , (sizeof("var3")-1), &vars[3], 0 },
96 { S("var4")("var4") , (sizeof("var4")-1), &vars[4], 0 },
97 { S("var5")("var5") , (sizeof("var5")-1), &vars[5], 0 },
98 { S("var6")("var6") , (sizeof("var6")-1), &vars[6], 0 },
99 { S("var7")("var7") , (sizeof("var7")-1), &vars[7], 0 },
100 { 0, 0, 0, 0 },
101};
102
103typedef struct opt_apply opt_apply;
104struct opt_apply {
105 char **opt;
106 char *val;
107};
108
109/*
110 * Specially expand the remote host name first
111 */
112static opt_apply rhost_expansion[] = {
113 { &fs_static.opt_rhost, "${host}" },
114 { 0, 0 },
115};
116/*
117 * List of options which need to be expanded
118 * Note that this the order here _may_ be important.
119 */
120static opt_apply expansions[] = {
121/* { &fs_static.opt_dir, 0 }, */
122 { &fs_static.opt_sublink, 0 },
123 { &fs_static.opt_rfs, "${path}" },
124 { &fs_static.opt_fs, "${autodir}/${rhost}${rfs}" },
125 { &fs_static.opt_opts, "rw" },
126 { &fs_static.opt_remopts, "${opts}" },
127 { &fs_static.opt_mount, 0 },
128 { &fs_static.opt_unmount, 0 },
129 { 0, 0 },
130};
131
132/*
133 * List of options which need to be free'ed before re-use
134 */
135static opt_apply to_free[] = {
136 { &fs_static.fs_glob, 0 },
137 { &fs_static.fs_local, 0 },
138 { &fs_static.fs_mtab, 0 },
139/* { &fs_static.opt_dir, 0 }, */
140 { &fs_static.opt_sublink, 0 },
141 { &fs_static.opt_rfs, 0 },
142 { &fs_static.opt_fs, 0 },
143 { &fs_static.opt_rhost, 0 },
144 { &fs_static.opt_opts, 0 },
145 { &fs_static.opt_remopts, 0 },
146 { &fs_static.opt_mount, 0 },
147 { &fs_static.opt_unmount, 0 },
148 { &vars[0], 0 },
149 { &vars[1], 0 },
150 { &vars[2], 0 },
151 { &vars[3], 0 },
152 { &vars[4], 0 },
153 { &vars[5], 0 },
154 { &vars[6], 0 },
155 { &vars[7], 0 },
156 { 0, 0 },
157};
158
159/*
160 * Skip to next option in the string
161 */
162static char *
163opt(char **p)
164{
165 char *cp = *p;
3
'cp' initialized to a null pointer value
166 char *dp = cp;
167 char *s = cp;
168
169top:
170 while (*cp && *cp != ';') {
4
Dereference of null pointer (loaded from variable 'cp')
171 if (*cp == '\"') {
172 /*
173 * Skip past string
174 */
175 cp++;
176 while (*cp && *cp != '\"')
177 *dp++ = *cp++;
178 if (*cp)
179 cp++;
180 } else {
181 *dp++ = *cp++;
182 }
183 }
184
185 /*
186 * Skip past any remaining ';'s
187 */
188 while (*cp == ';')
189 cp++;
190
191 /*
192 * If we have a zero length string
193 * and there are more fields, then
194 * parse the next one. This allows
195 * sequences of empty fields.
196 */
197 if (*cp && dp == s)
198 goto top;
199
200 *dp = '\0';
201
202 *p = cp;
203 return s;
204}
205
206static int
207eval_opts(char *opts, char *mapkey)
208{
209 /*
210 * Fill in the global structure fs_static by
211 * cracking the string opts. opts may be
212 * scribbled on at will.
213 */
214 char *o = opts;
215 char *f;
216
217 /*
218 * For each user-specified option
219 */
220 while (*(f = opt(&o))) {
2
Calling 'opt'
221 struct opt *op;
222 enum vs_opt { OldSyn, SelEQ, SelNE, VarAss } vs_opt;
223 char *eq = strchr(f, '=');
224 char *opt;
225 if (!eq || eq[1] == '\0' || eq == f) {
226 /*
227 * No value, just continue
228 */
229 plog(XLOG_USER0x0004, "key %s: No value component in \"%s\"", mapkey, f);
230 continue;
231 }
232
233 /*
234 * Check what type of operation is happening
235 * !=, =! is SelNE
236 * == is SelEQ
237 * := is VarAss
238 * = is OldSyn (either SelEQ or VarAss)
239 */
240 if (eq[-1] == '!') { /* != */
241 vs_opt = SelNE;
242 eq[-1] = '\0';
243 opt = eq + 1;
244 } else if (eq[-1] == ':') { /* := */
245 vs_opt = VarAss;
246 eq[-1] = '\0';
247 opt = eq + 1;
248 } else if (eq[1] == '=') { /* == */
249 vs_opt = SelEQ;
250 eq[0] = '\0';
251 opt = eq + 2;
252 } else if (eq[1] == '!') { /* =! */
253 vs_opt = SelNE;
254 eq[0] = '\0';
255 opt = eq + 2;
256 } else { /* = */
257 vs_opt = OldSyn;
258 eq[0] = '\0';
259 opt = eq + 1;
260 }
261
262 /*
263 * For each recognised option
264 */
265 for (op = opt_fields; op->name; op++) {
266 /*
267 * Check whether they match
268 */
269 if (FSTREQ(op->name, f)((*(op->name) == *(f)) && (strcmp(((op->name)),
((f))) == 0))
) {
270 switch (vs_opt) {
271#if 1 /* XXX ancient compat */
272 case OldSyn:
273 plog(XLOG_WARNING0x0008, "key %s: Old syntax selector found: %s=%s", mapkey, f, opt);
274 if (!op->sel_p) {
275 *op->optp = opt;
276 break;
277 }
278 /* fall through ... */
279#endif
280 case SelEQ:
281 case SelNE:
282 if (op->sel_p && (STREQ(*op->sel_p, opt)(strcmp((*op->sel_p), (opt)) == 0) == (vs_opt == SelNE))) {
283 plog(XLOG_MAP0x0040, "key %s: map selector %s (=%s) did not %smatch %s",
284 mapkey,
285 op->name,
286 *op->sel_p,
287 vs_opt == SelNE ? "not " : "",
288 opt);
289 return 0;
290 }
291 break;
292
293 case VarAss:
294 if (op->sel_p) {
295 plog(XLOG_USER0x0004, "key %s: Can't assign to a selector (%s)", mapkey, op->name);
296 return 0;
297 }
298 *op->optp = opt;
299 break;
300 }
301 break;
302 }
303 }
304
305 if (!op->name)
306 plog(XLOG_USER0x0004, "key %s: Unrecognised key/option \"%s\"", mapkey, f);
307 }
308
309 return 1;
310}
311
312/*
313 * Free an option
314 */
315static void
316free_op(opt_apply *p, int b)
317{
318 if (*p->opt) {
319 free(*p->opt);
320 *p->opt = 0;
321 }
322}
323
324/*
325 * Normalize slashes in the string.
326 */
327void
328normalize_slash(char *p)
329{
330 char *f = strchr(p, '/');
331 char *f0 = f;
332 if (f) {
333 char *t = f;
334 do {
335 /* assert(*f == '/'); */
336 if (f == f0 && f[0] == '/' && f[1] == '/') {
337 /* copy double slash iff first */
338 *t++ = *f++;
339 *t++ = *f++;
340 } else {
341 /* copy a single / across */
342 *t++ = *f++;
343 }
344
345 /* assert(f[-1] == '/'); */
346 /* skip past more /'s */
347 while (*f == '/')
348 f++;
349
350 /* assert(*f != '/'); */
351 /* keep copying up to next / */
352 while (*f && *f != '/') {
353 *t++ = *f++;
354 }
355
356 /* assert(*f == 0 || *f == '/'); */
357
358 } while (*f);
359 *t = 0; /* derived from fix by Steven Glassman */
360 }
361}
362
363/*
364 * Macro-expand an option. Note that this does not
365 * handle recursive expansions. They will go badly wrong.
366 * If sel is true then old expand selectors, otherwise
367 * don't expand selectors.
368 */
369static void
370expand_op(opt_apply *p, int sel_p)
371{
372/*
373 * The BUFSPACE macros checks that there is enough space
374 * left in the expansion buffer. If there isn't then we
375 * give up completely. This is done to avoid crashing the
376 * automounter itself (which would be a bad thing to do).
377 */
378#define BUFSPACE(ep, len)(((ep) + (len)) < expbuf+1024) (((ep) + (len)) < expbuf+PATH_MAX1024)
379static char expand_error[] = "No space to expand \"%s\"";
380
381 char expbuf[PATH_MAX1024+1];
382 char nbuf[NLEN16+1];
383 char *ep = expbuf;
384 char *cp = *p->opt;
385 char *dp;
386#ifdef DEBUG
387 char *cp_orig = *p->opt;
388#endif /* DEBUG */
389 struct opt *op;
390
391 while ((dp = strchr(cp, '$'))) {
392 char ch;
393 /*
394 * First copy up to the $
395 */
396 { int len = dp - cp;
397 if (BUFSPACE(ep, len)(((ep) + (len)) < expbuf+1024)) {
398 strncpy(ep, cp, len);
399 ep += len;
400 } else {
401 plog(XLOG_ERROR0x0002, expand_error, *p->opt);
402 goto out;
403 }
404 }
405 cp = dp + 1;
406 ch = *cp++;
407 if (ch == '$') {
408 if (BUFSPACE(ep, 1)(((ep) + (1)) < expbuf+1024)) {
409 *ep++ = '$';
410 } else {
411 plog(XLOG_ERROR0x0002, expand_error, *p->opt);
412 goto out;
413 }
414 } else if (ch == '{') {
415 /* Expansion... */
416 enum { E_All, E_Dir, E_File, E_Domain, E_Host } todo;
417 /*
418 * Find closing brace
419 */
420 char *br_p = strchr(cp, '}');
421 int len;
422 /*
423 * Check we found it
424 */
425 if (!br_p) {
426 /*
427 * Just give up
428 */
429 plog(XLOG_USER0x0004, "No closing '}' in \"%s\"", *p->opt);
430 goto out;
431 }
432 len = br_p - cp;
433 /*
434 * Figure out which part of the variable to grab.
435 */
436 if (*cp == '/') {
437 /*
438 * Just take the last component
439 */
440 todo = E_File;
441 cp++;
442 --len;
443 } else if (br_p[-1] == '/') {
444 /*
445 * Take all but the last component
446 */
447 todo = E_Dir;
448 --len;
449 } else if (*cp == '.') {
450 /*
451 * Take domain name
452 */
453 todo = E_Domain;
454 cp++;
455 --len;
456 } else if (br_p[-1] == '.') {
457 /*
458 * Take host name
459 */
460 todo = E_Host;
461 --len;
462 } else {
463 /*
464 * Take the whole lot
465 */
466 todo = E_All;
467 }
468 /*
469 * Truncate if too long. Since it won't
470 * match anyway it doesn't matter that
471 * it has been cut short.
472 */
473 if (len > NLEN16)
474 len = NLEN16;
475 /*
476 * Put the string into another buffer so
477 * we can do comparisons.
478 */
479 strncpy(nbuf, cp, len);
480 nbuf[len] = '\0';
481 /*
482 * Advance cp
483 */
484 cp = br_p + 1;
485 /*
486 * Search the option array
487 */
488 for (op = opt_fields; op->name; op++) {
489 /*
490 * Check for match
491 */
492 if (len == op->nlen && STREQ(op->name, nbuf)(strcmp((op->name), (nbuf)) == 0)) {
493 char xbuf[NLEN16+3];
494 char *val;
495 /*
496 * Found expansion. Copy
497 * the correct value field.
498 */
499 if (!(!op->sel_p == !sel_p)) {
500 /*
501 * Copy the string across unexpanded
502 */
503 snprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
504 todo == E_File ? "/" :
505 todo == E_Domain ? "." : "",
506 nbuf,
507 todo == E_Dir ? "/" :
508 todo == E_Host ? "." : "");
509 val = xbuf;
510 /*
511 * Make sure expansion doesn't
512 * munge the value!
513 */
514 todo = E_All;
515 } else if (op->sel_p) {
516 val = *op->sel_p;
517 } else {
518 val = *op->optp;
519 }
520 if (val) {
521 /*
522 * Do expansion:
523 * ${/var} means take just the last part
524 * ${var/} means take all but the last part
525 * ${.var} means take all but first part
526 * ${var.} means take just the first part
527 * ${var} means take the whole lot
528 */
529 int vlen = strlen(val);
530 char *vptr = val;
531 switch (todo) {
532 case E_Dir:
533 vptr = strrchr(val, '/');
534 if (vptr)
535 vlen = vptr - val;
536 vptr = val;
537 break;
538 case E_File:
539 vptr = strrchr(val, '/');
540 if (vptr) {
541 vptr++;
542 vlen = strlen(vptr);
543 } else
544 vptr = val;
545 break;
546 case E_Domain:
547 vptr = strchr(val, '.');
548 if (vptr) {
549 vptr++;
550 vlen = strlen(vptr);
551 } else {
552 vptr = "";
553 vlen = 0;
554 }
555 break;
556 case E_Host:
557 vptr = strchr(val, '.');
558 if (vptr)
559 vlen = vptr - val;
560 vptr = val;
561 break;
562 case E_All:
563 break;
564 }
565#ifdef DEBUG
566 /*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/
567#endif /* DEBUG */
568 if (BUFSPACE(ep, vlen)(((ep) + (vlen)) < expbuf+1024)) {
569 strlcpy(ep, vptr, expbuf + sizeof expbuf - ep);
570 ep += strlen(ep);
571 } else {
572 plog(XLOG_ERROR0x0002, expand_error, *p->opt);
573 goto out;
574 }
575 }
576 /*
577 * Done with this variable
578 */
579 break;
580 }
581 }
582 /*
583 * Check that the search was successful
584 */
585 if (!op->name) {
586 /*
587 * If it wasn't then scan the
588 * environment for that name
589 * and use any value found
590 */
591 char *env = getenv(nbuf);
592 if (env) {
593 int vlen = strlen(env);
594
595 if (BUFSPACE(ep, vlen)(((ep) + (vlen)) < expbuf+1024)) {
596 strlcpy(ep, env, expbuf + sizeof expbuf - ep);
597 ep += strlen(ep);
598 } else {
599 plog(XLOG_ERROR0x0002, expand_error, *p->opt);
600 goto out;
601 }
602#ifdef DEBUG
603 Debug(D_STR)
604 plog(XLOG_DEBUG0x0020, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
605#endif /* DEBUG */
606 } else {
607 plog(XLOG_USER0x0004, "Unknown sequence \"${%s}\"", nbuf);
608 }
609 }
610 } else {
611 /*
612 * Error, error
613 */
614 plog(XLOG_USER0x0004, "Unknown $ sequence in \"%s\"", *p->opt);
615 }
616 }
617
618out:
619 /*
620 * Handle common case - no expansion
621 */
622 if (cp == *p->opt) {
623 *p->opt = strdup(cp);
624 } else {
625 /*
626 * Finish off the expansion
627 */
628 if (BUFSPACE(ep, strlen(cp))(((ep) + (strlen(cp))) < expbuf+1024)) {
629 strlcpy(ep, cp, expbuf + sizeof expbuf - ep);
630 } else {
631 plog(XLOG_ERROR0x0002, expand_error, *p->opt);
632 }
633
634 /*
635 * Save the exansion
636 */
637 *p->opt = strdup(expbuf);
638 }
639
640 normalize_slash(*p->opt);
641
642#ifdef DEBUG
643 Debug(D_STR) {
644 plog(XLOG_DEBUG0x0020, "Expansion of \"%s\"...", cp_orig);
645 plog(XLOG_DEBUG0x0020, "... is \"%s\"", *p->opt);
646 }
647#endif /* DEBUG */
648}
649
650/*
651 * Wrapper for expand_op
652 */
653static void
654expand_opts(opt_apply *p, int sel_p)
655{
656 if (*p->opt) {
657 expand_op(p, sel_p);
658 } else if (p->val) {
659 /*
660 * Do double expansion, remembering
661 * to free the string from the first
662 * expansion...
663 */
664 char *s = *p->opt = expand_key(p->val);
665 expand_op(p, sel_p);
666 free(s);
667 }
668}
669
670/*
671 * Apply a function to a list of options
672 */
673static void
674apply_opts(void (*op)(), opt_apply ppp[], int b)
675{
676 opt_apply *pp;
677 for (pp = ppp; pp->opt; pp++)
678 (*op)(pp, b);
679}
680
681/*
682 * Free the option table
683 */
684void
685free_opts(am_opts *fo)
686{
687 /*
688 * Copy in the structure we are playing with
689 */
690 fs_static = *fo;
691
692 /*
693 * Free previously allocated memory
694 */
695 apply_opts(free_op, to_free, FALSE(0));
696}
697
698/*
699 * Expand lookup key
700 */
701char *
702expand_key(char *key)
703{
704 opt_apply oa;
705
706 oa.opt = &key; oa.val = 0;
707 expand_opts(&oa, TRUE(1));
708
709 return key;
710}
711
712/*
713 * Remove trailing /'s from a string
714 * unless the string is a single / (Steven Glassman)
715 */
716void
717deslashify(char *s)
718{
719 if (s && *s) {
720 char *sl = s + strlen(s);
721 while (*--sl == '/' && sl > s)
722 *sl = '\0';
723 }
724}
725
726int
727eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path,
728 char *key, char *map)
729{
730 int ok = TRUE(1);
731
732 free_opts(fo);
733
734 /*
735 * Clear out the option table
736 */
737 bzero(&fs_static, sizeof(fs_static));
738 bzero(vars, sizeof(vars));
739 bzero(fo, sizeof(*fo));
740
741 /*
742 * Set key, map & path before expansion
743 */
744 opt_key = key;
745 opt_map = map;
746 opt_path = path;
747
748 /*
749 * Expand global options
750 */
751 fs_static.fs_glob = expand_key(g_opts);
752
753 /*
754 * Expand local options
755 */
756 fs_static.fs_local = expand_key(opts);
757
758 /*
759 * Expand default (global) options
760 */
761 if (!eval_opts(fs_static.fs_glob, key))
1
Calling 'eval_opts'
762 ok = FALSE(0);
763
764 /*
765 * Expand local options
766 */
767 if (ok && !eval_opts(fs_static.fs_local, key))
768 ok = FALSE(0);
769
770 /*
771 * Normalise remote host name.
772 * 1. Expand variables
773 * 2. Normalize relative to host tables
774 * 3. Strip local domains from the remote host
775 * name before using it in other expansions.
776 * This makes mount point names and other things
777 * much shorter, while allowing cross domain
778 * sharing of mount maps.
779 */
780 apply_opts(expand_opts, rhost_expansion, FALSE(0));
781 if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
782 host_normalize(&fs_static.opt_rhost);
783
784 /*
785 * Macro expand the options.
786 * Do this regardless of whether we are accepting
787 * this mount - otherwise nasty things happen
788 * with memory allocation.
789 */
790 apply_opts(expand_opts, expansions, FALSE(0));
791
792 /*
793 * Strip trailing slashes from local pathname...
794 */
795 deslashify(fs_static.opt_fs);
796
797 /*
798 * ok... copy the data back out.
799 */
800 *fo = fs_static;
801
802 /*
803 * Clear defined options
804 */
805 opt_key = opt_map = opt_path = nullstr;
806
807 return ok;
808}