File: | src/usr.sbin/amd/amd/opts.c |
Warning: | line 170, column 9 Dereference of null pointer (loaded from variable 'cp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 NLEN16 16 /* conservative */ | |||
56 | #define S(x)(x) , (sizeof(x)-1) (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")("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 | ||||
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_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 | */ | |||
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+1024) (((ep) + (len)) < expbuf+PATH_MAX1024) | |||
379 | static 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 | ||||
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))(((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 | */ | |||
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(0)); | |||
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(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 | */ | |||
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(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)) | |||
| ||||
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 | } |