GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/amd/amd/opts.c Lines: 0 211 0.0 %
Date: 2017-11-07 Branches: 0 177 0.0 %

Line Branch Exec Source
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
 */
41
static struct am_opts fs_static;
42
43
static char *opt_host = hostname;
44
static char *opt_hostd = hostd;
45
static char nullstr[] = "";
46
static char *opt_key = nullstr;
47
static char *opt_map = nullstr;
48
static char *opt_path = nullstr;
49
50
static char *vars[8];
51
52
/*
53
 * Length of longest option name
54
 */
55
#define	NLEN	16	/* conservative */
56
#define S(x) (x) , (sizeof(x)-1)
57
static 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"), &fs_static.opt_opts, 0 },
65
	{ S("host"), 0, &opt_host },
66
	{ S("hostd"), 0, &opt_hostd },
67
	{ S("type"), &fs_static.opt_type, 0 },
68
	{ S("rhost"), &fs_static.opt_rhost, 0 },
69
	{ S("rfs"), &fs_static.opt_rfs, 0 },
70
	{ S("fs"), &fs_static.opt_fs, 0 },
71
	{ S("key"), 0, &opt_key },
72
	{ S("map"), 0, &opt_map },
73
	{ S("sublink"), &fs_static.opt_sublink, 0 },
74
	{ S("arch"), 0, &arch },
75
	{ S("dev"), &fs_static.opt_dev, 0 },
76
	{ S("pref"), &fs_static.opt_pref, 0 },
77
	{ S("path"), 0, &opt_path },
78
	{ S("autodir"), 0, &auto_dir },
79
	{ S("delay"), &fs_static.opt_delay, 0 },
80
	{ S("domain"), 0, &hostdomain },
81
	{ S("karch"), 0, &karch },
82
	{ S("cluster"), 0, &cluster },
83
	{ S("wire"), 0, &wire },
84
	{ S("byte"), 0, &endian },
85
	{ S("os"), 0, &op_sys },
86
	{ S("remopts"), &fs_static.opt_remopts, 0 },
87
	{ S("mount"), &fs_static.opt_mount, 0 },
88
	{ S("unmount"), &fs_static.opt_unmount, 0 },
89
	{ S("cache"), &fs_static.opt_cache, 0 },
90
	{ S("user"), &fs_static.opt_user, 0 },
91
	{ S("group"), &fs_static.opt_group, 0 },
92
	{ S("var0"), &vars[0], 0 },
93
	{ S("var1"), &vars[1], 0 },
94
	{ S("var2"), &vars[2], 0 },
95
	{ S("var3"), &vars[3], 0 },
96
	{ S("var4"), &vars[4], 0 },
97
	{ S("var5"), &vars[5], 0 },
98
	{ S("var6"), &vars[6], 0 },
99
	{ S("var7"), &vars[7], 0 },
100
	{ 0, 0, 0, 0 },
101
};
102
103
typedef struct opt_apply opt_apply;
104
struct opt_apply {
105
	char **opt;
106
	char *val;
107
};
108
109
/*
110
 * Specially expand the remote host name first
111
 */
112
static 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
 */
120
static 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
 */
135
static 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
 */
162
static char *
163
opt(char **p)
164
{
165
	char *cp = *p;
166
	char *dp = cp;
167
	char *s = cp;
168
169
top:
170
	while (*cp && *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
206
static int
207
eval_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))) {
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_USER, "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)) {
270
				switch (vs_opt) {
271
#if 1	/* XXX ancient compat */
272
				case OldSyn:
273
					plog(XLOG_WARNING, "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) == (vs_opt == SelNE))) {
283
						plog(XLOG_MAP, "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_USER, "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_USER, "key %s: Unrecognised key/option \"%s\"", mapkey, f);
307
	}
308
309
	return 1;
310
}
311
312
/*
313
 * Free an option
314
 */
315
static void
316
free_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
 */
327
void
328
normalize_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
 */
369
static void
370
expand_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+MAXPATHLEN)
379
static char expand_error[] = "No space to expand \"%s\"";
380
381
	char expbuf[MAXPATHLEN+1];
382
	char nbuf[NLEN+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)) {
398
			strncpy(ep, cp, len);
399
			ep += len;
400
		  } else {
401
			plog(XLOG_ERROR, expand_error, *p->opt);
402
			goto out;
403
		  }
404
		}
405
		cp = dp + 1;
406
		ch = *cp++;
407
		if (ch == '$') {
408
			if (BUFSPACE(ep, 1)) {
409
				*ep++ = '$';
410
			} else {
411
				plog(XLOG_ERROR, 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_USER, "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 > NLEN)
474
				len = NLEN;
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)) {
493
					char xbuf[NLEN+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)) {
569
							strlcpy(ep, vptr, expbuf + sizeof expbuf - ep);
570
							ep += strlen(ep);
571
						} else {
572
							plog(XLOG_ERROR, 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)) {
596
						strlcpy(ep, env, expbuf + sizeof expbuf - ep);
597
						ep += strlen(ep);
598
					} else {
599
						plog(XLOG_ERROR, expand_error, *p->opt);
600
						goto out;
601
					}
602
#ifdef DEBUG
603
					Debug(D_STR)
604
					plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
605
#endif /* DEBUG */
606
				} else {
607
					plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
608
				}
609
			}
610
		} else {
611
			/*
612
			 * Error, error
613
			 */
614
			plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt);
615
		}
616
	}
617
618
out:
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))) {
629
			strlcpy(ep, cp, expbuf + sizeof expbuf - ep);
630
		} else {
631
			plog(XLOG_ERROR, 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_DEBUG, "Expansion of \"%s\"...", cp_orig);
645
		plog(XLOG_DEBUG, "... is \"%s\"", *p->opt);
646
	}
647
#endif /* DEBUG */
648
}
649
650
/*
651
 * Wrapper for expand_op
652
 */
653
static void
654
expand_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
 */
673
static void
674
apply_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
 */
684
void
685
free_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);
696
}
697
698
/*
699
 * Expand lookup key
700
 */
701
char *
702
expand_key(char *key)
703
{
704
	opt_apply oa;
705
706
	oa.opt = &key; oa.val = 0;
707
	expand_opts(&oa, TRUE);
708
709
	return key;
710
}
711
712
/*
713
 * Remove trailing /'s from a string
714
 * unless the string is a single / (Steven Glassman)
715
 */
716
void
717
deslashify(char *s)
718
{
719
	if (s && *s) {
720
		char *sl = s + strlen(s);
721
		while (*--sl == '/' && sl > s)
722
			*sl = '\0';
723
	}
724
}
725
726
int
727
eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path,
728
    char *key, char *map)
729
{
730
	int ok = TRUE;
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))
762
		ok = FALSE;
763
764
	/*
765
	 * Expand local options
766
	 */
767
	if (ok && !eval_opts(fs_static.fs_local, key))
768
		ok = FALSE;
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);
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);
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
}