]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/amd/amd/opts.c
MFC r308493, r308619: Update amd from am-utils 6.1.5 to 6.2.
[FreeBSD/stable/10.git] / contrib / amd / amd / opts.c
1 /*
2  * Copyright (c) 1997-2014 Erez Zadok
3  * Copyright (c) 1989 Jan-Simon Pendry
4  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1989 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *
36  * File: am-utils/amd/opts.c
37  *
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include <config.h>
42 #endif /* HAVE_CONFIG_H */
43 #include <am_defs.h>
44 #include <amd.h>
45
46 /*
47  * MACROS:
48  */
49 #define NLEN    16      /* Length of longest option name (conservative) */
50 #define S(x) (x) , (sizeof(x)-1)
51 /*
52  * The BUFSPACE macros checks that there is enough space
53  * left in the expansion buffer.  If there isn't then we
54  * give up completely.  This is done to avoid crashing the
55  * automounter itself (which would be a bad thing to do).
56  */
57 #define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
58
59 /*
60  * TYPEDEFS:
61  */
62 typedef int (*IntFuncPtr) (char *);
63 typedef struct opt_apply opt_apply;
64 enum vs_opt { SelEQ, SelNE, VarAss };
65
66 /*
67  * STRUCTURES
68  */
69 struct opt {
70   char *name;                   /* Name of the option */
71   int nlen;                     /* Length of option name */
72   char **optp;                  /* Pointer to option value string */
73   char **sel_p;                 /* Pointer to selector value string */
74   int (*fxn_p)(char *);         /* Pointer to boolean function */
75   int case_insensitive;         /* How to do selector comparisons */
76 };
77
78 struct opt_apply {
79   char **opt;
80   char *val;
81 };
82
83 struct functable {
84   char *name;
85   IntFuncPtr func;
86 };
87
88 /*
89  * FORWARD DEFINITION:
90  */
91 static int f_in_network(char *);
92 static int f_xhost(char *);
93 static int f_netgrp(char *);
94 static int f_netgrpd(char *);
95 static int f_exists(char *);
96 static int f_false(char *);
97 static int f_true(char *);
98 static inline char *expand_options(char *key);
99
100 /*
101  * STATICS:
102  */
103 static char NullStr[] = "<NULL>";
104 static char nullstr[] = "";
105 static char *opt_dkey = NullStr;
106 static char *opt_host = nullstr; /* XXX: was the global hostname */
107 static char *opt_hostd = hostd;
108 static char *opt_key = nullstr;
109 static char *opt_keyd = nullstr;
110 static char *opt_map = nullstr;
111 static char *opt_path = nullstr;
112 char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR];
113 char *opt_uid = uid_str;
114 char *opt_gid = gid_str;
115 static char *vars[8];
116 static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */
117
118 /*
119  * GLOBALS
120  */
121 static struct am_opts fs_static;      /* copy of the options to play with */
122
123
124 /*
125  * Options in some order corresponding to frequency of use so that
126  * first-match algorithm is sped up.
127  */
128 static struct opt opt_fields[] = {
129   /* Name and length.
130         Option str.             Selector str.   boolean fxn.    case sensitive */
131   { S("opts"),
132        &fs_static.opt_opts,     0,              0,              FALSE   },
133   { S("host"),
134         0,                      &opt_host,      0,              TRUE    },
135   { S("hostd"),
136         0,                      &opt_hostd,     0,              TRUE    },
137   { S("type"),
138         &fs_static.opt_type,    0,              0,              FALSE   },
139   { S("rhost"),
140         &fs_static.opt_rhost,   0,              0,              TRUE    },
141   { S("rfs"),
142         &fs_static.opt_rfs,     0,              0,              FALSE   },
143   { S("fs"),
144         &fs_static.opt_fs,      0,              0,              FALSE   },
145   { S("key"),
146         0,                      &opt_key,       0,              FALSE   },
147   { S("map"),
148         0,                      &opt_map,       0,              FALSE   },
149   { S("sublink"),
150         &fs_static.opt_sublink, 0,              0,              FALSE   },
151   { S("arch"),
152         0,                      &gopt.arch,     0,              TRUE    },
153   { S("dev"),
154         &fs_static.opt_dev,     0,              0,              FALSE   },
155   { S("pref"),
156         &fs_static.opt_pref,    0,              0,              FALSE   },
157   { S("path"),
158         0,                      &opt_path,      0,              FALSE   },
159   { S("autodir"),
160         0,                      &gopt.auto_dir, 0,              FALSE   },
161   { S("delay"),
162         &fs_static.opt_delay,   0,              0,              FALSE   },
163   { S("domain"),
164         0,                      &hostdomain,    0,              TRUE    },
165   { S("karch"),
166         0,                      &gopt.karch,    0,              TRUE    },
167   { S("cluster"),
168         0,                      &gopt.cluster,  0,              TRUE    },
169   { S("wire"),
170         0,                      0,              f_in_network,   TRUE    },
171   { S("network"),
172         0,                      0,              f_in_network,   TRUE    },
173   { S("netnumber"),
174         0,                      0,              f_in_network,   TRUE    },
175   { S("byte"),
176         0,                      &endian,        0,              TRUE    },
177   { S("os"),
178         0,                      &gopt.op_sys,   0,              TRUE    },
179   { S("osver"),
180         0,                      &gopt.op_sys_ver,       0,      TRUE    },
181   { S("full_os"),
182         0,                      &gopt.op_sys_full,      0,      TRUE    },
183   { S("vendor"),
184         0,                      &gopt.op_sys_vendor,    0,      TRUE    },
185   { S("remopts"),
186         &fs_static.opt_remopts, 0,              0,              FALSE   },
187   { S("mount"),
188         &fs_static.opt_mount,   0,              0,              FALSE   },
189   { S("unmount"),
190         &fs_static.opt_unmount, 0,              0,              FALSE   },
191   { S("umount"),
192         &fs_static.opt_umount,  0,              0,              FALSE   },
193   { S("cache"),
194         &fs_static.opt_cache,   0,              0,              FALSE   },
195   { S("user"),
196         &fs_static.opt_user,    0,              0,              FALSE   },
197   { S("group"),
198         &fs_static.opt_group,   0,              0,              FALSE   },
199   { S(".key"),
200         0,                      &opt_dkey,      0,              FALSE   },
201   { S("key."),
202         0,                      &opt_keyd,      0,              FALSE   },
203   { S("maptype"),
204         &fs_static.opt_maptype, 0,              0,              FALSE   },
205   { S("cachedir"),
206         &fs_static.opt_cachedir, 0,             0,              FALSE   },
207   { S("addopts"),
208         &fs_static.opt_addopts, 0,              0,              FALSE   },
209   { S("uid"),
210         0,                      &opt_uid,       0,              FALSE   },
211   { S("gid"),
212         0,                      &opt_gid,       0,              FALSE   },
213   { S("mount_type"),
214         &fs_static.opt_mount_type, 0,           0,              FALSE   },
215   { S("dollar"),
216         &literal_dollar,        0,              0,              FALSE   },
217   { S("var0"),
218         &vars[0],               0,              0,              FALSE   },
219   { S("var1"),
220         &vars[1],               0,              0,              FALSE   },
221   { S("var2"),
222         &vars[2],               0,              0,              FALSE   },
223   { S("var3"),
224         &vars[3],               0,              0,              FALSE   },
225   { S("var4"),
226         &vars[4],               0,              0,              FALSE   },
227   { S("var5"),
228         &vars[5],               0,              0,              FALSE   },
229   { S("var6"),
230         &vars[6],               0,              0,              FALSE   },
231   { S("var7"),
232         &vars[7],               0,              0,              FALSE   },
233   { 0, 0, 0, 0, 0, FALSE },
234 };
235
236 static struct functable functable[] = {
237   { "in_network",       f_in_network },
238   { "xhost",            f_xhost },
239   { "netgrp",           f_netgrp },
240   { "netgrpd",          f_netgrpd },
241   { "exists",           f_exists },
242   { "false",            f_false },
243   { "true",             f_true },
244   { 0, 0 },
245 };
246
247 /*
248  * Specially expand the remote host name first
249  */
250 static opt_apply rhost_expansion[] =
251 {
252   {&fs_static.opt_rhost, "${host}"},
253   {0, 0},
254 };
255
256 /*
257  * List of options which need to be expanded
258  * Note that the order here _may_ be important.
259  */
260 static opt_apply expansions[] =
261 {
262   {&fs_static.opt_sublink, 0},
263   {&fs_static.opt_rfs, "${path}"},
264   {&fs_static.opt_fs, "${autodir}/${rhost}${rfs}"},
265   {&fs_static.opt_opts, "rw"},
266   {&fs_static.opt_remopts, "${opts}"},
267   {&fs_static.opt_mount, 0},
268   {&fs_static.opt_unmount, 0},
269   {&fs_static.opt_umount, 0},
270   {&fs_static.opt_cachedir, 0},
271   {&fs_static.opt_addopts, 0},
272   {0, 0},
273 };
274
275 /*
276  * List of options which need to be free'ed before re-use
277  */
278 static opt_apply to_free[] =
279 {
280   {&fs_static.fs_glob, 0},
281   {&fs_static.fs_local, 0},
282   {&fs_static.fs_mtab, 0},
283   {&fs_static.opt_sublink, 0},
284   {&fs_static.opt_rfs, 0},
285   {&fs_static.opt_fs, 0},
286   {&fs_static.opt_rhost, 0},
287   {&fs_static.opt_opts, 0},
288   {&fs_static.opt_remopts, 0},
289   {&fs_static.opt_mount, 0},
290   {&fs_static.opt_unmount, 0},
291   {&fs_static.opt_umount, 0},
292   {&fs_static.opt_cachedir, 0},
293   {&fs_static.opt_addopts, 0},
294   {&vars[0], 0},
295   {&vars[1], 0},
296   {&vars[2], 0},
297   {&vars[3], 0},
298   {&vars[4], 0},
299   {&vars[5], 0},
300   {&vars[6], 0},
301   {&vars[7], 0},
302   {0, 0},
303 };
304
305
306 /*
307  * expand backslash escape sequences
308  * (escaped slash is handled separately in normalize_slash)
309  */
310 static char
311 backslash(char **p)
312 {
313   char c;
314
315   if ((*p)[1] == '\0') {
316     plog(XLOG_USER, "Empty backslash escape");
317     return **p;
318   }
319
320   if (**p == '\\') {
321     (*p)++;
322     switch (**p) {
323     case 'g':
324       c = '\007';               /* Bell */
325       break;
326     case 'b':
327       c = '\010';               /* Backspace */
328       break;
329     case 't':
330       c = '\011';               /* Horizontal Tab */
331       break;
332     case 'n':
333       c = '\012';               /* New Line */
334       break;
335     case 'v':
336       c = '\013';               /* Vertical Tab */
337       break;
338     case 'f':
339       c = '\014';               /* Form Feed */
340       break;
341     case 'r':
342       c = '\015';               /* Carriage Return */
343       break;
344     case 'e':
345       c = '\033';               /* Escape */
346       break;
347     case '0':
348     case '1':
349     case '2':
350     case '3':
351     case '4':
352     case '5':
353     case '6':
354     case '7':
355       {
356         int cnt, val, ch;
357
358         for (cnt = 0, val = 0; cnt < 3; cnt++) {
359           ch = *(*p)++;
360           if (ch < '0' || ch > '7') {
361             (*p)--;
362             break;
363           }
364           val = (val << 3) | (ch - '0');
365         }
366
367         if ((val & 0xffffff00) != 0)
368           plog(XLOG_USER,
369                "Too large character constant %u\n",
370                val);
371         c = (char) val;
372         --(*p);
373       }
374       break;
375
376     default:
377       c = **p;
378       break;
379     }
380   } else
381     c = **p;
382
383   return c;
384 }
385
386
387 /*
388  * Skip to next option in the string
389  */
390 static char *
391 opt(char **p)
392 {
393   char *cp = *p;
394   char *dp = cp;
395   char *s = cp;
396
397 top:
398   while (*cp && *cp != ';') {
399     if (*cp == '"') {
400       /*
401        * Skip past string
402        */
403       for (cp++; *cp && *cp != '"'; cp++)
404         if (*cp == '\\')
405           *dp++ = backslash(&cp);
406         else
407           *dp++ = *cp;
408       if (*cp)
409         cp++;
410     } else {
411       *dp++ = *cp++;
412     }
413   }
414
415   /*
416    * Skip past any remaining ';'s
417    */
418   while (*cp == ';')
419     cp++;
420
421   /*
422    * If we have a zero length string
423    * and there are more fields, then
424    * parse the next one.  This allows
425    * sequences of empty fields.
426    */
427   if (*cp && dp == s)
428     goto top;
429
430   *dp = '\0';
431
432   *p = cp;
433   return s;
434 }
435
436
437 /*
438  * These routines add a new style of selector; function-style boolean
439  * operators.  To add new ones, just define functions as in true, false,
440  * exists (below) and add them to the functable, above.
441  *
442  * Usage example: Some people have X11R5 local, some go to a server. I do
443  * this:
444  *
445  *    *       exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \
446  *            -type:=nfs;rfs=/usr/pkg/${key} \
447  *            rhost:=server1 \
448  *            rhost:=server2
449  *
450  * -Rens Troost <rens@imsi.com>
451  */
452 static IntFuncPtr
453 functable_lookup(char *key)
454 {
455   struct functable *fp;
456
457   for (fp = functable; fp->name; fp++)
458     if (FSTREQ(fp->name, key))
459         return (fp->func);
460   return (IntFuncPtr) NULL;
461 }
462
463
464 /*
465  * Fill in the global structure fs_static by
466  * cracking the string opts.  opts may be
467  * scribbled on at will.  Does NOT evaluate options.
468  * Returns 0 on error, 1 if no syntax errors were discovered.
469  */
470 static int
471 split_opts(char *opts, char *mapkey)
472 {
473   char *o = opts;
474   char *f;
475
476   /*
477    * For each user-specified option
478    */
479   for (f = opt(&o); *f; f = opt(&o)) {
480     struct opt *op;
481     char *eq = strchr(f, '=');
482     char *opt = NULL;
483
484     if (!eq)
485       continue;
486
487     if (*(eq-1) == '!' ||
488         eq[1] == '=' ||
489         eq[1] == '!') { /* != or == or =! */
490       continue;                 /* we don't care about selectors */
491     }
492
493     if (*(eq-1) == ':') {       /* := */
494       *(eq-1) = '\0';
495     } else {
496       /* old style assignment */
497       eq[0] = '\0';
498     }
499     opt = eq + 1;
500
501     /*
502      * For each recognized option
503      */
504     for (op = opt_fields; op->name; op++) {
505       /*
506        * Check whether they match
507        */
508       if (FSTREQ(op->name, f)) {
509         if (op->sel_p) {
510           plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
511                mapkey, op->name);
512           return 0;
513         }
514         *op->optp = opt;        /* actual assignment into fs_static */
515         break;                  /* break out of for loop */
516       } /* end of "if (FSTREQ(op->name, f))" statement  */
517     } /* end of "for (op = opt_fields..." statement  */
518
519     if (!op->name)
520       plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
521   }
522
523   return 1;
524 }
525
526
527 /*
528  * Just evaluate selectors, which were split by split_opts.
529  * Returns 0 on error or no match, 1 if matched.
530  */
531 static int
532 eval_selectors(char *opts, char *mapkey)
533 {
534   char *o, *old_o;
535   char *f;
536   int ret = 0;
537
538   o = old_o = xstrdup(opts);
539
540   /*
541    * For each user-specified option
542    */
543   for (f = opt(&o); *f; f = opt(&o)) {
544     struct opt *op;
545     enum vs_opt vs_opt;
546     char *eq = strchr(f, '=');
547     char *fx;
548     IntFuncPtr func;
549     char *opt = NULL;
550     char *arg;
551
552     if (!eq) {
553       /*
554        * No value, is it a function call?
555        */
556       arg = strchr(f, '(');
557
558       if (!arg || arg[1] == '\0' || arg == f) {
559         /*
560          * No, just continue
561          */
562         plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
563         continue;
564       }
565
566       /* null-terminate the argument  */
567       *arg++ = '\0';
568       fx = strchr(arg, ')');
569       if (fx == NULL || fx == arg) {
570         plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f);
571         continue;
572       }
573       *fx = '\0';
574
575       if (f[0] == '!') {
576         vs_opt = SelNE;
577         f++;
578       } else {
579         vs_opt = SelEQ;
580       }
581       /*
582        * look up f in functable and pass it arg.
583        * func must return 0 on failure, and 1 on success.
584        */
585       if ((func = functable_lookup(f))) {
586         int funok;
587
588         /* this allocates memory, don't forget to free */
589         arg = expand_options(arg);
590         funok = func(arg);
591         XFREE(arg);
592
593         if (vs_opt == SelNE)
594           funok = !funok;
595         if (!funok)
596           goto out;
597
598         continue;
599       } else {
600         plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
601         goto out;
602       }
603     } else {
604       if (eq[1] == '\0' || eq == f) {
605 #ifdef notdef
606         /* We allow empty assignments */
607         plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f);
608 #endif
609         continue;
610       }
611     }
612
613     /*
614      * Check what type of operation is happening
615      * !=, =!  is SelNE
616      * == is SelEQ
617      * =, := is VarAss
618      */
619     if (*(eq-1) == '!') {       /* != */
620       vs_opt = SelNE;
621       *(eq-1) = '\0';
622       opt = eq + 1;
623     } else if (*(eq-1) == ':') {        /* := */
624       continue;
625     } else if (eq[1] == '=') {  /* == */
626       vs_opt = SelEQ;
627       eq[0] = '\0';
628       opt = eq + 2;
629     } else if (eq[1] == '!') {  /* =! */
630       vs_opt = SelNE;
631       eq[0] = '\0';
632       opt = eq + 2;
633     } else {
634       /* old style assignment */
635       continue;
636     }
637
638     /*
639      * For each recognized option
640      */
641     for (op = opt_fields; op->name; op++) {
642       /*
643        * Check whether they match
644        */
645       if (FSTREQ(op->name, f)) {
646         opt = expand_options(opt);
647
648         if (op->sel_p != NULL) {
649           int selok;
650           if (op->case_insensitive) {
651             selok = STRCEQ(*op->sel_p, opt);
652           } else {
653             selok = STREQ(*op->sel_p, opt);
654           }
655           if (vs_opt == SelNE)
656             selok = !selok;
657           if (!selok) {
658             plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
659                  mapkey,
660                  op->name,
661                  *op->sel_p,
662                  vs_opt == SelNE ? "mis" : "",
663                  opt);
664             XFREE(opt);
665             goto out;
666           }
667           XFREE(opt);
668         }
669         /* check if to apply a function */
670         if (op->fxn_p) {
671           int funok;
672
673           funok = op->fxn_p(opt);
674           if (vs_opt == SelNE)
675             funok = !funok;
676           if (!funok) {
677             plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
678                  mapkey,
679                  op->name,
680                  vs_opt == SelNE ? "mis" : "",
681                  opt);
682             XFREE(opt);
683             goto out;
684           }
685           XFREE(opt);
686         }
687         break;                  /* break out of for loop */
688       }
689     }
690
691     if (!op->name)
692       plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
693   }
694
695   /* all is ok */
696   ret = 1;
697
698  out:
699   free(old_o);
700   return ret;
701 }
702
703
704 /*
705  * Skip to next option in the string, but don't scribble over the string.
706  * However, *p gets repointed to the start of the next string past ';'.
707  */
708 static char *
709 opt_no_scribble(char **p)
710 {
711   char *cp = *p;
712   char *dp = cp;
713   char *s = cp;
714
715 top:
716   while (*cp && *cp != ';') {
717     if (*cp == '\"') {
718       /*
719        * Skip past string
720        */
721       cp++;
722       while (*cp && *cp != '\"')
723         *dp++ = *cp++;
724       if (*cp)
725         cp++;
726     } else {
727       *dp++ = *cp++;
728     }
729   }
730
731   /*
732    * Skip past any remaining ';'s
733    */
734   while (*cp == ';')
735     cp++;
736
737   /*
738    * If we have a zero length string
739    * and there are more fields, then
740    * parse the next one.  This allows
741    * sequences of empty fields.
742    */
743   if (*cp && dp == s)
744     goto top;
745
746   *p = cp;
747   return s;
748 }
749
750
751 /*
752  * Strip any selectors from a string.  Selectors are all assumed to be
753  * first in the string.  This is used for the new /defaults method which will
754  * use selectors as well.
755  */
756 char *
757 strip_selectors(char *opts, char *mapkey)
758 {
759   /*
760    * Fill in the global structure fs_static by
761    * cracking the string opts.  opts may be
762    * scribbled on at will.
763    */
764   char *o = opts;
765   char *oo = opts;
766   char *f;
767
768   /*
769    * Scan options.  Note that the opt() function scribbles on the opt string.
770    */
771   while (*(f = opt_no_scribble(&o))) {
772     enum vs_opt vs_opt = VarAss;
773     char *eq = strchr(f, '=');
774
775     if (!eq || eq[1] == '\0' || eq == f) {
776       /*
777        * No option or assignment?  Return as is.
778        */
779       plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f);
780       return o;
781     }
782     /*
783      * Check what type of operation is happening
784      * !=, =!  is SelNE
785      * == is SelEQ
786      * := is VarAss
787      */
788     if (*(eq-1) == '!') {       /* != */
789       vs_opt = SelNE;
790     } else if (*(eq-1) == ':') {        /* := */
791       vs_opt = VarAss;
792     } else if (eq[1] == '=') {  /* == */
793       vs_opt = SelEQ;
794     } else if (eq[1] == '!') {  /* =! */
795       vs_opt = SelNE;
796     }
797     switch (vs_opt) {
798     case SelEQ:
799     case SelNE:
800       /* Skip this selector, maybe there's another one following it */
801       plog(XLOG_USER, "skipping selector to \"%s\"", o);
802       /* store previous match. it may have been the first assignment */
803       oo = o;
804       break;
805
806     case VarAss:
807       /* found the first assignment, return the string starting with it */
808       dlog("found first assignment past selectors \"%s\"", o);
809       return oo;
810     }
811   }
812
813   /* return the same string by default. should not happen. */
814   return oo;
815 }
816
817
818 /*****************************************************************************
819  *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true):                     ***
820  *****************************************************************************/
821
822 /* test if arg is any of this host's network names or numbers */
823 static int
824 f_in_network(char *arg)
825 {
826   int status;
827
828   if (!arg)
829     return 0;
830
831   status = is_network_member(arg);
832   dlog("%s is %son a local network", arg, (status ? "" : "not "));
833   return status;
834 }
835
836
837 /*
838  * Test if arg is any of this host's names or aliases (CNAMES).
839  * Note: this function compares against the fully expanded host name (hostd).
840  * XXX: maybe we also need to compare against the stripped host name?
841  */
842 static int
843 f_xhost(char *arg)
844 {
845   struct hostent *hp;
846   char **cp;
847
848   if (!arg)
849     return 0;
850
851   /* simple test: does it match main host name? */
852   if (STREQ(arg, opt_hostd))
853     return 1;
854
855   /* now find all of the names of "arg" and compare against opt_hostd */
856   hp = gethostbyname(arg);
857   if (hp == NULL) {
858 #ifdef HAVE_HSTRERROR
859     plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno));
860 #else /* not HAVE_HSTRERROR */
861     plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno);
862 #endif /* not HAVE_HSTRERROR */
863     return 0;
864   }
865   /* check primary name */
866   if (hp->h_name) {
867     dlog("xhost: compare %s==%s", hp->h_name, opt_hostd);
868     if (STREQ(hp->h_name, opt_hostd)) {
869       plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name);
870       return 1;
871     }
872   }
873   /* check all aliases, if any */
874   if (hp->h_aliases == NULL) {
875     dlog("gethostbyname(%s) has no aliases", arg);
876     return 0;
877   }
878   cp = hp->h_aliases;
879   while (*cp) {
880     dlog("xhost: compare alias %s==%s", *cp, opt_hostd);
881     if (STREQ(*cp, opt_hostd)) {
882       plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp);
883       return 1;
884     }
885     cp++;
886   }
887   /* nothing matched */
888   return 0;
889 }
890
891
892 /* test if this host (short hostname form) is in netgroup (arg) */
893 static int
894 f_netgrp(char *arg)
895 {
896   int status;
897   char *ptr, *nhost;
898
899   if ((ptr = strchr(arg, ',')) != NULL) {
900     *ptr = '\0';
901     nhost = ptr + 1;
902   } else {
903     nhost = opt_host;
904   }
905   status = innetgr(arg, nhost, NULL, NULL);
906   dlog("netgrp = %s status = %d host = %s", arg, status, nhost);
907   if (ptr)
908     *ptr = ',';
909   return status;
910 }
911
912
913 /* test if this host (fully-qualified name) is in netgroup (arg) */
914 static int
915 f_netgrpd(char *arg)
916 {
917   int status;
918   char *ptr, *nhost;
919
920   if ((ptr = strchr(arg, ',')) != NULL) {
921     *ptr = '\0';
922     nhost = ptr + 1;
923   } else {
924     nhost = opt_hostd;
925   }
926   status = innetgr(arg, nhost, NULL, NULL);
927   dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost);
928   if (ptr)
929     *ptr = ',';
930   return status;
931 }
932
933
934 /* test if file (arg) exists via lstat */
935 static int
936 f_exists(char *arg)
937 {
938   struct stat buf;
939
940   if (lstat(arg, &buf) < 0)
941     return (0);
942   else
943     return (1);
944 }
945
946
947 /* always false */
948 static int
949 f_false(char *arg)
950 {
951   return (0);
952 }
953
954
955 /* always true */
956 static int
957 f_true(char *arg)
958 {
959   return (1);
960 }
961
962
963 /*
964  * Free an option
965  */
966 static void
967 free_op(opt_apply *p, int b)
968 {
969   XFREE(*p->opt);
970 }
971
972
973 /*
974  * Normalize slashes in the string.
975  */
976 void
977 normalize_slash(char *p)
978 {
979   char *f, *f0;
980
981   if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
982     return;
983
984   f0 = f = strchr(p, '/');
985   if (f) {
986     char *t = f;
987     do {
988       /* assert(*f == '/'); */
989       if (f == f0 && f[0] == '/' && f[1] == '/') {
990         /* copy double slash iff first */
991         *t++ = *f++;
992         *t++ = *f++;
993       } else {
994         /* copy a single / across */
995         *t++ = *f++;
996       }
997
998       /* assert(f[-1] == '/'); */
999       /* skip past more /'s */
1000       while (*f == '/')
1001         f++;
1002
1003       /* assert(*f != '/'); */
1004       /* keep copying up to next / */
1005       while (*f && *f != '/') {
1006         /* support escaped slashes '\/' */
1007         if (f[0] == '\\' && f[1] == '/')
1008           f++;                  /* skip backslash */
1009         *t++ = *f++;
1010       }
1011
1012       /* assert(*f == 0 || *f == '/'); */
1013
1014     } while (*f);
1015     *t = '\0';                  /* derived from fix by Steven Glassman */
1016   }
1017 }
1018
1019
1020 /*
1021  * Macro-expand an option.  Note that this does not
1022  * handle recursive expansions.  They will go badly wrong.
1023  * If sel_p is true then old expand selectors, otherwise
1024  * don't expand selectors.
1025  */
1026 static char *
1027 expand_op(char *opt, int sel_p)
1028 {
1029 #define EXPAND_ERROR "No space to expand \"%s\""
1030   char expbuf[MAXPATHLEN + 1];
1031   char nbuf[NLEN + 1];
1032   char *ep = expbuf;
1033   char *cp = opt;
1034   char *dp;
1035   struct opt *op;
1036   char *cp_orig = opt;
1037
1038   while ((dp = strchr(cp, '$'))) {
1039     char ch;
1040     /*
1041      * First copy up to the $
1042      */
1043     {
1044       int len = dp - cp;
1045
1046       if (len > 0) {
1047         if (BUFSPACE(ep, len)) {
1048           /*
1049            * We use strncpy (not xstrlcpy) because 'ep' relies on its
1050            * semantics.  BUFSPACE guarantees that ep can hold len.
1051            */
1052           strncpy(ep, cp, len);
1053           ep += len;
1054         } else {
1055           plog(XLOG_ERROR, EXPAND_ERROR, opt);
1056           goto out;
1057         }
1058       }
1059     }
1060
1061     cp = dp + 1;
1062     ch = *cp++;
1063     if (ch == '$') {
1064       if (BUFSPACE(ep, 1)) {
1065         *ep++ = '$';
1066       } else {
1067         plog(XLOG_ERROR, EXPAND_ERROR, opt);
1068         goto out;
1069       }
1070     } else if (ch == '{') {
1071       /* Expansion... */
1072       enum {
1073         E_All, E_Dir, E_File, E_Domain, E_Host
1074       } todo;
1075       /*
1076        * Find closing brace
1077        */
1078       char *br_p = strchr(cp, '}');
1079       int len;
1080
1081       /*
1082        * Check we found it
1083        */
1084       if (!br_p) {
1085         /*
1086          * Just give up
1087          */
1088         plog(XLOG_USER, "No closing '}' in \"%s\"", opt);
1089         goto out;
1090       }
1091       len = br_p - cp;
1092
1093       /*
1094        * Figure out which part of the variable to grab.
1095        */
1096       if (*cp == '/') {
1097         /*
1098          * Just take the last component
1099          */
1100         todo = E_File;
1101         cp++;
1102         --len;
1103       } else if (*(br_p-1) == '/') {
1104         /*
1105          * Take all but the last component
1106          */
1107         todo = E_Dir;
1108         --len;
1109       } else if (*cp == '.') {
1110         /*
1111          * Take domain name
1112          */
1113         todo = E_Domain;
1114         cp++;
1115         --len;
1116       } else if (*(br_p-1) == '.') {
1117         /*
1118          * Take host name
1119          */
1120         todo = E_Host;
1121         --len;
1122       } else {
1123         /*
1124          * Take the whole lot
1125          */
1126         todo = E_All;
1127       }
1128
1129       /*
1130        * Truncate if too long.  Since it won't
1131        * match anyway it doesn't matter that
1132        * it has been cut short.
1133        */
1134       if (len > NLEN)
1135         len = NLEN;
1136
1137       /*
1138        * Put the string into another buffer so
1139        * we can do comparisons.
1140        *
1141        * We use strncpy here (not xstrlcpy) because the dest is meant
1142        * to be truncated and we don't want to log it as an error.  The
1143        * use of the BUFSPACE macro above guarantees the safe use of
1144        * strncpy with nbuf.
1145        */
1146       strncpy(nbuf, cp, len);
1147       nbuf[len] = '\0';
1148
1149       /*
1150        * Advance cp
1151        */
1152       cp = br_p + 1;
1153
1154       /*
1155        * Search the option array
1156        */
1157       for (op = opt_fields; op->name; op++) {
1158         /*
1159          * Check for match
1160          */
1161         if (len == op->nlen && STREQ(op->name, nbuf)) {
1162           char xbuf[NLEN + 3];
1163           char *val;
1164           /*
1165            * Found expansion.  Copy
1166            * the correct value field.
1167            */
1168           if (!(!op->sel_p == !sel_p)) {
1169             /*
1170              * Copy the string across unexpanded
1171              */
1172             xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}",
1173                       todo == E_File ? "/" :
1174                       todo == E_Domain ? "." : "",
1175                       nbuf,
1176                       todo == E_Dir ? "/" :
1177                       todo == E_Host ? "." : "");
1178             val = xbuf;
1179             /*
1180              * Make sure expansion doesn't
1181              * munge the value!
1182              */
1183             todo = E_All;
1184           } else if (op->sel_p) {
1185             val = *op->sel_p;
1186           } else {
1187             val = *op->optp;
1188           }
1189
1190           if (val) {
1191             /*
1192              * Do expansion:
1193              * ${/var} means take just the last part
1194              * ${var/} means take all but the last part
1195              * ${.var} means take all but first part
1196              * ${var.} means take just the first part
1197              * ${var} means take the whole lot
1198              */
1199             int vlen = strlen(val);
1200             char *vptr = val;
1201             switch (todo) {
1202             case E_Dir:
1203               vptr = strrchr(val, '/');
1204               if (vptr)
1205                 vlen = vptr - val;
1206               vptr = val;
1207               break;
1208             case E_File:
1209               vptr = strrchr(val, '/');
1210               if (vptr) {
1211                 vptr++;
1212                 vlen = strlen(vptr);
1213               } else
1214                 vptr = val;
1215               break;
1216             case E_Domain:
1217               vptr = strchr(val, '.');
1218               if (vptr) {
1219                 vptr++;
1220                 vlen = strlen(vptr);
1221               } else {
1222                 vptr = "";
1223                 vlen = 0;
1224               }
1225               break;
1226             case E_Host:
1227               vptr = strchr(val, '.');
1228               if (vptr)
1229                 vlen = vptr - val;
1230               vptr = val;
1231               break;
1232             case E_All:
1233               break;
1234             }
1235
1236             if (BUFSPACE(ep, vlen+1)) {
1237               /*
1238                * Don't call xstrlcpy() to truncate a string here.  It causes
1239                * spurious xstrlcpy() syslog() errors.  Use memcpy() and
1240                * explicitly terminate the string.
1241                */
1242               memcpy(ep, vptr, vlen+1);
1243               ep += vlen;
1244               *ep = '\0';
1245             } else {
1246               plog(XLOG_ERROR, EXPAND_ERROR, opt);
1247               goto out;
1248             }
1249           }
1250           /*
1251            * Done with this variable
1252            */
1253           break;
1254         }
1255       }
1256
1257       /*
1258        * Check that the search was successful
1259        */
1260       if (!op->name) {
1261         /*
1262          * If it wasn't then scan the
1263          * environment for that name
1264          * and use any value found
1265          */
1266         char *env = getenv(nbuf);
1267
1268         if (env) {
1269           int vlen = strlen(env);
1270
1271           if (BUFSPACE(ep, vlen+1)) {
1272             xstrlcpy(ep, env, vlen+1);
1273             ep += vlen;
1274           } else {
1275             plog(XLOG_ERROR, EXPAND_ERROR, opt);
1276             goto out;
1277           }
1278           if (amuDebug(D_STR))
1279             plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
1280         } else {
1281           plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
1282         }
1283       }
1284     } else {
1285       /*
1286        * Error, error
1287        */
1288       plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt);
1289     }
1290   }
1291
1292 out:
1293   /*
1294    * Handle common case - no expansion
1295    */
1296   if (cp == opt) {
1297     opt = xstrdup(cp);
1298   } else {
1299     /*
1300      * Finish off the expansion
1301      */
1302     int vlen = strlen(cp);
1303     if (BUFSPACE(ep, vlen+1)) {
1304       xstrlcpy(ep, cp, vlen+1);
1305       /* ep += vlen; */
1306     } else {
1307       plog(XLOG_ERROR, EXPAND_ERROR, opt);
1308     }
1309
1310     /*
1311      * Save the expansion
1312      */
1313     opt = xstrdup(expbuf);
1314   }
1315
1316   normalize_slash(opt);
1317
1318   if (amuDebug(D_STR)) {
1319     plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
1320     plog(XLOG_DEBUG, "......... is \"%s\"", opt);
1321   }
1322   return opt;
1323 }
1324
1325
1326 /*
1327  * Wrapper for expand_op
1328  */
1329 static void
1330 expand_opts(opt_apply *p, int sel_p)
1331 {
1332   if (*p->opt) {
1333     *p->opt = expand_op(*p->opt, sel_p);
1334   } else if (p->val) {
1335     /*
1336      * Do double expansion, remembering
1337      * to free the string from the first
1338      * expansion...
1339      */
1340     char *s = expand_op(p->val, TRUE);
1341     *p->opt = expand_op(s, sel_p);
1342     XFREE(s);
1343   }
1344 }
1345
1346
1347 /*
1348  * Apply a function to a list of options
1349  */
1350 static void
1351 apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b)
1352 {
1353   opt_apply *pp;
1354
1355   for (pp = ppp; pp->opt; pp++)
1356     (*op) (pp, b);
1357 }
1358
1359
1360 /*
1361  * Free the option table
1362  */
1363 void
1364 free_opts(am_opts *fo)
1365 {
1366   /*
1367    * Copy in the structure we are playing with
1368    */
1369   fs_static = *fo;
1370
1371   /*
1372    * Free previously allocated memory
1373    */
1374   apply_opts(free_op, to_free, FALSE);
1375 }
1376
1377 am_opts *
1378 copy_opts(am_opts *old)
1379 {
1380   am_opts *newopts;
1381   newopts = CALLOC(struct am_opts);
1382
1383 #define _AM_OPT_COPY(field) do { \
1384     if (old->field) \
1385       newopts->field = xstrdup(old->field); \
1386   } while (0)
1387
1388   _AM_OPT_COPY(fs_glob);
1389   _AM_OPT_COPY(fs_local);
1390   _AM_OPT_COPY(fs_mtab);
1391   _AM_OPT_COPY(opt_dev);
1392   _AM_OPT_COPY(opt_delay);
1393   _AM_OPT_COPY(opt_dir);
1394   _AM_OPT_COPY(opt_fs);
1395   _AM_OPT_COPY(opt_group);
1396   _AM_OPT_COPY(opt_mount);
1397   _AM_OPT_COPY(opt_opts);
1398   _AM_OPT_COPY(opt_remopts);
1399   _AM_OPT_COPY(opt_pref);
1400   _AM_OPT_COPY(opt_cache);
1401   _AM_OPT_COPY(opt_rfs);
1402   _AM_OPT_COPY(opt_rhost);
1403   _AM_OPT_COPY(opt_sublink);
1404   _AM_OPT_COPY(opt_type);
1405   _AM_OPT_COPY(opt_mount_type);
1406   _AM_OPT_COPY(opt_unmount);
1407   _AM_OPT_COPY(opt_umount);
1408   _AM_OPT_COPY(opt_user);
1409   _AM_OPT_COPY(opt_maptype);
1410   _AM_OPT_COPY(opt_cachedir);
1411   _AM_OPT_COPY(opt_addopts);
1412
1413   return newopts;
1414 }
1415
1416
1417 /*
1418  * Expand selectors (variables that cannot be assigned to or overridden)
1419  */
1420 char *
1421 expand_selectors(char *key)
1422 {
1423   return expand_op(key, TRUE);
1424 }
1425
1426
1427 /*
1428  * Expand options (i.e. non-selectors, see above for definition)
1429  */
1430 static inline char *
1431 expand_options(char *key)
1432 {
1433   return expand_op(key, FALSE);
1434 }
1435
1436
1437 /*
1438  * Remove trailing /'s from a string
1439  * unless the string is a single / (Steven Glassman)
1440  * or unless it is two slashes // (Kevin D. Bond)
1441  * or unless amd.conf says not to touch slashes.
1442  */
1443 void
1444 deslashify(char *s)
1445 {
1446   if (!(gopt.flags & CFM_NORMALIZE_SLASHES))
1447     return;
1448
1449   if (s && *s) {
1450     char *sl = s + strlen(s);
1451
1452     while (*--sl == '/' && sl > s)
1453       *sl = '\0';
1454   }
1455 }
1456
1457
1458 int
1459 eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map)
1460 {
1461   int ok = TRUE;
1462
1463   free_opts(fo);
1464
1465   /*
1466    * Clear out the option table
1467    */
1468   memset((voidp) &fs_static, 0, sizeof(fs_static));
1469   memset((voidp) vars, 0, sizeof(vars));
1470   memset((voidp) fo, 0, sizeof(*fo));
1471
1472   /* set hostname */
1473   opt_host = (char *) am_get_hostname();
1474
1475   /*
1476    * Set key, map & path before expansion
1477    */
1478   opt_key = key;
1479   opt_map = map;
1480   opt_path = path;
1481
1482   opt_dkey = strchr(key, '.');
1483   if (!opt_dkey) {
1484     opt_dkey = NullStr;
1485     opt_keyd = key;
1486   } else {
1487     opt_keyd = strnsave(key, opt_dkey - key);
1488     opt_dkey++;
1489     if (*opt_dkey == '\0')      /* check for 'host.' */
1490       opt_dkey = NullStr;
1491   }
1492
1493   /*
1494    * Expand global options
1495    */
1496   fs_static.fs_glob = expand_selectors(g_opts);
1497
1498   /*
1499    * Expand local options
1500    */
1501   fs_static.fs_local = expand_selectors(opts);
1502
1503   /* break global options into fs_static fields */
1504   if ((ok = split_opts(fs_static.fs_glob, key))) {
1505     dlog("global split_opts ok");
1506     /*
1507      * evaluate local selectors
1508      */
1509     if ((ok = eval_selectors(fs_static.fs_local, key))) {
1510       dlog("local eval_selectors ok");
1511       /* if the local selectors matched, then do the local overrides */
1512       ok = split_opts(fs_static.fs_local, key);
1513       if (ok)
1514         dlog("local split_opts ok");
1515     }
1516   }
1517
1518   /*
1519    * Normalize remote host name.
1520    * 1.  Expand variables
1521    * 2.  Normalize relative to host tables
1522    * 3.  Strip local domains from the remote host
1523    *     name before using it in other expansions.
1524    *     This makes mount point names and other things
1525    *     much shorter, while allowing cross domain
1526    *     sharing of mount maps.
1527    */
1528   apply_opts(expand_opts, rhost_expansion, FALSE);
1529   if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
1530     host_normalize(&fs_static.opt_rhost);
1531
1532   /*
1533    * Macro expand the options.
1534    * Do this regardless of whether we are accepting
1535    * this mount - otherwise nasty things happen
1536    * with memory allocation.
1537    */
1538   apply_opts(expand_opts, expansions, FALSE);
1539
1540   /*
1541    * Strip trailing slashes from local pathname...
1542    */
1543   deslashify(fs_static.opt_fs);
1544
1545   /*
1546    * ok... copy the data back out.
1547    */
1548   *fo = fs_static;
1549
1550   /*
1551    * Clear defined options
1552    */
1553   if (opt_keyd != key && opt_keyd != nullstr)
1554     XFREE(opt_keyd);
1555   opt_keyd = nullstr;
1556   opt_dkey = NullStr;
1557   opt_key = opt_map = opt_path = nullstr;
1558
1559   return ok;
1560 }