* Copyright (c) 1989 Jan-Simon Pendry
* Copyright (c) 1989 Imperial College of Science, Technology & Medicine
* Copyright (c) 1989 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
* %sccs.include.redist.c%
* @(#)opts.c 1.3 (Berkeley) 6/26/91
* $Id: opts.c,v 5.2.2.3 1992/05/31 16:34:13 jsp Exp $
extern char *getenv
P((const char *));
* static copy of the options with
static struct am_opts fs_static
;
static char *opt_host
= hostname
;
static char *opt_hostd
= hostd
;
static char nullstr
[] = "";
static char *opt_key
= nullstr
;
static char *opt_map
= nullstr
;
static char *opt_path
= nullstr
;
* Length of longest option name
#define NLEN 16 /* conservative */
#define S(x) (x) , (sizeof(x)-1)
char *name
; /* Name of the option */
int nlen
; /* Length of option name */
char **optp
; /* Pointer to option value string */
char **sel_p
; /* Pointer to selector value string */
/* Options in something corresponding to frequency of use */
{ S("opts"), &fs_static
.opt_opts
, 0 },
{ S("host"), 0, &opt_host
},
{ S("hostd"), 0, &opt_hostd
},
{ S("type"), &fs_static
.opt_type
, 0 },
{ S("rhost"), &fs_static
.opt_rhost
, 0 },
{ S("rfs"), &fs_static
.opt_rfs
, 0 },
{ S("fs"), &fs_static
.opt_fs
, 0 },
{ S("key"), 0, &opt_key
},
{ S("map"), 0, &opt_map
},
{ S("sublink"), &fs_static
.opt_sublink
, 0 },
{ S("dev"), &fs_static
.opt_dev
, 0 },
{ S("pref"), &fs_static
.opt_pref
, 0 },
{ S("path"), 0, &opt_path
},
{ S("autodir"), 0, &auto_dir
},
{ S("delay"), &fs_static
.opt_delay
, 0 },
{ S("domain"), 0, &hostdomain
},
{ S("karch"), 0, &karch
},
{ S("cluster"), 0, &cluster
},
{ S("byte"), 0, &endian
},
{ S("remopts"), &fs_static
.opt_remopts
, 0 },
{ S("mount"), &fs_static
.opt_mount
, 0 },
{ S("unmount"), &fs_static
.opt_unmount
, 0 },
{ S("cache"), &fs_static
.opt_cache
, 0 },
{ S("user"), &fs_static
.opt_user
, 0 },
{ S("group"), &fs_static
.opt_group
, 0 },
{ S("var0"), &vars
[0], 0 },
{ S("var1"), &vars
[1], 0 },
{ S("var2"), &vars
[2], 0 },
{ S("var3"), &vars
[3], 0 },
{ S("var4"), &vars
[4], 0 },
{ S("var5"), &vars
[5], 0 },
{ S("var6"), &vars
[6], 0 },
{ S("var7"), &vars
[7], 0 },
typedef struct opt_apply opt_apply
;
* Specially expand the remote host name first
static opt_apply rhost_expansion
[] = {
{ &fs_static
.opt_rhost
, "${host}" },
* List of options which need to be expanded
* Note that this the order here _may_ be important.
static opt_apply expansions
[] = {
/* { &fs_static.opt_dir, 0 }, */
{ &fs_static
.opt_sublink
, 0 },
{ &fs_static
.opt_rfs
, "${path}" },
{ &fs_static
.opt_fs
, "${autodir}/${rhost}${rfs}" },
{ &fs_static
.opt_opts
, "rw" },
{ &fs_static
.opt_remopts
, "${opts}" },
{ &fs_static
.opt_mount
, 0 },
{ &fs_static
.opt_unmount
, 0 },
* List of options which need to be free'ed before re-use
static opt_apply to_free
[] = {
{ &fs_static
.fs_glob
, 0 },
{ &fs_static
.fs_local
, 0 },
{ &fs_static
.fs_mtab
, 0 },
/* { &fs_static.opt_dir, 0 }, */
{ &fs_static
.opt_sublink
, 0 },
{ &fs_static
.opt_rfs
, 0 },
{ &fs_static
.opt_fs
, 0 },
{ &fs_static
.opt_rhost
, 0 },
{ &fs_static
.opt_opts
, 0 },
{ &fs_static
.opt_remopts
, 0 },
{ &fs_static
.opt_mount
, 0 },
{ &fs_static
.opt_unmount
, 0 },
* Skip to next option in the string
static char *opt
P((char**));
while (*cp
&& *cp
!= ';') {
while (*cp
&& *cp
!= '\"')
* Skip past any remaining ';'s
* If we have a zero length string
* and there are more fields, then
* parse the next one. This allows
* sequences of empty fields.
static int eval_opts
P((char*, char*));
static int eval_opts(opts
, mapkey
)
* Fill in the global structure fs_static by
* cracking the string opts. opts may be
* For each user-specified option
enum vs_opt
{ OldSyn
, SelEQ
, SelNE
, VarAss
} vs_opt
;
char *eq
= strchr(f
, '=');
if (!eq
|| eq
[1] == '\0' || eq
== f
) {
* No value, just continue
plog(XLOG_USER
, "key %s: No value component in \"%s\"", mapkey
, f
);
* Check what type of operation is happening
* = is OldSyn (either SelEQ or VarAss)
if (eq
[-1] == '!') { /* != */
} else if (eq
[-1] == ':') { /* := */
} else if (eq
[1] == '=') { /* == */
} else if (eq
[1] == '!') { /* =! */
* For each recognised option
for (op
= opt_fields
; op
->name
; op
++) {
* Check whether they match
if (FSTREQ(op
->name
, f
)) {
#if AMD_COMPAT <= 5000108
plog(XLOG_WARNING
, "key %s: Old syntax selector found: %s=%s", mapkey
, f
, opt
);
if (op
->sel_p
&& (STREQ(*op
->sel_p
, opt
) == (vs_opt
== SelNE
))) {
plog(XLOG_MAP
, "key %s: map selector %s (=%s) did not %smatch %s",
vs_opt
== SelNE
? "not " : "",
plog(XLOG_USER
, "key %s: Can't assign to a selector (%s)", mapkey
, op
->name
);
plog(XLOG_USER
, "key %s: Unrecognised key/option \"%s\"", mapkey
, f
);
static void free_op
P((opt_apply
*, int));
static void free_op(p
, b
)
* Normalize slashes in the string.
void normalize_slash
P((char *p
));
char *f
= strchr(p
, '/');
if (f
== f0
&& f
[0] == '/' && f
[1] == '/') {
/* copy double slash iff first */
/* copy a single / across */
/* assert(f[-1] == '/'); */
/* keep copying up to next / */
while (*f
&& *f
!= '/') {
/* assert(*f == 0 || *f == '/'); */
*t
= 0; /* derived from fix by Steven Glassman */
* Macro-expand an option. Note that this does not
* handle recursive expansions. They will go badly wrong.
* If sel is true then old expand selectors, otherwise
* don't expand selectors.
static void expand_op
P((opt_apply
*, int));
static void expand_op(p
, sel_p
)
* The BUFSPACE macros checks that there is enough space
* left in the expansion buffer. If there isn't then we
* give up completely. This is done to avoid crashing the
* automounter itself (which would be a bad thing to do).
#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
static char expand_error
[] = "No space to expand \"%s\"";
char expbuf
[MAXPATHLEN
+1];
while (dp
= strchr(cp
, '$')) {
plog(XLOG_ERROR
, expand_error
, *p
->opt
);
plog(XLOG_ERROR
, expand_error
, *p
->opt
);
enum { E_All
, E_Dir
, E_File
, E_Domain
, E_Host
} todo
;
char *br_p
= strchr(cp
, '}');
plog(XLOG_USER
, "No closing '}' in \"%s\"", *p
->opt
);
* Figure out which part of the variable to grab.
* Just take the last component
} else if (br_p
[-1] == '/') {
* Take all but the last component
} else if (br_p
[-1] == '.') {
* Truncate if too long. Since it won't
* match anyway it doesn't matter that
* Put the string into another buffer so
* Search the option array
for (op
= opt_fields
; op
->name
; op
++) {
if (len
== op
->nlen
&& STREQ(op
->name
, nbuf
)) {
* the correct value field.
if (!(!op
->sel_p
== !sel_p
)) {
* Copy the string across unexpanded
sprintf(xbuf
, "${%s%s%s}",
todo
== E_Domain
? "." : "",
todo
== E_Host
? "." : "");
* Make sure expansion doesn't
* ${/var} means take just the last part
* ${var/} means take all but the last part
* ${.var} means take all but first part
* ${var.} means take just the first part
* ${var} means take the whole lot
vptr
= strrchr(val
, '/');
vptr
= strrchr(val
, '/');
/*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/
if (BUFSPACE(ep
, vlen
)) {
plog(XLOG_ERROR
, expand_error
, *p
->opt
);
* Done with this variable
* Check that the search was succesful
* If it wasn't then scan the
* environment for that name
* and use any value found
char *env
= getenv(nbuf
);
if (BUFSPACE(ep
, vlen
)) {
plog(XLOG_ERROR
, expand_error
, *p
->opt
);
plog(XLOG_DEBUG
, "Environment gave \"%s\" -> \"%s\"", nbuf
, env
);
plog(XLOG_USER
, "Unknown sequence \"${%s}\"", nbuf
);
plog(XLOG_USER
, "Unknown $ sequence in \"%s\"", *p
->opt
);
* Handle common case - no expansion
* Finish off the expansion
if (BUFSPACE(ep
, strlen(cp
))) {
plog(XLOG_ERROR
, expand_error
, *p
->opt
);
*p
->opt
= strdup(expbuf
);
normalize_slash(*p
->opt
);
plog(XLOG_DEBUG
, "Expansion of \"%s\"...", cp_orig
);
plog(XLOG_DEBUG
, "... is \"%s\"", *p
->opt
);
static void expand_opts
P((opt_apply
*, int));
static void expand_opts(p
, sel_p
)
* Do double expansion, remembering
* to free the string from the first
char *s
= *p
->opt
= expand_key(p
->val
);
* Apply a function to a list of options
static void apply_opts(op
, ppp
, b
)
for (pp
= ppp
; pp
->opt
; pp
++)
* Copy in the structure we are playing with
* Free previously allocated memory
apply_opts(free_op
, to_free
, FALSE
);
oa
.opt
= &key
; oa
.val
= 0;
* Remove trailing /'s from a string
* unless the string is a single / (Steven Glassman)
void deslashify
P((char *s
));
char *sl
= s
+ strlen(s
);
while (*--sl
== '/' && sl
> s
)
int eval_fs_opts(fo
, opts
, g_opts
, path
, key
, map
)
char *opts
, *g_opts
, *path
, *key
, *map
;
* Clear out the option table
bzero((voidp
) &fs_static
, sizeof(fs_static
));
bzero((voidp
) vars
, sizeof(vars
));
bzero((voidp
) fo
, sizeof(*fo
));
* Set key, map & path before expansion
fs_static
.fs_glob
= expand_key(g_opts
);
fs_static
.fs_local
= expand_key(opts
);
* Expand default (global) options
if (!eval_opts(fs_static
.fs_glob
, key
))
if (ok
&& !eval_opts(fs_static
.fs_local
, key
))
* Normalise remote host name.
* 2. Normalize relative to host tables
* 3. Strip local domains from the remote host
* name before using it in other expansions.
* This makes mount point names and other things
* much shorter, while allowing cross domain
apply_opts(expand_opts
, rhost_expansion
, FALSE
);
if (ok
&& fs_static
.opt_rhost
&& *fs_static
.opt_rhost
)
host_normalize(&fs_static
.opt_rhost
);
* Macro expand the options.
* Do this regardless of whether we are accepting
* this mount - otherwise nasty things happen
* with memory allocation.
apply_opts(expand_opts
, expansions
, FALSE
);
* Strip trailing slashes from local pathname...
deslashify(fs_static
.opt_fs
);
* ok... copy the data back out.
opt_key
= opt_map
= opt_path
= nullstr
;