/* * These routines add a new style of selector; function-style boolean * operators. To add new ones, just define functions as in true, false, * exists (below) and add them to the functable, above. * * Usage example: Some people have X11R5 local, some go to a server. I do * this: * * * exists(/usr/pkg/${key});type:=link;fs:=/usr/pkg/${key} || \ * -type:=nfs;rfs=/usr/pkg/${key} \ * rhost:=server1 \ * rhost:=server2 * * -Rens Troost <*****@*****.**> */ static IntFuncPtr functable_lookup(char *key) { struct functable *fp; for (fp = functable; fp->name; fp++) if (FSTREQ(fp->name, key)) return (fp->func); return (IntFuncPtr) NULL; }
/* * Fill in the global structure fs_static by * cracking the string opts. opts may be * scribbled on at will. Does NOT evaluate options. * Returns 0 on error, 1 if no syntax errors were discovered. */ static int split_opts(char *opts, char *mapkey) { char *o = opts; char *f; /* * For each user-specified option */ for (f = opt(&o); *f; f = opt(&o)) { struct opt *op; char *eq = strchr(f, '='); char *opt = NULL; if (!eq) continue; if (eq[-1] == '!' || eq[1] == '=' || eq[1] == '!') { /* != or == or =! */ continue; /* we don't care about selectors */ } if (eq[-1] == ':') { /* := */ eq[-1] = '\0'; } else { /* old style assignment */ eq[0] = '\0'; } opt = eq + 1; /* * For each recognized option */ for (op = opt_fields; op->name; op++) { /* * Check whether they match */ if (FSTREQ(op->name, f)) { if (op->sel_p) { plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", mapkey, op->name); return 0; } *op->optp = opt; /* actual assignment into fs_static */ break; /* break out of for loop */ } /* end of "if (FSTREQ(op->name, f))" statement */ } /* end of "for (op = opt_fields..." statement */ if (!op->name) plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); } return 1; }
/* * Process global section of configuration file options. * Return 0 upon success, 1 otherwise. */ static int process_global_option(const char *key, const char *val) { struct _func_map *gfp; /* ensure that val is valid */ if (!val || val[0] == '\0') return 1; /* * search for global function. */ for (gfp = glob_functable; gfp->name; gfp++) if (FSTREQ(gfp->name, key)) return (gfp->func)(val); fprintf(stderr, "conf: unknown global key: \"%s\"\n", key); return 1; /* failed to match any command */ }
static void mapc_repl_kv(mnt_map *m, char *key, char *val) { kv *k; /* * Compute the hash table offset */ k = m->kvhash[kvhash_of(key)]; /* * Scan the linked list for the key */ while (k && !FSTREQ(k->key, key)) k = k->next; if (k) { XFREE(k->val); k->val = val; } else { mapc_add_kv(m, key, val); } }
/**************************************************************************** *** FUNCTIONS *** ****************************************************************************/ static am_node * amfs_lookup_node(am_node *mp, char *fname, int *error_return) { am_node *new_mp; int error = 0; /* Error so far */ int in_progress = 0; /* # of (un)mount in progress */ mntfs *mf; char *expanded_fname = NULL; dlog("in amfs_lookup_node"); /* * If the server is shutting down * then don't return information * about the mount point. */ if (amd_state == Finishing) { if (mp->am_al == NULL || mp->am_al->al_mnt == NULL || mp->am_al->al_mnt->mf_fsflags & FS_DIRECT) { dlog("%s mount ignored - going down", fname); } else { dlog("%s/%s mount ignored - going down", mp->am_path, fname); } ereturn(ENOENT); } /* * Handle special case of "." and ".." */ if (fname[0] == '.') { if (fname[1] == '\0') return mp; /* "." is the current node */ if (fname[1] == '.' && fname[2] == '\0') { if (mp->am_parent) { dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); return mp->am_parent; /* ".." is the parent node */ } ereturn(ESTALE); } } /* * Check for valid key name. * If it is invalid then pretend it doesn't exist. */ if (!valid_key(fname)) { plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); ereturn(ENOENT); } /* * Expand key name. * expanded_fname is now a private copy. */ expanded_fname = expand_selectors(fname); /* * Search children of this node */ for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) { if (FSTREQ(new_mp->am_name, expanded_fname)) { if (new_mp->am_error) { error = new_mp->am_error; continue; } /* * If the error code is undefined then it must be * in progress. */ mf = new_mp->am_al->al_mnt; if (mf->mf_error < 0) goto in_progrss; /* * If there was a previous error with this node * then return that error code. */ if (mf->mf_flags & MFF_ERROR) { error = mf->mf_error; continue; } if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { in_progrss: /* * If the fs is not mounted or it is unmounting then there * is a background (un)mount in progress. In this case * we just drop the RPC request (return nil) and * wait for a retry, by which time the (un)mount may * have completed. */ dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x", expanded_fname, mf->mf_mount, (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags); in_progress++; if (mf->mf_flags & MFF_UNMOUNTING) { dlog("will remount later"); new_mp->am_flags |= AMF_REMOUNT; } continue; } /* * Otherwise we have a hit: return the current mount point. */ dlog("matched %s in %s", expanded_fname, new_mp->am_path); XFREE(expanded_fname); return new_mp; } } if (in_progress) { dlog("Waiting while %d mount(s) in progress", in_progress); XFREE(expanded_fname); ereturn(-1); } /* * If an error occurred then return it. */ if (error) { dlog("Returning error: %s", strerror(error)); XFREE(expanded_fname); ereturn(error); } /* * If the server is going down then just return, * don't try to mount any more file systems */ if ((int) amd_state >= (int) Finishing) { dlog("not found - server going down anyway"); ereturn(ENOENT); } /* * Allocate a new map */ new_mp = get_ap_child(mp, expanded_fname); XFREE(expanded_fname); if (new_mp == NULL) ereturn(ENOSPC); *error_return = -1; return new_mp; }
/* * Just evaluate selectors, which were split by split_opts. * Returns 0 on error or no match, 1 if matched. */ static int eval_selectors(char *opts, char *mapkey) { char *o, *old_o; char *f; int ret = 0; o = old_o = strdup(opts); /* * For each user-specified option */ for (f = opt(&o); *f; f = opt(&o)) { struct opt *op; enum vs_opt vs_opt; char *eq = strchr(f, '='); char *fx; IntFuncPtr func; char *opt = NULL; char *arg; if (!eq) { /* * No value, is it a function call? */ arg = strchr(f, '('); if (!arg || arg[1] == '\0' || arg == f) { /* * No, just continue */ plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f); continue; } /* null-terminate the argument */ *arg++ = '\0'; fx = strchr(arg, ')'); if (!arg || fx == arg) { plog(XLOG_USER, "key %s: Malformed function in \"%s\"", mapkey, f); continue; } *fx = '\0'; if (f[0] == '!') { vs_opt = SelNE; f++; } else { vs_opt = SelEQ; } /* * look up f in functable and pass it arg. * func must return 0 on failure, and 1 on success. */ if ((func = functable_lookup(f))) { int funok; /* this allocates memory, don't forget to free */ arg = expand_options(arg); funok = func(arg); XFREE(arg); if (vs_opt == SelNE) funok = !funok; if (!funok) goto out; continue; } else { plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f); goto out; } } else { if (eq[1] == '\0' || eq == f) { /* misformed selector */ plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f); continue; } } /* * Check what type of operation is happening * !=, =! is SelNE * == is SelEQ * =, := is VarAss */ if (eq[-1] == '!') { /* != */ vs_opt = SelNE; eq[-1] = '\0'; opt = eq + 1; } else if (eq[-1] == ':') { /* := */ continue; } else if (eq[1] == '=') { /* == */ vs_opt = SelEQ; eq[0] = '\0'; opt = eq + 2; } else if (eq[1] == '!') { /* =! */ vs_opt = SelNE; eq[0] = '\0'; opt = eq + 2; } else { /* old style assignment */ continue; } /* * For each recognized option */ for (op = opt_fields; op->name; op++) { /* * Check whether they match */ if (FSTREQ(op->name, f)) { opt = expand_options(opt); if (op->sel_p != NULL) { int selok; if (op->case_insensitive) { selok = STRCEQ(*op->sel_p, opt); } else { selok = STREQ(*op->sel_p, opt); } if (vs_opt == SelNE) selok = !selok; if (!selok) { plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", mapkey, op->name, *op->sel_p, vs_opt == SelNE ? "mis" : "", opt); XFREE(opt); goto out; } XFREE(opt); } /* check if to apply a function */ if (op->fxn_p) { int funok; funok = op->fxn_p(opt); if (vs_opt == SelNE) funok = !funok; if (!funok) { plog(XLOG_MAP, "key %s: map function %s did not %smatch %s", mapkey, op->name, vs_opt == SelNE ? "mis" : "", opt); XFREE(opt); goto out; } XFREE(opt); } break; /* break out of for loop */ } } if (!op->name) plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); } /* all is ok */ ret = 1; out: free(old_o); return ret; }
/* * Search the map for the key. * Put a safe copy in *pval or return * an error code */ int mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse) { int error = 0; kv *k = 0; /* * Firewall */ if (!m) { plog(XLOG_ERROR, "Null map request for %s", key); return ENOENT; } if (m->flags & MAPC_SYNC) { /* * Get modify time... */ time_t t; error = (*m->mtime)(m->map_name, &t); if (error || t > m->modify) { m->modify = t; plog(XLOG_INFO, "Map %s is out of date", m->map_name); mapc_sync(m); } } if (!MAPC_ISRE(m)) { /* * Compute the hash table offset */ k = m->kvhash[kvhash_of(key)]; /* * Scan the linked list for the key */ while (k && !FSTREQ(k->key, key)) k = k->next; } #ifdef HAS_REGEXP else if (recurse == MREC_FULL) { /* * Try for an RE match against the entire map. * Note that this will be done in a "random" * order. */ int i; for (i = 0; i < NKVHASH; i++) { k = m->kvhash[i]; while (k) { if (regexec((regex_t *)k->key, key, 0, NULL, 0) == 0) break; k = k->next; } if (k) break; } } #endif /* * If found then take a copy */ if (k) { if (k->val) *pval = strdup(k->val); else error = ENOENT; } else if (m->alloc >= MAPC_ALL) { /* * If the entire map is cached then this * key does not exist. */ error = ENOENT; } else { /* * Otherwise search the map. If we are * in incremental mode then add the key * to the cache. */ error = search_map(m, key, pval); if (!error && m->alloc == MAPC_INC) mapc_add_kv(m, strdup(key), strdup(*pval)); } /* * If an error, and a wildcard exists, * and the key is not internal then * return a copy of the wildcard. */ if (error > 0) { if (recurse == MREC_FULL && !MAPC_ISRE(m)) { char wildname[MAXPATHLEN]; char *subp; if (*key == '/') return error; /* * Keep chopping sub-directories from the RHS * and replacing with "/ *" and repeat the lookup. * For example: * "src/gnu/gcc" -> "src / gnu / *" -> "src / *" */ strlcpy(wildname, key, sizeof wildname); while (error && (subp = strrchr(wildname, '/'))) { strlcpy(subp, "/*", 3); #ifdef DEBUG dlog("mapc recurses on %s", wildname); #endif error = mapc_meta_search(m, wildname, pval, MREC_PART); if (error) *subp = 0; } if (error > 0 && m->wildcard) { *pval = strdup(m->wildcard); error = 0; } } } return error; }