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 |
|
|
} |