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